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
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
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
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)
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)
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
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)
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
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()
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