예제 #1
0
 def __init__(self):
     """Initialize the server to handle information."""
     parser_arguments = self.parse_commandline()
     (self.port, self.peers, self.difficulty, self.numtxinblock,
      self.numcores) = parser_arguments
     self.utxo = UTXO(self.numtxinblock, self.difficulty, self.numcores)
     self.message_map = self.message_mapping()  # Opcodes and message sizes
     self.socket = self.create_socket()
     self.close_status = mp.Value('i', 0)  # Checks close status
     self.socket_list = []  # Hold sockets for peers
     self.broadcasting = True
     self.listen_socket()  # Listen for clients
예제 #2
0
    def handleTxs(self, txs):
        result = []
        for tx in txs:
            if self.isValidTx(tx):
                result.append(tx)

                for inp in tx.inputs:
                    utxo = UTXO(inp.prevTxHash, inp.outputIndex)
                    self._pool.removeUTXO(utxo)

                for (i, out) in enumerate(tx.outputs):
                    utxo = UTXO(tx.hash, i)
                    self._pool.addUTXO(utxo, out)

        return result
예제 #3
0
    def isValidTx(self, tx: Transaction) -> bool:
        inputSum = decimal.Decimal()
        pool = copy.deepcopy(self._pool)
        for (index, inp) in enumerate(tx.inputs):
            utxo = UTXO(inp.prevTxHash, inp.outputIndex)

            if not pool.contains(utxo):
                return False

            output = pool.getTxOutput(utxo)

            pubKey = output.address
            message = tx.getRawDataToSign(index)
            if not Crypto.verifySignature(pubKey, message, inp.signature):
                return False

            inputSum += output.value
            pool.removeUTXO(utxo)

        outputSum = decimal.Decimal()
        for out in tx.outputs:
            if out.value < 0:
                return False
            outputSum += out.value

        return inputSum >= outputSum
예제 #4
0
    def isValidTx(self, tx: Transaction):
        utxo_used_this_tx = []
        input_sum = 0
        output_sum = 0

        for i, inp in enumerate(tx.inputs):
            utxo = UTXO(inp.prev_tx_hash, inp.output_index)
            # (1)
            if not self.utxo_pool.contains(utxo):
                return False

            # (2)
            previous_tx_output_corresponding_to_inp = self.utxo_pool.get_transaction_output(
                utxo)
            public_key = previous_tx_output_corresponding_to_inp.address
            raw_tx_message = tx.get_raw_data_to_sign(i)
            if not Crypto.verify_signature(public_key, raw_tx_message,
                                           inp.signature):
                return False

            # (3)
            if utxo in utxo_used_this_tx:
                return False

            utxo_used_this_tx.append(utxo)
            input_sum += previous_tx_output_corresponding_to_inp.value

        # (4)
        for op in tx.outputs:
            if op.value < 0: return False
            output_sum += op.value

        # (5)
        return (input_sum >= output_sum)
예제 #5
0
def getBalance():
    if request.method == 'GET':
        return render_template('index.html', valid_email=True)
    elif request.method == 'POST':
        valid_email = True
        total_balance = -1
        utxo_acc = UTXO(request.form['email'])
        try:
            total_balance = utxo_acc.displayBalance()
        except:
            valid_email = False
        finally:
            return render_template('index.html',
                                   useremail=request.form['email'],
                                   total_balance=total_balance,
                                   valid_email=valid_email)
예제 #6
0
    def handle_txs(self, possible_txs):
        valid_tx = []
        for tx in possible_txs:
            if not self.isValidTx(tx):
                continue

            valid_tx.append(tx)
            for inp in tx.inputs:
                used_utxo = UTXO(inp.prev_tx_hash, inp.output_index)
                self.utxo_pool.remove_utxo(used_utxo)

            tx_hashcode = tx.hashcode  # should this refresh ?
            for i, op in enumerate(tx.outputs):
                new_utxo = UTXO(tx_hashcode, i)
                self.utxo_pool.add_utxo(new_utxo, op)

        return valid_tx
예제 #7
0
 def add_coinbase_to_utxo_pool(self, block, utxo_pool):
     tx = block.coinbase
     for i, op in enumerate(tx.outputs):
         utxo = UTXO(tx.hashcode, i)
         utxo_pool.append(utxo, op)
예제 #8
0
def generate_transaction(receiver, amount, commission):
    publicKey = Key._publicKey
    privateKey = Key._privateKey

    publicKey_ser = publicKey.serialize(compressed=False)

    total = 0
    in_counter = 0
    out_counter = 0
    vin = []
    vout = []
    tmpUTXO = []  # Temporary UTXOset for resulting transaction

    # Check if amount or commission is negative
    if amount <= 0 or commission < 0:
        print('Invalid input value')
        return False

    # Check if balance is sufficient
    for key, value in UTXOset._myUTXOset.iterator():
        d_value = json.loads(value)

        address_ser = base64.b64decode(d_value["address"])
        if address_ser != publicKey_ser:
            UTXOset.Pop_myUTXO(key, d_value["index"])
            continue

        tmpUTXO.append(
            UTXO(key, d_value["index"], d_value["address"], d_value["amount"]))
        total += d_value["amount"]
        if total > amount + commission:
            break

    # Insufficient balance
    if total < amount + commission:
        print('Insufficient BTC balance')
        return False

    # Generate inputs
    for output in tmpUTXO:
        total += output.amount
        in_counter += 1

        # Generate signatures
        unlockSig = privateKey.generate_sign(output.txOutid)
        unlock = privateKey.ecdsa_serialize(unlockSig)
        vin.append(Vin(output.txOutid, output.index, unlock))

    # Generate outputs
    vout.append(Vout(amount, receiver))
    change = total - commission - amount
    if change > 0:
        vout.append(Vout(change, publicKey_ser))

    # Generate tx_id
    SumString = str(in_counter)
    for input in vin:
        SumString = SumString + str(input.tx_id) + str(input.index) + str(
            input.unlock)
    SumString = SumString + str(out_counter)
    for output in vout:
        SumString = SumString + str(output.value) + str(output.lock)
    keccak_hash = keccak.new(digest_bits=256)
    keccak_hash.update(SumString.encode(
        'ascii'))  # keccak_hash == tx_id of current transaction

    # Add to UTXOset and myUTXOset
    address = base64.b64encode(receiver).decode('utf-8')
    UTXOset.Insert_UTXO(keccak_hash.hexdigest().encode(), 0, address,
                        float(amount))

    if (change > 0):
        address = base64.b64encode(publicKey_ser).decode('utf-8')
        UTXOset.Insert_UTXO(keccak_hash.hexdigest().encode(), 1, address,
                            float(amount))
        UTXOset.Insert_myUTXO(keccak_hash.hexdigest().encode(), 1, address,
                              float(amount))

    # Delete from UTXOset and myUTXOset
    for otuput in tmpUTXO:
        UTXOset.Pop_UTXO(output.txOutid, output.index)
        UTXOset.Pop_myUTXO(output.txOutid, output.index)

    # Add to memoryPool
    Transaction.Insert_MemoryPool(keccak_hash.hexdigest().encode(), in_counter,
                                  vin, out_counter, vout)

    return True
예제 #9
0
class Server(object):
    """Initialize sockets to receive and transmit blockchain data."""
    def __init__(self):
        """Initialize the server to handle information."""
        parser_arguments = self.parse_commandline()
        (self.port, self.peers, self.difficulty, self.numtxinblock,
         self.numcores) = parser_arguments
        self.utxo = UTXO(self.numtxinblock, self.difficulty, self.numcores)
        self.message_map = self.message_mapping()  # Opcodes and message sizes
        self.socket = self.create_socket()
        self.close_status = mp.Value('i', 0)  # Checks close status
        self.socket_list = []  # Hold sockets for peers
        self.broadcasting = True
        self.listen_socket()  # Listen for clients
        # self.create_peer_sockets()  # Connect peer sockets for broadcasting

    def parse_commandline(self):
        """Handle command line arguments."""
        arg_parser = argparse.ArgumentParser()
        arg_parser.add_argument('--port',
                                help="Port node is listening on",
                                required=True)
        arg_parser.add_argument('--peers',
                                help="Comma separated list of peer ports",
                                required=True)
        arg_parser.add_argument('--difficulty',
                                help="Number of leading bytes",
                                default=0,
                                required=False)
        arg_parser.add_argument('--numtxinblock',
                                default=50000,
                                help="Transactions in a block",
                                required=False)
        arg_parser.add_argument('--numcores',
                                default=0,
                                help="Number of cores",
                                required=False)

        # List of arguments
        print("Parsing arguments.")
        arg_list = arg_parser.parse_args()
        port = int(arg_list.port)
        peers = arg_list.peers.split(',')
        difficulty = int(arg_list.difficulty)
        numtxinblock = int(arg_list.numtxinblock)
        numcores = int(arg_list.numcores)

        return (port, peers, difficulty, numtxinblock, numcores)

    def message_mapping(self):
        """Opcodes for message types and respective size mapping."""
        message_size = {
            TX_OPCODE: 128,
            CLOSE_OPCODE: 0,
            BLOCK_OPCODE: (160 + (128 * self.numtxinblock)),
            GET_BLOCK_OPCODE: 32
        }
        return (message_size)

    def create_socket(self):
        """Initialize socket for node to begin receiving requests."""
        new_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        new_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        new_socket.bind(('localhost', self.port))
        return (new_socket)

    def listen_socket(self):
        """Listen for new clients individually."""
        self.socket.listen(10)
        print("Socket is listening on port: ", self.port)
        process_queue = []  # Create queue to hold processes
        while True:
            if (self.close_status.value == 0):
                client, address = self.socket.accept()
                print("Connection received from: ", address)
                cur_process = mp.Process(target=self.connect_socket,
                                         args=(client, address))
                process_queue.append(cur_process)  # Hold processes
                cur_process.start()
            else:
                print("Received close message.")
                break  # If close has been called, stop listening
            for p in process_queue:
                p.join()

    def create_peer_sockets(self):
        """Create sockets for each peer node."""
        for peer in self.peers:  # Broadcast to all peers in list
            peer_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            print("Connecting to peer: ", peer)
            peer_socket.connect(('localhost', int(peer)))
            self.socket_list.append(peer_socket)  # Add socket to list
        print("Peer socket list: ", self.socket_list)

    def broadcast_message(self, message):
        """Share transactions/blocks with peer nodes."""
        for peer_socket in self.socket_list:  # Broadcast to all peers in list
            print("Broadcasting to peer socket.")
            peer_socket.send(message)
            print("Broadcasting to peer finished.")

    def close_peer_sockets(self):
        """Close list of peer sockets."""
        for peer_socket in self.socket_list:
            peer_socket.close()

    def process_data_bytes(self, client_socket):
        """Only receive bytes of a certain message."""
        # Receive first byte, check it then receive more
        # If this is done, no need to truncate data
        receiving_data = client_socket.recv(1)
        message = bytearray()
        message.extend(receiving_data)
        if (len(message) > 0):
            opcode = chr(int(message[0:1].hex(), 16))  # Check opcode of byte
            message.extend(client_socket.recv(self.message_map.get(opcode)))
            # print("Error!")
        return (message)

    def connect_socket(self, client_socket, client_address):
        """Handle connections and incoming data."""
        start_time = time.time()  # Start recording time
        self.create_peer_sockets()  # Connect peer sockets for broadcasting
        message = self.process_data_bytes(client_socket)
        while message:
            print("Length of current message is:", len(message))
            opcode = chr(int(message[0:1].hex(), 16))  # Get opcode
            print("Current opcode: ", opcode)

            # Get specific message
            msg_start = 1
            msg_end = 1 + self.message_map.get(opcode)
            current_message = message[msg_start:msg_end]
            msg_with_opcode = message[0:msg_end]

            # Based on current opcode, execute specific action
            if opcode == TX_OPCODE:
                # Create transaction and broadcast if legal
                new_tx = Transaction(current_message)
                broadcasting, block = self.utxo.process_transaction(new_tx)
            elif opcode == CLOSE_OPCODE:
                self.close_status.value = 1  # Indicate close
                broadcasting = True  # Forward close signal to peers
                block = False
                print("Broadcasting close message.")
                # self.stop()
            elif opcode == BLOCK_OPCODE:
                broadcasting = False
                # Initialize received block
                block = Block(self.difficulty, msg_with_opcode, self.numcores)
                print("Block received: ", block)
            elif opcode == GET_BLOCK_OPCODE:
                # Pass specified block information to sender
                block_at_height = self.utxo.process_get_block(current_message)
                client_socket.send(block_at_height)

            # Decide when to broadcast transactions and blocks
            if broadcasting and block:
                self.broadcast_message(msg_with_opcode)
                self.broadcast_message(block.msg_bytearray)
                print("Block and transaction broadcast to peer.")
            elif block:
                self.broadcast_message(block.msg_bytearray)
                print("Block broadcast to peer.")
            elif broadcasting:
                self.broadcast_message(msg_with_opcode)

            # Read in more data
            message = self.process_data_bytes(client_socket)

        # Message is completely read and node will close down
        self.close_peer_sockets()  # Close peers after finishing
        self.close()
        end_time = time.time()  # Stop recording time
        work_time = end_time - start_time  # Compute duration of process
        print("Time to run blockchain: ", work_time)

    def close(self):
        """Close the node socket."""
        self.socket.close()
예제 #10
0
 def removeInputWithUTXO(self, ut: UTXO):
     for index, inp in enumerate(self._inputs):
         u = UTXO(inp.prevTxHash, inp.outputIndex)
         if u == ut:
             self._inputs = self._inputs[:index] + self._inputs[index + 1:]
             return