diff --git a/app/main.go b/app/main.go deleted file mode 100644 index addbe376c44e680442d12d688b5f25f07f92d2ba..0000000000000000000000000000000000000000 --- a/app/main.go +++ /dev/null @@ -1,750 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "gopkg.in/yaml.v3" - "io/ioutil" - "math/rand" - "net" - . "node/types" - "os" - "strconv" - "strings" - "time" -) - -/** - * @file - * @brief Distributed Systems - Blockchain - * @author Xavier Perret - * @date 28/09/2022 - 05/10/2022 - */ - -// Config - is the configuration of a node, it's id, address, port and neighbours -type Config struct { - ID int `yaml:"id"` - Address string `yaml:"address"` - Port int `yaml:"port"` - Neighbours []struct { - ID int `yaml:"id"` - Address string `yaml:"address"` - Port int `yaml:"port"` - } `yaml:"neighbours"` -} - -// ServerReady is true if the server can listen to new connection attempt, used for initialization -var ServerReady = false - -// DB in memory database -var DB Database - -// ConnectionType is the type of connection used by the server -var ConnectionType = "tcp" - -// a node generates a transaction “trans”, stores it in its object-based storage -// and sends it to all nodes of the network. This function relies on the broadcast by wave distributed -// algorithm. -// trans - -func createTransaction(trans Transaction) { - -} - -func isTransactionValid(trans Transaction) bool { - return false -} - -// A node sends a request to all nodes to check if the transaction trans, stored locally, -// is the same as the ones stored in the other nodes. The result (rate) is a percentage representing the -// transactions which are the same as the one stored locally. This function relies on the broadcast by wave -// with ACK distributed algorithm. However, some adjustments are needed to implement this function. -func vote(server net.Listener, trans Transaction, config Config, parentAddress string, stimulatedByClient bool) AckTransaction { - var ack AckTransaction - ack.Id = "" - ack.TotalNodes = 0 - ack.AmountOfCorrectNode = 0 - - var transactionToRate Transaction - - for _, transactionInDatabase := range DB { - if transactionInDatabase.Id == trans.Id { - ack.Id = trans.Id - transactionToRate = transactionInDatabase - } - } - - // todo add stuff - - if ack.Id != "" { - if transactionToRate.Receiver != trans.Receiver { - ack.AmountOfCorrectNode = 0 - ack.TotalNodes = 1 - } - if transactionToRate.Sender != trans.Sender { - ack.AmountOfCorrectNode = 0 - ack.TotalNodes = 1 - } - if transactionToRate.Amount != trans.Amount { - ack.AmountOfCorrectNode = 0 - ack.TotalNodes = 1 - } - } - if ack.TotalNodes == 0 { - ack.AmountOfCorrectNode = 1 - ack.TotalNodes = 1 - } - - var newAck AckTransaction - - reach := false - count := 0 - - limit := len(config.Neighbours) - if stimulatedByClient { - reach = true - go sendVoteToAllNeighbours(config, trans, parentAddress) - } - - var mess Message - for count < limit { - if count != 0 || reach { - fmt.Println("Count is ", count, " to ", limit) - - connection, err := server.Accept() - if err != nil { - fmt.Println("Error accepting: ", err.Error()) - err = nil - continue - } - fmt.Println("*****************************************") - fmt.Println("Processing client request number ", count) - fmt.Println("The connection is ", connection) - fmt.Println("The remote address is ", connection.RemoteAddr()) - fmt.Println("The local address is ", connection.LocalAddr()) - fmt.Println("*****************************************") - - err = json.NewDecoder(connection).Decode(&mess) - if err != nil { - fmt.Println("Error while decoding the transactionInDatabase", err) - err = nil - continue - } - - if mess.MessageType == "AckResponse" { - var body map[string]interface{} = mess.MessageBody.(map[string]interface{}) - newAck.Id = body["id"].(string) - newAck.AmountOfCorrectNode = body["amountOfCorrectNode"].(int) - newAck.TotalNodes = body["totalNodes"].(int) - if ack.Id == newAck.Id { - ack.TotalNodes += newAck.TotalNodes - ack.AmountOfCorrectNode += newAck.AmountOfCorrectNode - } - fmt.Println("Node correct : ", ack.AmountOfCorrectNode, "out of ", ack.TotalNodes) - } - } - - count++ // received message - if !reach { - reach = true - go sendVoteToAllNeighbours(config, trans, parentAddress) - } - } - fmt.Println("***********************************") - fmt.Println("Transaction has been rated") - fmt.Println("Node correct : ", ack.AmountOfCorrectNode, "out of ", ack.TotalNodes) - fmt.Println("***********************************") - - if !stimulatedByClient { - address := parentAddress - for _, neighbour := range config.Neighbours { - fmt.Println("Comparing ", neighbour.Address, " with ", parentAddress) - - if neighbour.Address == parentAddress { - address = address + ":" + strconv.Itoa(neighbour.Port) - fmt.Println("Calculated address is ", address) - } - } - fmt.Println("Sending to ", address) - go sendAckToNeighbour(config, ack, address) - } - return ack -} - -func sendVoteToAllNeighbours(config Config, trans Transaction, parentIp string) { - for neighbour := range config.Neighbours { - ip := config.Neighbours[neighbour].Address - port := strconv.Itoa(config.Neighbours[neighbour].Port) - address := ip + ":" + port - if parentIp != ip { - go sendVoteToNeighbour(config, trans, address) - } - } -} - -func fake(authenticTrans Transaction, fakeTrans Transaction) { - -} - -func printTransaction(trans Transaction) { - fmt.Println("--------------------") - fmt.Println("Printing transaction") - fmt.Println("The id is ", trans.Id) - fmt.Println("The sender is ", trans.Sender) - fmt.Println("The receiver is ", trans.Receiver) - fmt.Println("The amount is ", trans.Amount) - fmt.Println("--------------------") -} - -func listAllTransactions() { - for i, transaction := range DB { - fmt.Println("***********INDEX N=,", i, "****************") - printTransaction(transaction) - fmt.Println("***********************************") - } -} - -// userCreatedTransaction is a function to interactively create a transaction using the terminal -func userCreatedTransaction(config Config) Transaction { - fmt.Println("--------------------STARTING USER CREATED TRANSACTION--------------------") - var trans Transaction - now := time.Now().String() - randomString := strconv.Itoa(rand.Int()) - trans.Id = now + randomString - - fmt.Println() - var receiver string - fmt.Print("\nPlease enter the receiver") - _, err := fmt.Scanln(&receiver) - if err != nil { - return Transaction{} - } - trans.Receiver = receiver - - var sender string - fmt.Print("\nPlease enter the sender") - _, err = fmt.Scanln(&sender) - if err != nil { - return Transaction{} - } - trans.Sender = sender - - fmt.Println("Creating a transaction with sender id:", trans.Sender) - fmt.Println("transaction id is ", trans.Id) - fmt.Print("\nEnter amount :") - - _, err = fmt.Scanln(&trans.Amount) - if err != nil { - fmt.Println("error") - os.Exit(1) - } - - for neighbour := range config.Neighbours { - fmt.Println("Neighbour ", config.Neighbours[neighbour].ID) - fmt.Println("address ", config.Neighbours[neighbour].Address) - fmt.Println("port", config.Neighbours[neighbour].Port) - } - fmt.Println("--------------------FINISHED USER CREATED TRANSACTION--------------------") - - return trans -} - -// sendTransactionToNeighbour is a function to send a transaction to a neighbour -func sendTransactionToNeighbour(config Config, trans Transaction, destinationIp string, destinationPort string) { - fmt.Println() - ipAddr := destinationIp + ":" + destinationPort - fmt.Println("Trying to connect to ", destinationIp, ":", destinationPort) - - conn, err := net.Dial(ConnectionType, ipAddr) - if err != nil { - fmt.Println("Error while connecting to the neighbour", err) - os.Exit(1) - } - - mess := Message{ - MessageType: "transaction", - MessageBody: trans, - } - fmt.Println("Sending message to neighbour", mess) - encoder := json.NewEncoder(conn) - err = encoder.Encode(mess) - if err != nil { - fmt.Println("Error while encoding the transaction", err) - os.Exit(1) - } - conn.Close() - - fmt.Println("MessageBody successfully sent to neighbour") -} - -func sendTransactionToAllNeighbours(config Config, trans Transaction) { - for neighbour := range config.Neighbours { - fmt.Println("Sending transaction to neighbour ", config.Neighbours[neighbour].ID) - ip := config.Neighbours[neighbour].Address - port := strconv.Itoa(config.Neighbours[neighbour].Port) - sendTransactionToNeighbour(config, trans, ip, port) - } -} - -func printAllNeighbours(config Config) { - fmt.Println("Listing neighbours :") - for neighbour := range config.Neighbours { - fmt.Println("Neighbour ", config.Neighbours[neighbour].ID) - } -} - -func processClient(connection net.Conn, server net.Listener, config Config, stimulatedByClient bool) { - fmt.Println("Processing client") - fmt.Println("The connection is ", connection) - fmt.Println("The remote address is ", connection.RemoteAddr()) - fmt.Println("The local address is ", connection.LocalAddr()) - - var mess Message - - jsonDecoder := json.NewDecoder(connection) - err := jsonDecoder.Decode(&mess) - if err != nil { - fmt.Println("Error while decoding the message", err) - } - fmt.Println("The message is ", mess) - - fmt.Println("A client has connected") - - if mess.MessageType == "error" { - fmt.Println("Error while decoding the message", err) - return - } else if mess.MessageType == "transaction" { - fmt.Println("Received a transaction") - var trans Transaction - var body map[string]interface{} = mess.MessageBody.(map[string]interface{}) - trans.Id = body["id"].(string) - trans.Receiver = body["receiver"].(string) - trans.Sender = body["sender"].(string) - trans.Amount = body["amount"].(string) - for _, transaction := range DB { - if transaction.Id == trans.Id { - return - } - } - processTransaction(server, config, trans, stimulatedByClient) - } else if mess.MessageType == "rate" { - fmt.Println("Received a rate") - var trans Transaction - var body map[string]interface{} = mess.MessageBody.(map[string]interface{}) - trans.Id = body["id"].(string) - trans.Receiver = body["receiver"].(string) - trans.Sender = body["sender"].(string) - trans.Amount = body["amount"].(string) - printTransaction(trans) - // todo change this for cloud - address := strings.Split(connection.RemoteAddr().String(), ":")[0] - vote(server, trans, config, address, stimulatedByClient) - } else if mess.MessageType == "fake" { - fmt.Println("Received a fake") - var fakeTransaction Transaction - var body map[string]interface{} = mess.MessageBody.(map[string]interface{}) - fakeTransaction.Id = body["id"].(string) - fakeTransaction.Receiver = body["receiver"].(string) - fakeTransaction.Sender = body["sender"].(string) - fakeTransaction.Amount = body["amount"].(string) - for transactionIndex := range DB { - if DB[transactionIndex].Id == fakeTransaction.Id { - DB[transactionIndex].Sender = fakeTransaction.Sender - DB[transactionIndex].Receiver = fakeTransaction.Receiver - DB[transactionIndex].Amount = fakeTransaction.Amount - fmt.Println("Successfully replaced the legitimate transaction with a fake one!") - } - } - } else if mess.MessageType == "list" { - fmt.Println("Received a list all transactions order") - listAllTransactionsToClient(connection) - } else { - fmt.Println("Unknown message type") - } -} - -func listAllTransactionsToClient(conn net.Conn) { - mess := Message{ - MessageType: "list", - MessageBody: DB, - } - - fmt.Println("Sending message to neighbour", mess) - encoder := json.NewEncoder(conn) - err := encoder.Encode(mess) - if err != nil { - fmt.Println("Error while encoding the transaction", err) - os.Exit(1) - } - conn.Close() -} - -func processTransaction(server net.Listener, config Config, trans Transaction, stimulatedByClient bool) { - fmt.Println("Processing transaction") - printTransaction(trans) - fmt.Println("The transaction has been added") - - reach := false - count := 0 - - limit := len(config.Neighbours) - if stimulatedByClient { - reach = true - addTransactionToDb(trans) - go sendTransactionToAllNeighbours(config, trans) - } - - for count < limit { - if count != 0 || reach { - fmt.Println("Count is ", count, " to ", limit) - - connection, err := server.Accept() - if err != nil { - fmt.Println("Error accepting: ", err.Error()) - err = nil - continue - } - fmt.Println("*****************************************") - fmt.Println("Processing client request number ", count) - fmt.Println("The connection is ", connection) - fmt.Println("The remote address is ", connection.RemoteAddr()) - fmt.Println("The local address is ", connection.LocalAddr()) - fmt.Println("*****************************************") - - err = json.NewDecoder(connection).Decode(&trans) - if err != nil { - fmt.Println("Error while decoding the transaction", err) - err = nil - continue - } - fmt.Println("Received back a transaction") - printTransaction(trans) - } - - count++ // received message - if !reach { - reach = true - addTransactionToDb(trans) - go sendTransactionToAllNeighbours(config, trans) - } - } - fmt.Println("***********************************") - fmt.Println("All transactions have been received") - fmt.Println("***********************************") -} - -func addTransactionToDb(trans Transaction) { - for _, transaction := range DB { - if transaction.Id == trans.Id { - return - } - } - - DB = append(DB, trans) -} - -func serverLoop(config Config, stimulatedByClient bool) { - addr := "0.0.0.0" - port := strconv.FormatInt(int64(config.Port), 10) - - server, err := net.Listen("tcp", addr+":"+port) - if err != nil { - fmt.Println("Error while creating the server", err) - os.Exit(1) - } - - fmt.Println("Server is ready to accept connections") - fmt.Println("listening on ", addr, ":", port) - - ServerReady = true - for { - // Listening for connections - connection, err := server.Accept() - if err != nil { - fmt.Println("Error accepting: ", err.Error()) - } else { - processClient(connection, server, config, stimulatedByClient) - } - } -} - -func userInputLoop(config Config, isAlsoServer bool) { - for true { - var operation string - if !ServerReady { - continue - } - fmt.Println() - fmt.Println() - fmt.Println("Please enter the operation you want to do") - fmt.Println("1. Create a transaction") - fmt.Println("2. Rate a transaction (from the client)") - fmt.Println("3. Fabricate a fake transaction") - fmt.Println("4. Print all transactions") - fmt.Println("8. Exit") - fmt.Print("Your choice: ") - _, err := fmt.Scanln(&operation) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - switch operation { - case "1": - fmt.Println("You chose to create a transaction") - if isAlsoServer { - fmt.Println("Not yet implemented!") - //newTrans := userCreatedTransaction(config) - //createTransaction(newTrans) - } else { - newTrans := userCreatedTransaction(config) - addTransactionToDb(newTrans) - printAllNeighbours(config) - fmt.Println("TRANSACTION READY TO BE SENT") - fmt.Println("Please enter the ID of the neighbour you want to send the transaction to") - var neighbourID string - _, err := fmt.Scanln(&neighbourID) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - neighbourIDInt, err := strconv.ParseInt(neighbourID, 10, 64) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - sendTransactionToNeighbour(config, newTrans, config.Neighbours[neighbourIDInt].Address, strconv.Itoa(config.Neighbours[neighbourIDInt].Port)) - } - break - case "2": - fmt.Println("You chose to rate a transaction") - listAllTransactions() - fmt.Print("\nPlease enter the index of the transaction you want to rate:") - var transID string - _, err := fmt.Scanln(&transID) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - transIDInt, err := strconv.ParseInt(transID, 10, 64) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - printAllNeighbours(config) - fmt.Println("Please enter the ID of the neighbour you want to send the transaction to") - var neighbourID string - _, err = fmt.Scanln(&neighbourID) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - neighbourIDInt, err := strconv.ParseInt(neighbourID, 10, 64) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - address := config.Neighbours[neighbourIDInt].Address + ":" + strconv.Itoa(config.Neighbours[neighbourIDInt].Port) - fmt.Println("Sending rate demand to ", address) - sendVoteToNeighbour(config, DB[transIDInt], address) - break - case "3": - fmt.Println("You chose to fabricate a fake transaction") - fmt.Println("You chose to fake a transaction") - listAllTransactions() - fmt.Print("\nPlease enter the index of the transaction you want to overwrite by faking:") - var transID string - _, err := fmt.Scanln(&transID) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - transIDInt, err := strconv.ParseInt(transID, 10, 64) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - printAllNeighbours(config) - fmt.Println("Please enter the ID of the neighbour you to ask to fake the given transaction") - var neighbourID string - _, err = fmt.Scanln(&neighbourID) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - neighbourIDInt, err := strconv.ParseInt(neighbourID, 10, 64) - if err != nil { - fmt.Println("error :", err.Error()) - os.Exit(1) - } - tmpFakeTrans := userCreatedTransaction(config) - transToFake := DB[transIDInt] - fakeTrans := Transaction{ - Id: transToFake.Id, - Sender: tmpFakeTrans.Sender, - Receiver: tmpFakeTrans.Receiver, - Amount: tmpFakeTrans.Amount, - } - ip := config.Neighbours[neighbourIDInt].Address - port := strconv.Itoa(config.Neighbours[neighbourIDInt].Port) - address := ip + ":" + port - sendFakeTransactionToNeighbour(fakeTrans, address) - break - case "4": - fmt.Println("You chose to print all transactions") - listAllTransactions() - break - case "5": - fmt.Println("You chose to ask for all transactions of a given node") - break - case "6": - break - case "7": - break - case "8": - fmt.Println("You chose to exit") - os.Exit(0) - default: - fmt.Println("You chose an invalid option") - break - } - } -} - -func sendFakeTransactionToNeighbour(trans Transaction, address string) { - fmt.Println() - fmt.Println("Trying to connect to ", address) - - conn, err := net.Dial(ConnectionType, address) - if err != nil { - fmt.Println("Error while connecting to the neighbour", err) - os.Exit(1) - } - - mess := Message{ - MessageType: "fake", - MessageBody: trans, - } - fmt.Println("Sending message to neighbour", mess) - encoder := json.NewEncoder(conn) - err = encoder.Encode(mess) - if err != nil { - fmt.Println("Error while encoding the transaction", err) - os.Exit(1) - } - conn.Close() - - fmt.Println("MessageBody successfully sent to neighbour") -} - -func sendVoteToNeighbour(config Config, trans Transaction, address string) { - fmt.Println() - fmt.Println("Trying to connect to ", address) - - conn, err := net.Dial(ConnectionType, address) - if err != nil { - fmt.Println("Error while connecting to the neighbour", err) - os.Exit(1) - } - - mess := Message{ - MessageType: "rate", - MessageBody: trans, - } - fmt.Println("Sending message to neighbour", mess) - encoder := json.NewEncoder(conn) - err = encoder.Encode(mess) - if err != nil { - fmt.Println("Error while encoding the transaction", err) - os.Exit(1) - } - conn.Close() - - fmt.Println("MessageBody successfully sent to neighbour") -} - -func sendAckToNeighbour(config Config, ack AckTransaction, address string) { - fmt.Println("*******************STARTING TO SEND ACK*******************") - fmt.Println("Trying to connect to ", address) - var ackToSend AckTransaction - ackToSend.Id = ack.Id - ackToSend.AmountOfCorrectNode = ack.AmountOfCorrectNode - ackToSend.TotalNodes = ack.TotalNodes - - conn, err := net.Dial(ConnectionType, address) - if err != nil { - fmt.Println("Error while connecting to the neighbour", err) - os.Exit(1) - } - - mess := Message{ - MessageType: "AckResponse", - MessageBody: ackToSend, - } - fmt.Println("Sending message to neighbour", mess) - encoder := json.NewEncoder(conn) - err = encoder.Encode(mess) - if err != nil { - fmt.Println("Error while encoding the transaction", err) - os.Exit(1) - } - conn.Close() - - fmt.Println("MessageBody successfully sent to neighbour") - fmt.Println("*******************FINISHED ACK*******************") -} - -func main() { - if len(os.Args) <= 2 { - fmt.Println("First argument should be the path of the config file '--path=<config.yaml>'") - fmt.Println("Second argument should be either '--server' or '--client'") - os.Exit(1) - } - - // INIT CONFIG - args := os.Args[1:] - fmt.Println("Program started with arguments", args) - fmt.Println("config file path is ", args[0][7:]) - - buf, err := ioutil.ReadFile(args[0][7:]) - if err != nil { - fmt.Println("Error while reading the config file", err) - fmt.Println("Exiting...") - os.Exit(1) - } - - var c Config - err = yaml.Unmarshal(buf, &c) - if err != nil { - os.Exit(1) - - } - fmt.Println("The config is ", c) - fmt.Println("The ID is ", c.ID) - fmt.Println("The port is ", c.Port) - fmt.Println("The neighbours are :") - for neighbour := range c.Neighbours { - fmt.Println("\tThe neighbour is ", c.Neighbours[neighbour].ID) - fmt.Println("\tThe neighbour address is ", c.Neighbours[neighbour].Address) - fmt.Println("\tThe neighbour port is ", c.Neighbours[neighbour].Port) - fmt.Println() - } - fmt.Println() - - // INIT SERVER AND/OR CLIENT LOOP - isStimulatedByClient := false - - if len(os.Args) == 4 { - if os.Args[3] == "--root" { - isStimulatedByClient = true - } - } - if os.Args[2] == "--server" { - fmt.Println("Starting server") - go serverLoop(c, isStimulatedByClient) - userInputLoop(c, true) - } - if os.Args[2] == "--client" { - fmt.Println("Starting client") - go serverLoop(c, isStimulatedByClient) - userInputLoop(c, false) - } -}