Exemple #1
0
def create_block():

    with open(parentdir + "/core" + "/key.pem") as f:
        key = RSA.importKey(f.read())
    """ First transaction for first block """
    trans1 = {
        "vin": [None],  # First transaction
        "vout": [{
            "amount": 1000000.0,
            "address": key.publickey().exportKey().decode()
        }]
    }
    trans1["txid"] = sha(json.dumps(trans1))

    block1 = {
        "header": {
            "prev_block_hash": None,
            "merkle_root": None,
            "timestamp": None,
            "nonce": None,
            "difficulty_target": "0"
        },
        "transactions": [trans1]
    }

    utxo = UTXO()
    blockdb = Blockdb()

    blockdb.add_block(block1)
    utxo.add_trans(block1['transactions'], sha(json.dumps(block1['header'])))
Exemple #2
0
    def new_block(self, conn, block):

        utxo = UTXO()
        blockdb = Blockdb()
        # check if block is already in the local chain
        exists = blockdb.get_block_by_hash(sha(block))
        if exists != None:
            return None
        # ensure the block is valid
        if not self.verify("block", block):
            return None
        logging.debug("New block verified")
        # remove all transaction in the block from unconfirmed pool
        self.pool.check_new_block(sha(block))
        # add all transaction outputs to utxo
        utxo.add_trans(block['transactions'], sha(json.dumps(block)))
        # remove all inputs from utxo
        utxo.remove_trans(block['transactions'])
        # save block in Blockdb
        blockdb.add_block(block)
        # propogate block
        self.client.prop_block(json.dumps(block).encode())
Exemple #3
0
class Client():
    """
    Client should initiate all data transfer
     - progpogate for new transactions
     - progpogate for new block
     - request recent blocks
     - request IPs
    """

    PORT = 5050
    DISCONNECT_MESSAGE = "DISCONNECT"

    def __init__(self):

        self.utxo = UTXO()
        self.blockdb = Blockdb()
        self.connections = {}  # {ip : conn}
        with open(parentdir + "/network" + "/addr.json") as file:
            self.ips = json.load(file)
        self.startup_connect()

    def startup_connect(self):
        """
        This method is called when a node first enters/reenters a network,
        it queries its database of known nodes in the network and
        establishes a connection
        """

        for num, ip in enumerate(
                self.ips.keys()):  # a list of ip addresses to connect to
            client = socket.socket(
                socket.AF_INET, socket.SOCK_STREAM
            )  # IPv4, TCP ## SHOULD IT BE IN THE FOR LOOP ???
            try:
                client.connect((ip, self.PORT))
                self.connections[ip] = client
                print("Client connected to :", ip)
            except:  ## ENSURE THE FAILURE IS DUE TO A SERVER BEING DOWN
                self.failed_conn(ip)

    def connect_to_ip(self,
                      ip):  ## CHECK TO ENSURE CONNECTION IS NOT ALREADY MADE
        """
        When a new node on the network connects to local node, this 
        method is establisehd a two-way connection
        """

        if ip in self.connections.keys():
            return None

        client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        try:
            client.connect((ip, self.PORT))
            self.connections[ip] = client
        except:
            self.failed_conn(ip)
        else:
            self.add_ip(ip)

    def add_ip(self, ip):

        if ip not in self.ips.keys():
            self.ips[ip] = 0

    def failed_conn(self, ip):
        """
        When a node can't be reached this method is called 
        """

        if self.ips[ip] < 3:
            self.ips[ip] += 1
        else:
            del self.ips[ip]

    def prop_trans(self, trans):
        """
        trans : dict
            dictionary containing transaction data
        """

        trans = json.dumps(trans)
        for conn in self.connections.values():
            conn.send("NEW_TRANS".encode())
            _ = conn.recv(1024).decode()
            trans_encoded = trans.encode()
            trans_size = str(sys.getsizeof(trans_encoded))
            conn.send(trans_size.encode())
            _ = conn.recv(1024).decode()
            conn.sendall(
                trans_encoded
            )  ## RETURNS NONE IF SUCESSFUL, THROWS ERROR OTHERWISE, ADD ERROR HANDLING
        print("Transaction Sent")

    def prop_block(self, block):

        block = json.dumps(block)
        for conn in self.connections.values():
            conn.send("NEW_BLOCK".encode())
            block_encoded = block.encode()
            block_size = str(sys.getsizeof(block_encoded))
            conn.send(block_size.encode())
            conn.sendall(block_encoded)
        print("Block Sent")

    def req_chain(self):

        longest = (None, 0)
        for conn in self.connections.values():
            conn.send("GET_CHAIN_LEN".encode())
            chain_len = int(conn.recv(1024).decode())
            if chain_len > longest[1]:
                longest = (conn, chain_len)

        latest = self.blockdb.get_latest()
        # in case the server can't connect to any nodes
        if longest[0] == None:
            print("No nodes are currently available")
        elif longest[1] <= latest:
            print("Blockchain is up to date")
        else:
            # once longest chain is found request the blocks
            self.req_block(longest[0])

    def req_block(self, conn):
        """
        conn : socket object
            the connection with the longest chain

        Send the hash of the latest block all nodes will send back the 
        number of missing blocks, this node will extend using the longest chain
        method
        """

        latest = self.blockdb.get_latest()
        conn.send("GET_BLOCKS".encode())
        conn.send(str(latest).encode())
        num_blocks = int(conn.recv(1024).decode())

        block_num = 1
        while block_num <= num_blocks:
            block_size = int(conn.recv(1024).decode())
            block = conn.recv(1024)

            while len(block) < block_size:
                block += conn.recv(1024)

            block = json.loads(block.decode())
            self.utxo.add_trans(block['transactions'], sha(json.dumps(block)))
            self.blockdb.add_block(block)  ## DOESNT VARIFY THE BLOCK
            block_num += 1
        print("Blockchain Updated")

    def req_node(self):

        for conn in self.connections.values():
            conn.send("GET_NODES")
            file_size = int(conn.recv(1024).decode())

            data = conn.recv(1024).decode()
            while len(data) < file_size:
                data += conn.recv(1024)

            data = json.loads(data.decode())
            for key in data.keys():
                if key not in self.ips.keys:
                    self.ips[key] = 0  ## HAVE TO RECIEVE LOTS OF DATA

    def close(self):

        for conn in self.connections.values():
            conn.send(self.DISCONNECT_MESSAGE.encode())
            conn.close()