import socket from minicoin import Blockchain # ============== Config do Servidor ============== HOST = '127.0.0.1' # localhost PORT = 3421 minicoin_ascii = r""" ;;;;;;;;;; ;;;;;;;;;;;;;;;;;; ;;;;;; ;;;;;; ;;;;; ;;;;;;;;;;;;;; ;;;;;; ;;;;; ;;;;;;;;;;;;;;;;;; ;;;;; ;;;; ;;;;;;;;;;;;;;;;;;;;: ;;;; ;;;; ;;;;;;;;;;;;;;;:;;;;;;;; ;;;; ;;; ;;;;;;; ;;;;;; ;;;;;;; ;;; ;;; ;;;;;;; ;: ;;;;; ; ;;;;;;; ;;; ;;; ;;;;;;; ;; ;;;; ;; ;;;;;;; ;;;; ;;; ;;;;;;; ;;; ;; ;;; ;;;;;;; ;;; ;;; ;;;;;;; ;;;; ;;;; ;;;;;;; ;;; ;;;; ;;;;;; ;;;;; ;;;; ;;;;;; ;;;; ;;;; :;;;;;;;;;;;;;;;;;;;;; ;;;; :;;;: ;;;;;;;;;;;;;;;;;; :;;;;; ;;;;; ;;;;;;;;;;;;;; ;;;;;; ;;;;; ;;;;;; ;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;; """ # ============== Funções Auxiliares ============== def send_and_receive(cli_conn, message): ''' Envia uma mensagem ao cliente e aguarda uma resposta. ''' cli_conn.sendall(message.encode('utf-8')) data = cli_conn.recv(1024) if not data: print("[LOG] Cliente desconectou.") return None return data.decode('utf-8') def get_int_from_client(cli_conn, prompt_msg) -> int | None: ''' Pede um inteiro ao cliente, tratando erros. ''' try: data = send_and_receive(cli_conn, prompt_msg) if data is None: # cliente desconectou return None num = int(data) if num <= 0: cli_conn.sendall("[ERROR] O valor deve ser um número positivo (maior que zero).\n\n".encode('utf-8')) return None return num except ValueError: cli_conn.sendall("[ERROR] Valor inválido, insira um número inteiro.\n\n".encode('utf-8')) return None def minicoin_interface(cli_conn): ''' Envia o menu principal ao cliente e espera resposta. ''' interface = ( "======= Minicoin System =======\n" "1. Criar conta\n" "2. Ver saldo\n" "3. Depositar\n" "4. Sacar\n" "5. Ver histórico de transações\n" "6. Sair\n" "===============================\n" "[EVENT] Escolha uma opção: " ) return send_and_receive(cli_conn, interface) # ============== Ações do Menu ============== def create_account(blockchain, cli_conn): ''' Processa criação da conta, criando o Bloco Genese que tem o nome do titular da conta e o deposito inicial. ''' if blockchain: cli_conn.sendall("[ERROR] Conta já existe. Não é possível criar outra.\n".encode('utf-8')) return blockchain acc_owner = send_and_receive(cli_conn, "[EVENT] Digite o nome do titular da conta: ") initial_deposit = get_int_from_client(cli_conn, "[EVENT] Digite o valor do depósito inicial: ") if initial_deposit is None: return None blockchain = Blockchain() blockchain.createGenesisBlock(acc_owner, initial_deposit) cli_conn.sendall(f"[SUCCESS] Conta criada com sucesso para {acc_owner} com depósito inicial de ${initial_deposit} Minicoins.\n\n".encode('utf-8')) return blockchain def check_balance(blockchain, cli_conn) -> int: ''' Processa verificação do saldo. Retorna 0 caso sucesso, 1 cc. ''' if not blockchain: cli_conn.sendall("[ERROR] Nenhuma conta criada ainda.\n\n".encode('utf-8')) return 1 if not blockchain.isValid(): cli_conn.sendall("[ERROR] Blockchain está corrompida.\n\n".encode('utf-8')) return 1 balance = blockchain.getBalance() cli_conn.sendall(f"[INFO] Saldo atual do usuário {blockchain.chain[0].data.acc_owner}: ${balance} Minicoins.\n\n".encode('utf-8')) return 0 def make_deposit(blockchain, cli_conn) -> int: ''' Processa um deposito. Retorna 0 caso sucesso, 1 cc. ''' if not blockchain: cli_conn.sendall("[ERROR] Nenhuma conta criada ainda.\n\n".encode('utf-8')) return 1 amount = get_int_from_client(cli_conn, "[EVENT] Digite o valor a depositar: ") if amount is None: return 1 blockchain.addBlock(amount, "deposit") cli_conn.sendall(f"[SUCCESS] Depósito de ${amount} Minicoins realizado com sucesso.\n\n".encode('utf-8')) return 0 def make_withdrawal(blockchain, cli_conn) -> int: ''' Processa um saque. Captura erros com try/except. Retorna 0 em sucesso, 1 cc. ''' if not blockchain: cli_conn.sendall("[ERROR] Nenhuma conta criada ainda.\n\n".encode('utf-8')) return 1 amount = get_int_from_client(cli_conn, "[EVENT] Digite o valor a sacar: ") if amount is None: return 1 try: blockchain.addBlock(amount, "withdraw") cli_conn.sendall(f"[SUCCESS] Saque de ${amount} Minicoins realizado com sucesso.\n\n".encode('utf-8')) return 0 except Exception as e: cli_conn.sendall(f"[ERROR] Erro ao realizar saque: {e}\n\n".encode('utf-8')) return 1 def view_transaction_history(blockchain, cli_conn) -> int: ''' Processa visualização do histórico. Retorna 0 em sucesso, 1 cc. ''' if not blockchain: cli_conn.sendall(b"[ERROR] Nenhuma conta criada ainda.\n\n") return 1 if not blockchain.isValid(): cli_conn.sendall("[ERROR] Blockchain está corrompida.\n\n".encode('utf-8')) return 1 registry = "=== Histórico de Transações ===\n\n" for i, block in enumerate(blockchain.chain): data = block.data if i == 0: registry += f"[INFO] Bloco {i}: Titular: {data.acc_owner}, Saldo Inicial: ${data.amount}\n\n" else: registry += f"[INFO] Bloco {i}: Operação: {data.operation}, Valor: ${data.amount}\n\n" registry += "\n===============================\n" cli_conn.sendall(registry.encode('utf-8')) return 0 # ============== Função Principal ============== def main(): print("Iniciando servidor Minicoin...") print(minicoin_ascii) # 1. abra um socket, usando a porta específica do serviço with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: # permite que reinicie o server apos cntrl+c s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) s.bind((HOST, PORT)) # 2. fique em modo de escuta, à espera de requisição, de conexão de clientes # fila de tamanho 5 s.listen(5) print(f"[LOG] Servidor escutando em {HOST}:{PORT}") blockchain = None # servidor sempre em execução while True: # 3. chegando uma requisição, abra um novo socket para atende-la # cli_conn <- novo socket pavra o cliente # cli_addr <- endereço (IP, PORT) do cliente cli_conn, cli_addr = s.accept() with cli_conn: print(f"[LOG] [NOVA CONEXÃO] Conectado por: {cli_addr}") try: cli_conn.recv(1024) # consome a mensagem que inicia o servidor # 4. comunique-se com o cliente # fica recebendo dados até o cliente fechar while True: user_request = minicoin_interface(cli_conn) match user_request: case '1': blockchain = create_account(blockchain, cli_conn) if blockchain: print(f"[LOG] Conta criada para {cli_addr}") else: print(f"[LOG] [ERROR] Erro ao criar a conta para {cli_addr}") case '2': if check_balance(blockchain, cli_conn) == 0: print(f"[LOG] Saldo enviado para {cli_addr}.") else: print("[LOG] [ERROR] Erro na checagem de saldo.") case '3': if make_deposit(blockchain, cli_conn) == 0: print(f"[LOG] Depósito processado para {cli_addr}") else: print("[LOG] [ERROR] Erro na tentativa de deposito.") case '4': if make_withdrawal(blockchain, cli_conn) == 0: print(f"[LOG] Saque processado para {cli_addr}") else: print("[LOG] [ERROR] Erro na tentativa de saque.") case '5': if view_transaction_history(blockchain, cli_conn) == 0: print(f"[LOG] Histórico enviado para {cli_addr}") else: print("[LOG] [ERROR] Erro na tentativa de visualizar o histórico de transações.") case '6': print(f"[LOG] Saindo do sistema bancário para {cli_addr}.") break case _: cli_conn.sendall("[LOG] Opção INVÁLIDA, digite apenas o número da opção que deseja.\n\n".encode('utf-8')) except (BrokenPipeError, ConnectionResetError): print(f"[LOG] [CONEXÃO QUEBRADA] Cliente {cli_addr} desconectou abruptamente.") print(f"[LOG] [CONEXÃO ENCERRADA] {cli_addr}") print("Aguardando novas conexões...") if __name__ == "__main__": main()