Jovian
⭐️
Sign In

Socket Programming-Threads

In [1]:
import threading
import socket
import pickle
import hashlib
# from ecdsa import SigningKey
In [2]:
import time
import hashlib
import json
import uuid

NANOSECONDS = 1
MICROSECONDS = 1000 * NANOSECONDS
MILLISECONDS = 1000 * MICROSECONDS
SECONDS = 1000 * MILLISECONDS

MINE_RATE = 4 * SECONDS

STARTING_BALANCE = 1000

MINING_REWARD = 50
MINING_REWARD_INPUT = { 'address': '*mining-reward*' }
In [3]:
'''CRYPTO'''

def crypto_hash(*args):
    """
    Return a sha-256 hash of the given arguments.
    """
    stringified_args = sorted(map(lambda data: json.dumps(data), args))
    joined_data = ''.join(stringified_args)

    return hashlib.sha256(joined_data.encode('utf-8')).hexdigest()

def main():
    print(f"crypto_hash('one', 2, [3]): {crypto_hash('one', 2, [3])}")
    print(f"crypto_hash(2, 'one', [3]): {crypto_hash(2, 'one', [3])}")

if __name__ == '__main__':
    main()

crypto_hash('one', 2, [3]): 49d135ee795768472bd5f9e5d11c3982e6bdeae55bc86a0f4351654e3d4e8b2a crypto_hash(2, 'one', [3]): 49d135ee795768472bd5f9e5d11c3982e6bdeae55bc86a0f4351654e3d4e8b2a
In [4]:
HEX_TO_BINARY_CONVERSION_TABLE = {
    '0': '0000',
    '1': '0001',
    '2': '0010',
    '3': '0011',
    '4': '0100',
    '5': '0101',
    '6': '0110',
    '7': '0111',
    '8': '1000',
    '9': '1001',
    'a': '1010',
    'b': '1011',
    'c': '1100',
    'd': '1101',
    'e': '1110',
    'f': '1111'
}

def hex_to_binary(hex_string):
    binary_string = ''

    for character in hex_string:
        binary_string += HEX_TO_BINARY_CONVERSION_TABLE[character]

    return binary_string

def main():
    number = 451
    hex_number = hex(number)[2:]
    print(f'hex_number: {hex_number}')

    binary_number = hex_to_binary(hex_number)
    print(f'binary_number: {binary_number}')

    original_number = int(binary_number, 2)
    print(f'original_number: {original_number}')

    hex_to_binary_crypto_hash = hex_to_binary(crypto_hash('test-data'))
    print(f'hex_to_binary_crypto_hash: {hex_to_binary_crypto_hash}')

if __name__ == '__main__':
    main()
hex_number: 1c3 binary_number: 000111000011 original_number: 451 hex_to_binary_crypto_hash: 1000011110000111100110000100010011000000001010110100001011001011111110101111110000101001100110010001101001110111011001010110010111111001101101101011011100001001101101100101101011110011111100101001000111011011000010101001000000000000010011001100111110101011
In [5]:
'''BLOCK.PY'''

GENESIS_DATA = {
    'timestamp': 1,
    'last_hash': 'genesis_last_hash',
    'hash': 'genesis_hash',
    'data': [],
    'difficulty': 3,
    'nonce': 'genesis_nonce'
}

class Block:
    """
    Block: a unit of storage.
    Store transactions in a blockchain that supports a cryptocurrency.
    """
    def __init__(self, timestamp, last_hash, hash, data, difficulty, nonce):
        self.timestamp = timestamp
        self.last_hash = last_hash
        self.hash = hash
        self.data = data
        self.difficulty = difficulty
        self.nonce = nonce

    def __repr__(self):
        return (
            'Block('
            f'timestamp: {self.timestamp}, '
            f'last_hash: {self.last_hash}, '
            f'hash: {self.hash}, '
            f'data: {self.data}, '
            f'difficulty: {self.difficulty}, '
            f'nonce: {self.nonce})'
        )

    def __eq__(self, other):
        return self.__dict__ == other.__dict__

    def to_json(self):
        """
        Serialize the block into a dictionary of its attributes
        """
        return self.__dict__

    @staticmethod
    def mine_block(last_block, data):
        """
        Mine a block based on the given last_block and data, until a block hash
        is found that meets the leading 0's proof of work requirement.
        """
        timestamp = time.time()
        last_hash = last_block.hash
        difficulty = Block.adjust_difficulty(last_block, timestamp)
        nonce = 0
        hash = crypto_hash(timestamp, last_hash, data, difficulty, nonce)

        while hex_to_binary(hash)[0:difficulty] != '0' * difficulty:
            nonce += 1
            timestamp = time.time()
            difficulty = Block.adjust_difficulty(last_block, timestamp)
            hash = crypto_hash(timestamp, last_hash, data, difficulty, nonce)

        return Block(timestamp, last_hash, hash, data, difficulty, nonce)

    @staticmethod
    def genesis():
        """
        Generate the genesis block.
        """
        return Block(**GENESIS_DATA)

    @staticmethod
    def from_json(block_json):
        """
        Deserialize a block's json representation back into a block instance.
        """
        return Block(**block_json)

    @staticmethod
    def adjust_difficulty(last_block, new_timestamp):
        """
        Calculate the adjusted difficulty according to the MINE_RATE.
        Increase the difficulty for quickly mined blocks.
        Decrease the difficulty for slowly mined blocks.
        """
        if (new_timestamp - last_block.timestamp) < MINE_RATE:
            return last_block.difficulty + 1

        if (last_block.difficulty - 1) > 0:
            return last_block.difficulty - 1

        return 1

    @staticmethod
    def is_valid_block(last_block, block):
        """
        Validate block by enforcing the following rules:
          - the block must have the proper last_hash reference
          - the block must meet the proof of work requirement
          - the difficulty must only adjust by 1
          - the block hash must be a valid combination of the block fields
        """
        if block.last_hash != last_block.hash:
            raise Exception('The block last_hash must be correct')

        if hex_to_binary(block.hash)[0:block.difficulty] != '0' * block.difficulty:
            raise Exception('The proof of work requirement was not met')

        if abs(last_block.difficulty - block.difficulty) > 1:
            raise Exception('The block difficulty must only adjust by 1')

        reconstructed_hash = crypto_hash(
            block.timestamp,
            block.last_hash,
            block.data,
            block.nonce,
            block.difficulty
        )

        if block.hash != reconstructed_hash:
            raise Exception('The block hash must be correct')

def main():
    genesis_block = Block.genesis()
    bad_block = Block.mine_block(genesis_block, 'foo')
    bad_block.last_hash = 'evil_data'

    try:
        Block.is_valid_block(genesis_block, bad_block)
    except Exception as e:
        print(f'is_valid_block: {e}')

if __name__ == '__main__':
    main()

is_valid_block: The block last_hash must be correct
In [6]:
'''BLOCKCHAIN.PY'''

class Blockchain:
    """
    Blockchain: a public ledger of transactions.
    Implemented as a list of blocks - data sets of transactions
    """
    def __init__(self):
        self.chain = [Block.genesis()]

    def add_block(self, data):
        self.chain.append(Block.mine_block(self.chain[-1], data))

    def __repr__(self):
        return f'Blockchain: {self.chain}'

    def replace_chain(self, chain):
        """
        Replace the local chain with the incoming one if the following applies:
          - The incoming chain is longer than the local one.
          - The incoming chain is formatted properly.
        """
        if len(chain) <= len(self.chain):
            raise Exception('Cannot replace. The incoming chain must be longer.')

        try:
            Blockchain.is_valid_chain(chain)
        except Exception as e:
            raise Exception(f'Cannot replace. The incoming chain is invalid: {e}')

        self.chain = chain

    def to_json(self):
        """
        Serialize the blockchain into a list of blocks.
        """
        return list(map(lambda block: block.to_json(), self.chain))

    @staticmethod
    def from_json(chain_json):
        """
        Deserialize a list of serialized blocks into a Blokchain instance.
        The result will contain a chain list of Block instances.
        """
        blockchain = Blockchain()
        blockchain.chain = list(
            map(lambda block_json: Block.from_json(block_json), chain_json)
        )

        return blockchain

    @staticmethod
    def is_valid_chain(chain):
        """
        Validate the incoming chain.
        Enforce the following rules of the blockchain:
          - the chain must start with the genesis block
          - blocks must be formatted correctly
        """
        if chain[0] != Block.genesis():
            raise Exception('The genesis block must be valid')

        for i in range(1, len(chain)):
            block = chain[i]
            last_block = chain[i-1]
            Block.is_valid_block(last_block, block)

        Blockchain.is_valid_transaction_chain(chain)

    @staticmethod
    def is_valid_transaction_chain(chain):
        """
        Enforce the rules of a chain composed of blocks of transactions.
            - Each transaction must only appear once in the chain.
            - There can only be one mining reward per block.
            - Each transaction must be valid.
        """
        transaction_ids = set()

        for i in range(len(chain)):
            block = chain[i]
            has_mining_reward = False

            for transaction_json in block.data:
                transaction = Transaction.from_json(transaction_json)

                if transaction.id in transaction_ids:
                    raise Exception(f'Transaction {transaction.id} is not unique')

                transaction_ids.add(transaction.id)

                if transaction.input == MINING_REWARD_INPUT:
                    if has_mining_reward:
                        raise Exception(
                            'There can only be one mining reward per block. '\
                            f'Check block with hash: {block.hash}'
                        )

                    has_mining_reward = True
                else:
                    historic_blockchain = Blockchain()
                    historic_blockchain.chain = chain[0:i]
                    historic_balance = Wallet.calculate_balance(
                        historic_blockchain,
                        transaction.input['address']
                    )

                    if historic_balance != transaction.input['amount']:
                        raise Exception(
                            f'Transaction {transaction.id} has an invalid '\
                            'input amount'
                        )

                Transaction.is_valid_transaction(transaction)

def main():
    blockchain = Blockchain()
    blockchain.add_block('one')
    blockchain.add_block('two')

    print(blockchain)
    print(f'blockchain.py ___name__: {__name__}')

if __name__ == '__main__':
    main()

Blockchain: [Block(timestamp: 1, last_hash: genesis_last_hash, hash: genesis_hash, data: [], difficulty: 3, nonce: genesis_nonce), Block(timestamp: 1575278097.145539, last_hash: genesis_hash, hash: 0f36cd705da21e551a59066bdde1cbbf8a51256aa550c1c44d928895665bcb67, data: one, difficulty: 4, nonce: 16), Block(timestamp: 1575278097.147438, last_hash: 0f36cd705da21e551a59066bdde1cbbf8a51256aa550c1c44d928895665bcb67, hash: 06949074de3833da35d3366db5c89a136aaf4d92cc47e1c3e68f36484f8fd031, data: two, difficulty: 5, nonce: 34)] blockchain.py ___name__: __main__
In [7]:
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.asymmetric import ec
from cryptography.hazmat.primitives.asymmetric.utils import (
    encode_dss_signature,
    decode_dss_signature
)
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.exceptions import InvalidSignature

class Wallet:
    def __init__(self, blockchain=None):
        self.blockchain = blockchain
        self.address = str(uuid.uuid4())[0:8]
        self.private_key = ec.generate_private_key(
            ec.SECP256K1(),
            default_backend()
        )
        self.public_key = self.private_key.public_key()
        self.serialize_public_key()

    @property
    def balance(self):
        return Wallet.calculate_balance(self.blockchain, self.address)

    def sign(self, data):
        return decode_dss_signature(self.private_key.sign(
            json.dumps(data).encode('utf-8'),
            ec.ECDSA(hashes.SHA256())
        ))

    def serialize_public_key(self):
        self.public_key = self.public_key.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo
        ).decode('utf-8')

    @staticmethod
    def verify(public_key, data, signature):
        deserialized_public_key = serialization.load_pem_public_key(
            public_key.encode('utf-8'),
            default_backend()
        )

        (r, s) = signature

        try:
            deserialized_public_key.verify(
                encode_dss_signature(r, s),
                json.dumps(data).encode('utf-8'),
                ec.ECDSA(hashes.SHA256())    
            )
            return True
        except InvalidSignature:
            return False

    @staticmethod
    def calculate_balance(blockchain, address):
        balance = STARTING_BALANCE

        if not blockchain:
            return balance

        for block in blockchain.chain:
            for transaction in block.data:
                if transaction['input']['address'] == address:
                    # Any time the address conducts a new transaction it resets
                    # its balance
                    balance = transaction['output'][address]
                elif address in transaction['output']:
                    balance += transaction['output'][address]

        return balance

def main():
    wallet = Wallet()
    print(f'wallet.__dict__: {wallet.__dict__}')

    data = { 'foo': 'bar' }
    signature = wallet.sign(data)
    print(f'signature: {signature}')

    should_be_valid = Wallet.verify(wallet.public_key, data, signature)
    print(f'should_be_valid: {should_be_valid}')

    should_be_invalid = Wallet.verify(Wallet().public_key, data, signature)
    print(f'should_be_invalid: {should_be_invalid}')

if __name__ == '__main__':
    main()

wallet.__dict__: {'blockchain': None, 'address': '931398a2', 'private_key': <cryptography.hazmat.backends.openssl.ec._EllipticCurvePrivateKey object at 0x10ae14668>, 'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElk6+2D7ZO0cfVQVZS9rsXYey+O28Stzk\n8wRMOV0F7jRYeh4R5NpQx9EnmWgIAaUvv0rmj7dhMbRS0PWzqbLgUw==\n-----END PUBLIC KEY-----\n'} signature: (94743314351653674976515242954598088162415349490524533940006996002257412808174, 111748276331797854191966404850785075572631812299803717553676173535590960786167) should_be_valid: True should_be_invalid: False
In [8]:
'''TRANSACTION POOL'''
class TransactionPool:
    def __init__(self):
        self.transaction_map = {}

    def set_transaction(self, transaction):
        self.transaction_map[transaction.id] = transaction


    def existing_transaction(self, address):
        for transaction in self.transaction_map.values():
            if transaction.input['address'] == address:
                return transaction
    
    def transaction_data(self):
        return list(map(
            lambda transaction: transaction.to_json(),
            self.transaction_map.values()
        ))

    def clear_blockchain_transactions(self, blockchain):
        for block in blockchain.chain:
            for transaction in block.data:
                try:
                    del self.transaction_map[transaction['id']]
                except KeyError:
                    pass
In [9]:
'''Transaction'''

class Transaction:
    def __init__(
        self,
        sender_wallet=None,
        recipient=None,
        amount=None,
        id=None,
        output=None,
        input=None
    ):
        self.id = id or str(uuid.uuid4())[0:8]
        self.output = output or self.create_output(
            sender_wallet,
            recipient,
            amount
        )
        self.input = input or self.create_input(sender_wallet, self.output)

    def create_output(self, sender_wallet, recipient, amount):
        if amount > sender_wallet.balance:
            raise Exception('Amount exceeds balance')

        output = {}
        output[recipient] = amount
        output[sender_wallet.address] = sender_wallet.balance - amount

        return output

    def create_input(self, sender_wallet, output):
        return {
            'timestamp': time.time(),
            'amount': sender_wallet.balance,
            'address': sender_wallet.address,
            'public_key': sender_wallet.public_key,
            'signature': sender_wallet.sign(output)
        }

    def update(self, sender_wallet, recipient, amount):
        if amount > self.output[sender_wallet.address]:
            raise Exception('Amount exceeds balance')

        if recipient in self.output:
            self.output[recipient] = self.output[recipient] + amount
        else:
            self.output[recipient] = amount

        self.output[sender_wallet.address] = \
            self.output[sender_wallet.address] - amount

        self.input = self.create_input(sender_wallet, self.output)

    def to_json(self):
        return self.__dict__

    @staticmethod
    def from_json(transaction_json):
        return Transaction(**transaction_json)

    @staticmethod
    def is_valid_transaction(transaction):
        if transaction.input == MINING_REWARD_INPUT:
            if list(transaction.output.values()) != [MINING_REWARD]:
                raise Exception('Invalid mining reward')
            return

        output_total = sum(transaction.output.values())

        if transaction.input['amount'] != output_total:
            raise Exception('Invalid transaction output values')

        if not Wallet.verify(
            transaction.input['public_key'],
            transaction.output,
            transaction.input['signature']
        ):
            raise Exception('Invalid signature')

    @staticmethod
    def reward_transaction(miner_wallet):
        output = {}
        output[miner_wallet.address] = MINING_REWARD

        return Transaction(input=MINING_REWARD_INPUT, output=output)

def main():
    transaction = Transaction(Wallet(), 'recipient', 15)
    print(f'transaction.__dict__: {transaction.__dict__}')

    transaction_json = transaction.to_json()
    restored_transaction = Transaction.from_json(transaction_json)
    print(f'restored_transaction.__dict__: {restored_transaction.__dict__}')

if __name__ == '__main__':
    main()
transaction.__dict__: {'id': '87d2f3f5', 'output': {'recipient': 15, 'e5c7a076': 985}, 'input': {'timestamp': 1575278100.03131, 'amount': 1000, 'address': 'e5c7a076', 'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKICI7eF61glkGiaW1mzVIWWew75Y4Z6w\nEM9OcC96snpJOCRNl9TK+AqxsABrw1tnwvNI66hmJXRuu4YwG97kSQ==\n-----END PUBLIC KEY-----\n', 'signature': (24402925174531200565800114030440923187948989778634865216294251011069329157399, 104162572889219278658019457931199534300022531170646960401410900266047503174034)}} restored_transaction.__dict__: {'id': '87d2f3f5', 'output': {'recipient': 15, 'e5c7a076': 985}, 'input': {'timestamp': 1575278100.03131, 'amount': 1000, 'address': 'e5c7a076', 'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEKICI7eF61glkGiaW1mzVIWWew75Y4Z6w\nEM9OcC96snpJOCRNl9TK+AqxsABrw1tnwvNI66hmJXRuu4YwG97kSQ==\n-----END PUBLIC KEY-----\n', 'signature': (24402925174531200565800114030440923187948989778634865216294251011069329157399, 104162572889219278658019457931199534300022531170646960401410900266047503174034)}}
In [ ]:
 
In [24]:
VALIDATOR_FEE = 20
In [114]:
# client side

class Peer:
    
    def __init__(self, ip):
        self.MANAGER_IP = '192.168.43.152'
        self.MANAGER_PORT = 8821
        self.server_port = 5521
        self.client_ip = ip
        self.server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
        self.server_thread = threading.Thread(target=self.start_server)
        self.server_thread.daemon = True
        self.server_thread.start()
        self.stop_server = False
        self.peers = set()
        self.blockchain = Blockchain()
        self.wallet = Wallet(self.blockchain)
        self.transaction_pool = TransactionPool()
        self.validators = dict()
       
    
    def start_server(self):
        self.server_socket.bind((self.client_ip, self.server_port))
        self.server_socket.listen(100)
        while True:
            client_peer, address = self.server_socket.accept()
            # actions
            data = client_peer.recv(4096).decode('utf-8')
            # add  
            if data.startswith('add-peer'):
                self.peers.add(data.split()[1])
            
            elif data.startswith('remove-peer'):
                self.peers.remove(data.split()[1])
            
            elif data.startswith('sync-chain'):
                client_peer.send("ok".encode('utf-8'))
                blockchain_recieved = pickle.loads(client_peer.recv(4096))
                self.blockchain.replace_chain(blockchain_recieved)
            
            elif data.startswith('add-block'):
                client_peer.send("ok".encode('utf-8'))
                block = pickle.loads(client_peer.recv(4096))
                potential_chain = self.blockchain.chain[:]
                potential_chain.append(block)
                try:
                    self.blockchain.replace_chain(potential_chain)
                    self.transaction_pool.clear_blockchain_transactions(
                        self.blockchain
                    )
                    sock = socket.socket()
                    sock.connect((self.MANAGER_IP, self.MANAGER_PORT))
                    sock.send("update-transactionpool".encode('utf-8'))
                    sock.send(pickle.dumps(self.transaction_pool))
                    sock.close()
                    print('\n -- Successfully replaced the local chain')
                except Exception as e:
                    _inform_peers('report-faulty blockchain')
                    print(f'\n -- Did not replace chain: {e}')

            
            elif data.startswith('report-faulty blockchain'):
                self.validators[self.getLeader()]-=PUNISHMENT_FEE
            
            
            elif data.startswith('add-transaction'):
                print("Adding trasaction")
                client_peer.send("ok".encode('utf-8'))
                
                new_transaction = client_peer.recv(4096)
                print("Recieved")
                self.transaction_pool.set_transaction(pickle.loads(new_transaction))
                print("Done")
            
            elif data.startswith('update-transactionpool'):
                client_peer.send("ok".encode('utf-8'))
                new_transactionpool = client_peer.recv(4096)
                self.transaction_pool = pickle.loads(new_transactionpool)
            
            elif data.startswith('add-validator'):
                client_peer.send("ok".encode('utf-8'))
                msg = client_peer.recv(4096).decode('utf-8')
                client_ip, amount = msg.split()
                self.validators[client_ip] = int(amount)
                
            client_peer.close()
                
        self.server_socket.close()
      
    
    def _stop_server(self):
        self.stop_server = True
    
    def _inform_manager_add(self, message):
        sock = socket.socket()
        sock.connect((self.MANAGER_IP, self.MANAGER_PORT))
        sock.send(message.encode('utf-8'))
        peers = pickle.loads(sock.recv(4096))
        sock.close()
        sock = socket.socket()
        sock.connect((self.MANAGER_IP, self.MANAGER_PORT))
        sock.send(f"get-transactionpool {self.client_ip}".encode('utf-8'))
        self.transaction_pool = pickle.loads(sock.recv(40960))
        self.peers = peers
        sock.close()
        sock = socket.socket()
        sock.connect((self.MANAGER_IP, self.MANAGER_PORT))
        print('adding validator')
        sock.send(f"get-validators {self.client_ip}".encode('utf-8'))
        self.validators = pickle.loads(sock.recv(40960))
        print('done')
        sock.close()
    
    
    def _inform_manager_delete(self, message):
        sock = socket.socket()
        sock.connect((self.MANAGER_IP, self.MANAGER_PORT))
        sock.send(message.encode('utf-8'))
    
    
    def leave_network(self):
        return self._inform_manager_delete(f"remove {self.client_ip}")
        
        
    def join_network(self):
        return self._inform_manager_add(f'add {self.client_ip}')
    
    
    def show_blockchain(self):
        print(self.blockchain.to_json())
    
    
    def _inform_peers(self, message, amount=None, data=None):
        for peer in self.peers:
            sock=socket.socket()
            sock.connect((peer, self.server_port))
            sock.send(message.encode("utf-8"))
            if amount is not None:
                rec = sock.recv(4096)
                sock.send(f'{self.client_ip} {amount}'.encode('utf-8'))
            if data is not None:
                rec = sock.recv(4096)
                sock.send(pickle.dumps(data))
            sock.close()
    
    def sync_chain(self):
        for peer in self.peers:
            sock = socket.socket()
            try:
                self._inform_peers("sync-chain", data=self.blockchain)
            except Exception as e:
                print(peer)
                print(e)
            finally:
                sock.close()
    
    def mine_transaction(self):
        transaction_data = self.transaction_pool.transaction_data()
        print(transaction_data)
        transaction_data.append(Transaction.reward_transaction(self.wallet).to_json())
        self.blockchain.add_block(transaction_data)
        block = self.blockchain.chain[-1]
        try:
            print("adding")
            self._inform_peers("add-block", data=block)
            print("clearting transaction")
            self.transaction_pool.clear_blockchain_transactions(self.blockchain)
            print("informing peers")
            self._inform_peers("update-transactionpool", data=self.transaction_pool)
        except Exception as e:
            print(e)
    
    
    def wallet_transact(self, transaction_data):
        transaction = self.transaction_pool.existing_transaction(self.wallet.address)
        if transaction:
            transaction.update(
                self.wallet,
                transaction_data['recipient'],
                transaction_data['amount']
            )
        else:
            print("Creating")
            transaction = Transaction(
                self.wallet,
                transaction_data['recipient'],
                transaction_data['amount']
            )
            print("Done")
        
        try:
            self.transaction_pool.set_transaction(transaction)
            sock = socket.socket()
            sock.connect((self.MANAGER_IP, self.MANAGER_PORT))
            sock.send(f'update-transactionpool'.encode('utf-8'))
            _ = sock.recv(4096)
            sock.send(pickle.dumps(self.transaction_pool))
            sock.close()
            self._inform_peers("add-transaction", data=transaction)
        except Exception as e:
            print(e)
    
        
    def _add_stake(self, amount):
        if amount > VALIDATOR_FEE:
            self.wallet_transact({'recipient': '0', 'amount': amount})
            self.validators[self.client_ip]=amount
            sock = socket.socket()
            sock.connect((self.MANAGER_IP, self.MANAGER_PORT))
            sock.send(f'update-validator {self.client_ip} {amount}'.encode('utf-8'))
            sock.close()
            self._inform_peers('add-validator', amount=amount, data=None)
        else:
            print("Amount less than fee")
    
    def mine_pos_transaction(self):
        if self.client_ip == self.getLeader():
            self.mine_transaction()
        else:
            print("Cannot mine because not a leader")
        pass
    def get_leader(self):
        return max(self.validators, key = lambda x : self.validators[x])
In [115]:
client = Peer('192.168.43.228')
In [116]:
client.join_network()
adding validator done
In [117]:
client._add_stake(30)
Creating Done
In [118]:
client.validators
Out[118]:
{'192.168.43.154': 60, '192.168.43.228': 30}
In [119]:
client.get_leader()
Out[119]:
'192.168.43.154'
In [120]:
print(client.peers)
client.wallet.address
{'192.168.43.154'}
Out[120]:
'5463f20a'
In [121]:
client.transaction_pool.transaction_data()
Out[121]:
[{'id': 'ba0dee45',
  'output': {'0': 60, '5f41261d': 940},
  'input': {'timestamp': 1575296080.9897263,
   'amount': 1000,
   'address': '5f41261d',
   'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMteOvPvspxka/rRh5yb7uDzkpaqigvPI\nlqDl1gv3bz3i/DHKA+CpD49M8JRJY3jKs7/KfjAJTiRC+PXApgJxfQ==\n-----END PUBLIC KEY-----\n',
   'signature': (1273332479922796040475211761970847087209911730184075411111625181820760646310,
    98843888115935956190421703689612750220458122563767855075124391578424904095255)}},
 {'id': '00376c51',
  'output': {'0': 30, '5463f20a': 970},
  'input': {'timestamp': 1575296101.370921,
   'amount': 1000,
   'address': '5463f20a',
   'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEi6sSKf+y3P/zAiLDN2AVz01TVdN8JI6Q\nz67JindJgy6pZ9DNKgOoGCkeErUuSeFpAE73IFxwyWPo2a0QlX4F6g==\n-----END PUBLIC KEY-----\n',
   'signature': (27220409299400205234094930045906444344139216777306689644222916097383441049473,
    51505093775487752620498549404952990758957779909047413919471728213368819362012)}}]
-- Successfully replaced the local chain
In [125]:
client.blockchain.to_json()
Out[125]:
[{'timestamp': 1,
  'last_hash': 'genesis_last_hash',
  'hash': 'genesis_hash',
  'data': [],
  'difficulty': 3,
  'nonce': 'genesis_nonce'},
 {'timestamp': 1575296119.1689024,
  'last_hash': 'genesis_hash',
  'hash': '03515bf87d0aa02877e9bf21dec57e7a72b3b49d2dbcc1647aa8edc101297dba',
  'data': [{'id': 'ba0dee45',
    'output': {'0': 60, '5f41261d': 940},
    'input': {'timestamp': 1575296080.9897263,
     'amount': 1000,
     'address': '5f41261d',
     'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEMteOvPvspxka/rRh5yb7uDzkpaqigvPI\nlqDl1gv3bz3i/DHKA+CpD49M8JRJY3jKs7/KfjAJTiRC+PXApgJxfQ==\n-----END PUBLIC KEY-----\n',
     'signature': (1273332479922796040475211761970847087209911730184075411111625181820760646310,
      98843888115935956190421703689612750220458122563767855075124391578424904095255)}},
   {'id': '00376c51',
    'output': {'0': 30, '5463f20a': 970},
    'input': {'timestamp': 1575296101.370921,
     'amount': 1000,
     'address': '5463f20a',
     'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEi6sSKf+y3P/zAiLDN2AVz01TVdN8JI6Q\nz67JindJgy6pZ9DNKgOoGCkeErUuSeFpAE73IFxwyWPo2a0QlX4F6g==\n-----END PUBLIC KEY-----\n',
     'signature': (27220409299400205234094930045906444344139216777306689644222916097383441049473,
      51505093775487752620498549404952990758957779909047413919471728213368819362012)}},
   {'id': '5c296409',
    'output': {'5f41261d': 50},
    'input': {'address': '*mining-reward*'}}],
  'difficulty': 4,
  'nonce': 10},
 {'timestamp': 1575296161.9476094,
  'last_hash': '03515bf87d0aa02877e9bf21dec57e7a72b3b49d2dbcc1647aa8edc101297dba',
  'hash': '01ce5b677dcacf67ac55dbe780fa693fba6fbc1c9ae691f39e94a69ae0a7de48',
  'data': [{'id': '75642ced',
    'output': {'5f41261d': 50},
    'input': {'address': '*mining-reward*'}}],
  'difficulty': 5,
  'nonce': 73}]
In [18]:
transaction_data = {
    
    'recipient': 'df89fdf0',
    'amount': 50
    
}
In [ ]:
 
In [20]:
client.wallet_transact(transaction_data=transaction_data)
--------------------------------------------------------------------------- UnboundLocalError Traceback (most recent call last) <ipython-input-20-24bf976980b9> in <module>() ----> 1 client.wallet_transact(transaction_data=transaction_data) <ipython-input-9-53b27ec81c0f> in wallet_transact(self, transaction_data) 149 transaction_flag = self.transaction_pool.existing_transaction(self.wallet.address) 150 if transaction_flag: --> 151 print(transaction.to_json()) 152 print(type(transaction)) 153 transaction.update( UnboundLocalError: local variable 'transaction' referenced before assignment
In [21]:
client.transaction_pool.transaction_data()
Out[21]:
[{'id': 'edf73fe9',
  'output': {'df89fdf0': 50, '38bea9bc': 950},
  'input': {'timestamp': 1575092471.3096619,
   'amount': 1000,
   'address': '38bea9bc',
   'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFqBirQLUFNVGqHev8NFG4fNRt7+iGKad\nAcXS+wJpU0LjF+umTBoNDyzHJ0mZjMLZmbwGsUiISX4XUvhReBHl9A==\n-----END PUBLIC KEY-----\n',
   'signature': (86601791649062792261259163818488106919215240991768732060206245758704579503075,
    104832379344641634525548202349387821152870488219339694798708214331528975953900)}}]
In [26]:
client.transaction_pool.transaction_data()
Out[26]:
[{'id': 'edf73fe9',
  'output': {'df89fdf0': 50, '38bea9bc': 950},
  'input': {'timestamp': 1575092471.3096619,
   'amount': 1000,
   'address': '38bea9bc',
   'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFqBirQLUFNVGqHev8NFG4fNRt7+iGKad\nAcXS+wJpU0LjF+umTBoNDyzHJ0mZjMLZmbwGsUiISX4XUvhReBHl9A==\n-----END PUBLIC KEY-----\n',
   'signature': (86601791649062792261259163818488106919215240991768732060206245758704579503075,
    104832379344641634525548202349387821152870488219339694798708214331528975953900)}}]
In [22]:
client.mine_transaction()
[{'id': 'edf73fe9', 'output': {'df89fdf0': 50, '38bea9bc': 950}, 'input': {'timestamp': 1575092471.3096619, 'amount': 1000, 'address': '38bea9bc', 'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFqBirQLUFNVGqHev8NFG4fNRt7+iGKad\nAcXS+wJpU0LjF+umTBoNDyzHJ0mZjMLZmbwGsUiISX4XUvhReBHl9A==\n-----END PUBLIC KEY-----\n', 'signature': (86601791649062792261259163818488106919215240991768732060206245758704579503075, 104832379344641634525548202349387821152870488219339694798708214331528975953900)}}] adding [Errno 60] Operation timed out
In [24]:
client.blockchain.to_json()
Out[24]:
[{'timestamp': 1,
  'last_hash': 'genesis_last_hash',
  'hash': 'genesis_hash',
  'data': [],
  'difficulty': 3,
  'nonce': 'genesis_nonce'},
 {'timestamp': 1575092698.500551,
  'last_hash': 'genesis_hash',
  'hash': '052cd7524fbbc22d0578f7cc6c1730a109d17f104af2233d1b1550d2f70a1470',
  'data': [{'id': 'edf73fe9',
    'output': {'df89fdf0': 50, '38bea9bc': 950},
    'input': {'timestamp': 1575092471.3096619,
     'amount': 1000,
     'address': '38bea9bc',
     'public_key': '-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEFqBirQLUFNVGqHev8NFG4fNRt7+iGKad\nAcXS+wJpU0LjF+umTBoNDyzHJ0mZjMLZmbwGsUiISX4XUvhReBHl9A==\n-----END PUBLIC KEY-----\n',
     'signature': (86601791649062792261259163818488106919215240991768732060206245758704579503075,
      104832379344641634525548202349387821152870488219339694798708214331528975953900)}},
   {'id': '9826ad0b',
    'output': {'38bea9bc': 50},
    'input': {'address': '*--official-mining-reward--*'}}],
  'difficulty': 4,
  'nonce': 3}]
In [27]:
client.sync_chain()
192.168.43.10 [Errno 60] Operation timed out
In [126]:
import jovian
In [ ]:
jovian.commit()
[jovian] Saving notebook..
In [ ]: