def register_node_to_ring(self, wallet_dict: dict):
        '''Handle request to enter the network. The bootstrap node
        should register the node in `ring` and respond with index and
        current blockchain.

        Arguments:

        * `wallet_dict`: `dict` directly from `.to_dict()`.

        Returns:

        * `dict` with ['id', 'blockchain'].'''

        node_wallet = Wallet.from_dict(wallet_dict)
        # if node has already contacted before to register
        # do not produce new index, ...
        index = self.pubk2ind.get(pubk_to_key(node_wallet.public_key),
                                  len(self.pubk2ind))
        self.pubk2ind[pubk_to_key(node_wallet.public_key)] = index
        self.ring[index] = node_wallet
        self.ring_bak[index] = node_wallet.deepcopy()

        info = dict(blockchain=self.blockchain.to_dict(), id=index)

        return info
def view_blockchain():
    '''Return human readable blockchain'''

    blockchain_list = []
    for block in NODE.blockchain.chain:
        human_readable = dict()
        for transaction in block.list_of_transactions:
            if isinstance(transaction.sender_pubk, int):
                # genesis block
                human_readable.setdefault('transactions', []).append(
                    dict(sender='Genesis',
                         receiver='0',
                         amount=sum([
                             to.amount
                             for to in transaction.transaction_outputs
                         ])))
            else:
                human_readable.setdefault('transactions', []).append(
                    dict(transaction_id=transaction.transaction_id,
                         transaction_inputs=transaction.transaction_inputs,
                         sender=NODE.pubk2ind[pubk_to_key(transaction.sender_pubk)],
                         receiver=NODE.pubk2ind[pubk_to_key(transaction.receiver_pubk)],
                         amounts=[transaction_output.amount \
                             for transaction_output in transaction.transaction_outputs])
                )
        blockchain_list.append(human_readable)
    return jsonify(blockchain_list), 200
    def receive_wallets(self, wallet_dict: dict):
        '''Receive all wallets from the bootstrap and
        copy to ring and backup. Also, process all transactions
        received before receiving the wallets.

        Arguments:

        * `wallet_dict`: `dict` with indices as key values
        and `Wallet` `dicts` as values.'''

        self.ring = {
            int(idx): Wallet.from_dict(wallet_dict[idx]) \
                if int(idx) != self.my_id else self.my_wallet() \
                for idx in wallet_dict
        }
        self.pubk2ind = {
            pubk_to_key(self.ring[idx].public_key): idx
            for idx in self.ring
        }
        self.ring_bak = object_dict_deepcopy(self.ring)

        while True:
            new_ring = self.valid_chain(self.blockchain)
            if new_ring is not None:
                self.ring = object_dict_deepcopy(new_ring)
                self.ring_bak = object_dict_deepcopy(new_ring)
                break
            else:
                self.my_id, self.blockchain = first_contact_data(
                    self.bootstrap_address, self.my_wallet())

        # process transactions received before wallets
        self.process_transactions()
    def add_utxos(self, transaction_outputs: list, ring: dict):
        '''Add unspent transactions to respective wallets.

        Arguments:

        * `transaction_outputs`: iterable of `TransactionOutput` objects.

        * `ring`: `Wallet` of nodes.'''

        for tro in transaction_outputs:
            ring[self.pubk2ind[pubk_to_key(
                tro.receiver_public_key)]].add_utxo(tro)
def view_transactions():
    '''Return human readable format (`sender`, `receiver`, `amount`)
    of every transacion in the last block of the blockchain.'''

    last_block = NODE.blockchain.chain[-1]
    human_readable = dict()
    for transaction in last_block.list_of_transactions:
        if isinstance(transaction.sender_pubk, int):
            # genesis block
            human_readable.setdefault('transactions', []).append(
                dict(sender='Genesis',
                     receiver='0',
                     amount=sum([
                         to.amount for to in transaction.transaction_outputs
                     ])))
        else:
            human_readable.setdefault('transactions', []).append(
                dict(sender=NODE.pubk2ind[pubk_to_key(
                    transaction.sender_pubk)],
                     receiver=NODE.pubk2ind[pubk_to_key(
                         transaction.receiver_pubk)],
                     amount=transaction.transaction_outputs[0].amount))
    return jsonify(human_readable), 200
    def check_my_mined_block(self, block_dict: dict):
        '''Check block returned from miner and its coherence
        with the current blockchain. Append if everything
        is proper. Renew miner. NOTE: block is not broadcasted.

        Arguments:

        * `block_dict`: `dict` directly from `to_dict()`.

        Returns:

        * The mined block or `None` if not appended.'''

        block = Block.from_dict(block_dict)

        if block.previous_hash == self.blockchain.get_block_hash(-1):

            block_transactions, _ = self.transaction_queue.split(self.capacity,
                                                                 assign=1)
            # allow miner to be recalled now that the transaction queue is up-to-date
            self.miner_pid = None

            for tra in block_transactions:
                self.add_utxos(tra.transaction_outputs, ring=self.ring_bak)
                self.ring_bak[self.pubk2ind[pubk_to_key(tra.sender_pubk)]]\
                    .remove_utxos(tra.transaction_inputs)

            # NOTE: broadcast block from API so not inside lock
            # self.broadcast_block(block)

            self.blockchain.append_block(block)

        else:
            # new blockchain/block received and miner was not killed in time
            # enable to recall, but transaction queue is new so dont meddle
            self.miner_pid = None
            block = None

        if len(self.transaction_queue) >= self.capacity:
            self.mine_block()

        return block
    def validate_transaction(self, transaction: Transaction, ring: dict):
        '''Validate received transaction.

        Arguments:

        * `transaction`: [Reconstructed from `dict`] `Transaction`.

        * `ring`: ring of `Wallet`s the validation is based upon.

        Returns:

        * `True` if valid.'''

        # signature
        if not PKCS1_v1_5.new(transaction.sender_pubk).\
            verify(transaction.make_hash(as_str=False), transaction.signature):
            return False

        # double spending
        if len(set(transaction.transaction_inputs)) < len(
                transaction.transaction_inputs):
            return False

        # check ids are the same, note that utxos can be 1 or 2
        if not all([utxo.transaction_id == transaction.transaction_id \
            for utxo in transaction.transaction_outputs]):
            return False

        # check if transaction inputs exist
        amount = 0
        for utxo in transaction.transaction_outputs:
            if utxo.amount < 0:
                return False
            amount += utxo.amount

        return ring[self.pubk2ind[pubk_to_key(transaction.sender_pubk)]]\
            .check_and_remove_utxos(transaction.transaction_inputs, amount)
    def __init__(self,
                 bootstrap_address: str,
                 capacity: int,
                 difficulty: int,
                 port: int,
                 nodes=0,
                 is_bootstrap=False):
        '''Initialize `Node` object.

        Arguments:

        * `bootstrap_address`: ip+port of bootstrap (considered known a priori).

        * `capacity`: capacity of a block in blockchain.

        * `difficulty`: difficulty of mining a block.

        * `port`: port listening to.

        * `nodes`: number of nodes in the network (considered known a priori).

        * `is_bootstrap`: if this node is the bootstrap.'''

        wallet = generate_wallet(port)

        # validated transactions
        self.transaction_queue = TransactionQueue()
        # transactions not reflected in wallets of ring
        self.unprocessed_transaction_queue = TransactionQueue()

        self.miner_pid = None

        self.capacity = capacity

        self.difficulty = difficulty

        self.nodes = nodes

        if is_bootstrap:
            self.my_id = 0
            # information for every node (its address (ip:port),
            # its public key, its balance, its utxos)
            # should be modified with TRANSACTION_LOCK
            self.ring = {self.my_id: wallet}
            # backup containing info as it appears in the blockchain
            # Should be modified with BLOCK_LOCK
            self.ring_bak = object_dict_deepcopy(self.ring)
            # public key to index correspondence
            self.pubk2ind = {
                pubk_to_key(self.my_wallet().public_key): self.my_id
            }
            self.blockchain = self.init_bootstrap_blockchain()
        else:
            self.bootstrap_address = bootstrap_address
            self.my_id, self.blockchain = first_contact_data(
                self.bootstrap_address, wallet)
            # information for every node (its address (ip:port),
            # its public key, its balance, its utxos)
            self.ring = {self.my_id: wallet}
            # public key to index correspondence
            self.pubk2ind = {
                pubk_to_key(self.my_wallet().public_key): self.my_id
            }