diff --git a/app/command-line/userinput.go b/app/command-line/userinput.go index 537c93efe38b5d391940e28b09bba8653fb96f58..f2ed7370875e09a8ade0b46038d21c52c32d18fa 100644 --- a/app/command-line/userinput.go +++ b/app/command-line/userinput.go @@ -5,8 +5,54 @@ import ( . "node/types" "os" "strconv" + "time" ) +// 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 +} + func UserInputLoop(config Config, isAlsoServer bool) { for true { var operation string diff --git a/app/process-connection/process-connection.go b/app/process-connection/process-connection.go index c09c12a6e521a59b7bbe9abafdcc682235c788bd..4c8b5f1561991ce08243e1e49789aebeac4df8dc 100644 --- a/app/process-connection/process-connection.go +++ b/app/process-connection/process-connection.go @@ -9,14 +9,16 @@ import ( . "node/types" "node/utilities" "os" + "strconv" "strings" "sync" ) -func listAllTransactionsToClient(conn net.Conn) { +func listAllTransactionsToClient(conn net.Conn, objectStorage Blob) { + database := ObjectStorageAPI.ReadDatabaseFromBlobStorage(objectStorage) mess := Message{ MessageType: "list", - MessageBody: DB, + MessageBody: database, } fmt.Println("Sending message to neighbour", mess) @@ -26,7 +28,11 @@ func listAllTransactionsToClient(conn net.Conn) { fmt.Println("Error while encoding the transaction", err) os.Exit(1) } - conn.Close() + + err = conn.Close() + if err != nil { + fmt.Println("Error closing connection", err) + } } func processTransaction(serverListener net.Listener, serverConfig Config, objectStorage Blob, mess Message, amIRoot bool) { @@ -87,16 +93,16 @@ func processTransaction(serverListener net.Listener, serverConfig Config, object count++ // received message if !reach { reach = true - addTransactionToDb(trans) - go sendTransactionToAllNeighbours(serverConfig, trans) + database = ObjectStorageAPI.ReadDatabaseFromBlobStorage(objectStorage) + database = ObjectStorageAPI.AddTransactionToBlobStorage(trans, database, objectStorage) + go Sender.SendTransactionToAllNeighbours(serverConfig, trans) } } fmt.Println("***********************************") fmt.Println("All transactions have been received") fmt.Println("***********************************") } - -func processRate(serverListener net.Listener, serverConfig Config, mess Message, amIRoot bool) { +func processRate(conn net.Conn, serverListener net.Listener, serverConfig Config, objectStorage Blob, mess Message, amIRoot bool) { var trans Transaction var body map[string]interface{} = mess.MessageBody.(map[string]interface{}) trans.Id = body["id"].(string) @@ -107,24 +113,110 @@ func processRate(serverListener net.Listener, serverConfig Config, mess Message, // todo change this for cloud address := strings.Split(conn.RemoteAddr().String(), ":")[0] - vote(server, trans, serverConfig, address, amIRoot) + vote(serverListener, serverConfig, trans, address, objectStorage, amIRoot) } -func processFake(serverListener net.Listener, serverConfig Config, mess Message, amIRoot bool) { - 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!") +// 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, serverConfig Config, trans Transaction, parentAddress string, objectStorage Blob, amIRoot bool) AckTransaction { + var ack AckTransaction + ack.Id = "" + ack.TotalNodes = 0 + ack.AmountOfCorrectNode = 0 + + var transactionToRate Transaction + + database := ObjectStorageAPI.ReadDatabaseFromBlobStorage(objectStorage) + for _, transactionInDatabase := range database { + if transactionInDatabase.Id == trans.Id { + ack.Id = trans.Id + transactionToRate = transactionInDatabase + } + } + + if ack.Id == "" { + fmt.Println("Error retrieving the transaction") + return ack + } + + ack = utilities.CompareTransactions(transactionToRate, trans, ack) + + var newAck AckTransaction + + reach := false + count := 0 + + limit := len(serverConfig.Neighbours) + if amIRoot { + reach = true + go Sender.SendVoteToAllNeighbours(serverConfig, 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 process-manage-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 Sender.SendVoteToAllNeighbours(serverConfig, trans, parentAddress) + } + } + fmt.Println("***********************************") + fmt.Println("Transaction has been rated") + fmt.Println("Node correct : ", ack.AmountOfCorrectNode, "out of ", ack.TotalNodes) + fmt.Println("***********************************") + + if !amIRoot { + address := parentAddress + for _, neighbour := range serverConfig.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 Sender.SendAckToNeighbour(serverConfig, ack, address) } + return ack } func ProcessClient(conn net.Conn, server net.Listener, objectStorage Blob, serverConfig Config, amIRoot bool, mutex *sync.Mutex) { @@ -149,13 +241,10 @@ func ProcessClient(conn net.Conn, server net.Listener, objectStorage Blob, serve processTransaction(server, serverConfig, objectStorage, mess, amIRoot) } else if mess.MessageType == "rate" { fmt.Println("Received a rate") - processRate() - } else if mess.MessageType == "fake" { - fmt.Println("Received a fake transaction") - processFake() + processRate(conn, server, serverConfig, objectStorage, mess, amIRoot) } else if mess.MessageType == "list" { fmt.Println("Received an order to list all transactions") - listAllTransactionsToClient(conn) + listAllTransactionsToClient(conn, objectStorage) } else { fmt.Println("Unknown message type") } diff --git a/app/sender/sender.go b/app/sender/sender.go index 59e4c83976f1c689bb37a77de4f1be341407d673..ad815d3e2b52d8608d26348384d17356cf5b64e7 100644 --- a/app/sender/sender.go +++ b/app/sender/sender.go @@ -117,3 +117,29 @@ func SendTransactionToAllNeighbours(config Config, trans Transaction) { SendTransactionToNeighbour(config, trans, ip, port) } } + +func SendFakeTransactionToNeighbour(trans Transaction, address string) { + fmt.Println() + fmt.Println("Trying to connect to ", address) + + conn, err := net.Dial("tcp", 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") +} diff --git a/app/utilities/utilities.go b/app/utilities/utilities.go index dc3147a5a0180e949445638838f0fabb96153d22..0958407346beb53d1ba608ca47372fd2a568cb6e 100644 --- a/app/utilities/utilities.go +++ b/app/utilities/utilities.go @@ -53,3 +53,33 @@ func PrintingDatabaseToConsole(database Database) { fmt.Println("--------------------") } } + +func CompareTransactions(trans1 Transaction, trans2 Transaction, ack AckTransaction) AckTransaction { + newAck := AckTransaction{ + Id: ack.Id, + AmountOfCorrectNode: ack.AmountOfCorrectNode, + TotalNodes: ack.TotalNodes, + } + + if trans1.Id != trans2.Id { + return newAck + } + + areBothTransactionEquals := true + if trans1.Receiver != trans2.Receiver { + areBothTransactionEquals = false + } + if trans1.Sender != trans2.Sender { + areBothTransactionEquals = false + } + if trans1.Amount != trans2.Amount { + areBothTransactionEquals = false + } + + if areBothTransactionEquals { + newAck.AmountOfCorrectNode += 1 + } + newAck.TotalNodes += 1 + + return newAck +}