Пример #1
0
class Node(object):
    def __init__(self, host, port):
        self.socket = host + ':' + str(port)
        self.peers = set()
        self.users = dict()
        self.transaction_pool = dict()
        self.user_balence_pool = dict()
        self.blockchain = Blockchain(self)
        # Raft
        self.raft = None
        self.uncommitted_user = None
        self.uncommitted_transaction = None
        # broadcast_ methods: for broadcast user requests by sending message to raft module
        # add_* methods: for verifying changes validity and storing changes
        # commit_* methods: for committing all changes and updating all relating status

    ###############
    # Node setting
    ###############

    def set_raft(self, raft):
        self.raft = raft

    def get_socket(self):
        return self.socket

    # Initialization node by replicating from ann existing peer node
    def clone_from_peer(self, peer):
        try:
            response = requests.get(
                url=f'http://{peer}/node/clone',
                timeout=config.CONNECTION_TIMEOUT_IN_SECONDS)
            if response.status_code == 200:
                replica = response.json()
                for p in replica.get('peers'):
                    self.add_peer(p)
                self.users = replica.get('users')
                self.transaction_pool = replica.get('transaction_pool')
                self.user_balence_pool = replica.get('user_balence_pool')
                self.blockchain.set_chain(replica.get('blockchain'))
                return True
        except Exception as e:
            print(e)
        return False

    ####################
    # P2P Network Peers
    ####################

    def get_peers(self):
        return list(self.peers)

    def register_peer(self, peer):
        if peer != self.socket:
            try:
                response = requests.get(
                    url=f'http://{peer}/',
                    timeout=config.CONNECTION_TIMEOUT_IN_SECONDS)
                if response.status_code == 200:
                    self.broadcast_peer(peer)
                    self.peers.add(peer)
                    return True
            except Exception as e:
                print(e)
        return False

    def add_peer(self, peer):
        if peer != self.socket:
            self.peers.add(peer)
            return True
        return False

    def broadcast_peer(self, peer):
        json = {'peer': peer}
        for node in self.peers:
            try:
                response = requests.post(
                    url=f'http://{node}/peer/add',
                    json=json,
                    timeout=config.CONNECTION_TIMEOUT_IN_SECONDS)
            except Exception as e:
                print(e)

    ###############
    # Users
    ###############

    def get_users(self):
        return self.users

    def register_user(self, user, password):
        if user in self.users:
            return False
        self.broadcast_user(user, password)
        return True

    def add_user(self, user, password):
        if user not in self.users:
            self.uncommitted_user = (user, password)
            return True
        return False

    def commit_user(self):
        if self.uncommitted_user is not None:
            user = self.uncommitted_user[0]
            password = self.uncommitted_user[1]
            self.users[user] = password
            self.user_balence_pool[user] = config.NEW_USER_REWARD
            self.uncommitted_user = None
            return True
        return False

    def broadcast_user(self, user, password):
        json = {
            'username': user,
            'password': password,
            'type': 'broadcast_user'
        }
        self.raft.handle_message(json)

    def authenticate_user(self, user, password):
        return self.users.get(user) == password

    #############################
    # Transactions and Balences
    ##############################

    def get_transaction_pool(self):
        return self.transaction_pool

    def get_transaction_pool_as_list(self):
        return list(self.transaction_pool.values())

    def reset_transaction_pool(self):
        self.transaction_pool = dict()

    def start_transaction(self, sender, recipient, amount):
        if self.verify_transaction(sender, recipient, amount):
            transaction = self.create_transaction(sender, amount, recipient)
            self.broadcast_transaction(transaction)
            return transaction
        else:
            return None

    def add_transaction(self, transaction):
        transaction_id = transaction.get('transaction_id')
        sender = transaction.get('sender')
        recipient = transaction.get('recipient')
        amount = transaction.get('amount')
        if self.verify_transaction(sender, recipient, amount):
            self.uncommitted_transaction = transaction
            return True
        return False

    def commit_transaction(self):
        if self.uncommitted_transaction is not None:
            transaction_id = self.uncommitted_transaction.get('transaction_id')
            sender = self.uncommitted_transaction.get('sender')
            recipient = self.uncommitted_transaction.get('recipient')
            amount = self.uncommitted_transaction.get('amount')
            self.transaction_pool[
                transaction_id] = self.uncommitted_transaction
            self.update_user_balence_pool(sender, recipient, amount)
            self.uncommitted_transaction = None
            return True
        return False

    def broadcast_transaction(self, transaction):
        json = {'transaction': transaction, 'type': 'broadcast_transaction'}
        self.raft.handle_message(json)

    def verify_transaction(self, sender, recipient, amount):
        if self.user_balence_pool.get(
                sender) is None or self.user_balence_pool.get(
                    recipient) is None:
            return False
        return self.user_balence_pool.get(sender) >= amount

    @staticmethod
    def create_transaction(sender, amount, recipient):
        return {
            'transaction_id': str(uuid4()),
            'sender': sender,
            'recipient': recipient,
            'amount': amount,
            'timestamp': int(time())
        }

    def get_user_balence_pool(self):
        return self.user_balence_pool

    def update_user_balence_pool(self, sender, recipient, amount):
        self.user_balence_pool[sender] = self.user_balence_pool.get(
            sender) - amount
        self.user_balence_pool[recipient] = self.user_balence_pool.get(
            recipient) + amount

    ###############
    # Blockchain
    ###############

    def get_full_chain(self):
        return self.blockchain.get_chain()

    def get_last_block(self):
        return self.blockchain.get_last_block()

    def get_committed_user_balences(self):
        return self.blockchain.get_committed_user_balences()

    def mine(self, miner):
        new_block = self.blockchain.mine(miner)
        if new_block is not None:
            new_blockchain = self.blockchain.get_chain().copy()
            new_blockchain.append(new_block)
            self.broadcast_chain(new_blockchain)
        return new_block

    def add_chain(self, chain):
        return self.blockchain.add_cahin(chain)

    def commit_chain(self):
        return self.blockchain.commit_chain()

    def broadcast_chain(self, chain):
        json = {'blockchain': chain, 'type': 'broadcast_chain'}
        self.raft.handle_message(json)

    '''