Skip to content
Snippets Groups Projects
Select Git revision
  • 520c4f932a6a9c9fd5db09bf5b333d7845631b04
  • master default protected
2 results

main.go

Blame
  • Xavier Perret's avatar
    xavier.perret authored
    520c4f93
    History
    main.go 21.14 KiB
    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)
    	}
    }