diff --git a/global/client/client b/global/client/client new file mode 100755 index 0000000000000000000000000000000000000000..d23c565b1ee0655215bd60f52f633f43090d0742 Binary files /dev/null and b/global/client/client differ diff --git a/global/client/client.go b/global/client/client.go new file mode 100644 index 0000000000000000000000000000000000000000..52ed843c6c0baa71ea4d51b0e8bfb3399781d0a6 --- /dev/null +++ b/global/client/client.go @@ -0,0 +1,87 @@ +package main + +import ( + "encoding/json" + "fmt" + types "local/helper" + "net" + "os" + "strconv" +) + +const ( + ServerPort = "8017" + ServerType = "tcp" +) + +func main() { + args := os.Args + if len(args) < 3 { + fmt.Printf("Usage: %s <server> <action>", args[0]) + os.Exit(1) + } + + serverHost := args[1] + action, _ := strconv.Atoi(args[2]) + var id int + var from string + var to string + var amount int + var m types.Message + + switch action { + case types.MCreateTransaction: + if len(args) != 7 { + fmt.Printf("Usage: %s %s %d <id> <from> <to> <amount>\n", args[0], serverHost, action) + os.Exit(1) + } + id, _ = strconv.Atoi(args[3]) + from = args[4] + to = args[5] + amount, _ = strconv.Atoi(args[6]) + + t := types.Transaction{ + Id: id, + From: from, + To: to, + Amount: amount, + } + m = types.Message{ + Action: types.MCreateTransaction, + Transaction: t, + Proactive: serverHost, + } + sendMessage(serverHost, m) + break + case types.MListTransactions: + m = types.Message{ + Action: types.MListTransactions, + Proactive: serverHost, + } + sendMessage(serverHost, m) + break + default: + break + } +} + +func sendMessage(serverHost string, message types.Message) { + + data, err := json.Marshal(message) + if err != nil { + panic(err) + } + connection, err := net.Dial(ServerType, serverHost+":"+ServerPort) + if err != nil { + panic(err) + } + _, err = connection.Write(data) + buffer := make([]byte, 2048) + + mLen, err := connection.Read(buffer) + if err != nil { + panic(err) + } + fmt.Println("Received: ", string(buffer[:mLen])) + defer connection.Close() +} diff --git a/global/go.mod b/global/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..b6307071dd46e3338ca7b29cbe022c1e5a6134b6 --- /dev/null +++ b/global/go.mod @@ -0,0 +1,16 @@ +module local + +go 1.18 + +require ( + github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1 + github.com/joho/godotenv v1.4.0 + gopkg.in/yaml.v2 v2.4.0 +) + +require ( + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 // indirect + golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect + golang.org/x/text v0.3.7 // indirect +) diff --git a/global/go.sum b/global/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..f81b95cb278f3bbeca61ebce1f41c416c5f5e7e2 --- /dev/null +++ b/global/go.sum @@ -0,0 +1,29 @@ +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8Jgil9UUZtMvxhEFqWo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 h1:QkAcEIAKbNL4KoFr4SathZPhDhF4mVwpBMFlYjyAqy8= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1 h1:XUNQ4mw+zJmaA2KXzP9JlQiecy1SI+Eog7xVkPiqIbg= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.1/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1 h1:BMTdr+ib5ljLa9MxTJK8x/Ds0MbBb4MfuW5BL0zMJnI= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.5.1/go.mod h1:c6WvOhtmjNUWbLfOG1qxM/q0SPvQNSVJvolm+C52dIU= +github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 h1:BWe8a+f/t+7KY7zH2mqygeUD0t8hNFXe08p1Pb3/jKE= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/dnaeon/go-vcr v1.1.0 h1:ReYa/UBrRyQdant9B4fNHGoCNKw6qh6P0fsdGmZpR7c= +github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg= +github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4 h1:Qj1ukM4GlMWXNdMBuXcXfz/Kw9s1qm0CLY32QxuSImI= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= +golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88 h1:Tgea0cVUD0ivh5ADBX4WwuI12DUd2to3nCYe2eayMIw= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= diff --git a/global/helper/azure_handler.go b/global/helper/azure_handler.go new file mode 100644 index 0000000000000000000000000000000000000000..bd3d5504bda3d67e006611c04556c9694bdc0778 --- /dev/null +++ b/global/helper/azure_handler.go @@ -0,0 +1,55 @@ +package helper + +import ( + "bytes" + "context" + "fmt" + "github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" + "io" +) + +var client *azblob.Client +var err error +var ctx context.Context + +func ConnectToStorage(connectionString string) { + client, err = azblob.NewClientFromConnectionString(connectionString, nil) + if err != nil { + panic(err) + } + ctx = context.Background() +} + +func ListBlobsOnContainer(containerName string, options *azblob.ListBlobsFlatOptions) { + fmt.Printf("Listing blobs on container [%s] ...\n", containerName) + pager := client.NewListBlobsFlatPager(containerName, options) + for pager.More() { + resp, err := pager.NextPage(ctx) + if err != nil { + panic(err) + } + for _, _blob := range resp.Segment.BlobItems { + fmt.Printf("%s\n", *_blob.Name) + } + } +} + +func ReadFromStorage(containerName string, blobName string) []byte { + resp, err := client.DownloadStream(ctx, containerName, blobName, nil) + if err != nil { + panic(err) + } + blobData, err := io.ReadAll(resp.Body) + if err != nil { + panic(err) + } + return blobData +} + +func WriteToStorage(containerName string, blobName string, data []byte) { + blobContentReader := bytes.NewReader(data) + _, err := client.UploadStream(ctx, containerName, blobName, blobContentReader, nil) + if err != nil { + panic(err) + } +} diff --git a/global/helper/types.go b/global/helper/types.go new file mode 100644 index 0000000000000000000000000000000000000000..3f9750b877d79678cf03d23d486701f7b83a686a --- /dev/null +++ b/global/helper/types.go @@ -0,0 +1,50 @@ +package helper + +import "fmt" + +type Transaction struct { + Id int `json:"id"` + From string `json:"from"` + To string `json:"to"` + Amount int `json:"amount"` +} + +type Node struct { + Id int `json:"id"` + Address string `json:"address"` + EdgeWeight int `json:"edge_weight"` +} + +type CurrentNode struct { + Address string + Neighbours []Node + Reached bool + Count int + TotalVotes int + CorrectVotes int + Container string +} + +const ( + MCreateTransaction = iota + MListTransactions = iota +) + +type Message struct { + Action int `json:"action"` + Transaction Transaction `json:"transaction"` + Proactive string `json:"proactive"` + Transactions []Transaction `json:"transactions"` +} + +func (m Message) String() string { + return fmt.Sprintf("Message{action=%d,transaction=%s,proactive=%s}", m.Action, m.Transaction.String(), m.Proactive) +} + +func (n Node) String() string { + return fmt.Sprintf("Node{id=%d,address=%s}", n.Id, n.Address) +} + +func (t Transaction) String() string { + return fmt.Sprintf("Transaction{id=%d,from=%s,to=%s,amount=%d}", t.Id, t.From, t.To, t.Amount) +} diff --git a/global/neighbour-1.yaml b/global/neighbour-1.yaml new file mode 100644 index 0000000000000000000000000000000000000000..fa02d144e588215916cae75b4311f6d1724de78e --- /dev/null +++ b/global/neighbour-1.yaml @@ -0,0 +1,10 @@ +id: 1 +address: "127.0.0.11" +neighbours: + - id: 2 + address: "127.0.0.12" + edge_weight: 0 + + - id: 3 + address: "127.0.0.13" + edge_weight: 0 \ No newline at end of file diff --git a/global/neighbour-2.yaml b/global/neighbour-2.yaml new file mode 100644 index 0000000000000000000000000000000000000000..b4131ad0485ad00bf39179d0d0250a23ac2d9a4e --- /dev/null +++ b/global/neighbour-2.yaml @@ -0,0 +1,10 @@ +id: 2 +address: "127.0.0.12" +neighbours: + - id: 1 + address: "127.0.0.11" + edge_weight: 0 + + - id: 6 + address: "127.0.0.16" + edge_weight: 0 \ No newline at end of file diff --git a/global/neighbour-3.yaml b/global/neighbour-3.yaml new file mode 100644 index 0000000000000000000000000000000000000000..07763a7193af0ba8a19b0e4f12aa1b4e848475fa --- /dev/null +++ b/global/neighbour-3.yaml @@ -0,0 +1,10 @@ +id: 3 +address: "127.0.0.13" +neighbours: + - id: 1 + address: "127.0.0.11" + edge_weight: 0 + + - id: 4 + address: "127.0.0.14" + edge_weight: 0 \ No newline at end of file diff --git a/global/neighbour-4.yaml b/global/neighbour-4.yaml new file mode 100644 index 0000000000000000000000000000000000000000..6af3c01402c2a974c1c65170559a7d8afcaf847b --- /dev/null +++ b/global/neighbour-4.yaml @@ -0,0 +1,10 @@ +id: 4 +address: "127.0.0.14" +neighbours: + - id: 3 + address: "127.0.0.13" + edge_weight: 0 + + - id: 5 + address: "127.0.0.15" + edge_weight: 0 \ No newline at end of file diff --git a/global/neighbour-5.yaml b/global/neighbour-5.yaml new file mode 100644 index 0000000000000000000000000000000000000000..e696a9131ed962dcc0815b861fc5fd58121c7c3d --- /dev/null +++ b/global/neighbour-5.yaml @@ -0,0 +1,10 @@ +id: 5 +address: "127.0.0.15" +neighbours: + - id: 4 + address: "127.0.0.14" + edge_weight: 0 + + - id: 6 + address: "127.0.0.16" + edge_weight: 0 \ No newline at end of file diff --git a/global/neighbour-6.yaml b/global/neighbour-6.yaml new file mode 100644 index 0000000000000000000000000000000000000000..c51edd45254d4473d2a86d137f8d6d3886eda0c6 --- /dev/null +++ b/global/neighbour-6.yaml @@ -0,0 +1,10 @@ +id: 6 +address: "127.0.0.16" +neighbours: + - id: 2 + address: "127.0.0.12" + edge_weight: 0 + + - id: 5 + address: "127.0.0.15" + edge_weight: 0 \ No newline at end of file diff --git a/global/neighbour-x.yaml b/global/neighbour-x.yaml new file mode 100644 index 0000000000000000000000000000000000000000..229fb9f0329d944e6e268e5b9d9a9b9f45da8d21 --- /dev/null +++ b/global/neighbour-x.yaml @@ -0,0 +1,10 @@ +id: x +address: "127.0.0.x" +neighbours: + - id: x + address: "127.0.0.x" + edge_weight: 0 + + - id: x + address: "127.0.0.x" + edge_weight: 0 \ No newline at end of file diff --git a/global/server/.env-sample b/global/server/.env-sample new file mode 100644 index 0000000000000000000000000000000000000000..30c297d8a055d7f685203f716b3d74e70a985d85 --- /dev/null +++ b/global/server/.env-sample @@ -0,0 +1,2 @@ +AZ_ACCOUNT_ACCESS_KEY= +AZ_STORAGE_BLOB_URL= \ No newline at end of file diff --git a/global/server/server b/global/server/server new file mode 100755 index 0000000000000000000000000000000000000000..b38d1bb3d9694f013c8aa73138e8ca23893c507b Binary files /dev/null and b/global/server/server differ diff --git a/global/server/server.go b/global/server/server.go new file mode 100644 index 0000000000000000000000000000000000000000..a7521da15f11b7a6535b091d3b936e92335222f7 --- /dev/null +++ b/global/server/server.go @@ -0,0 +1,210 @@ +package main + +import ( + "encoding/json" + "fmt" + dotenv "github.com/joho/godotenv" + "gopkg.in/yaml.v2" + helper "local/helper" + "net" + "os" +) + +type Config struct { + Id int `yaml:"id"` + Address string `yaml:"address"` + Neighbours []helper.Node `yaml:"neighbours"` +} + +var transactions []helper.Transaction + +var config Config + +var node helper.CurrentNode + +const ( + ServerPort = "8017" + ServerType = "tcp" + TransactionsFileName = "transactions.json" +) + +var server net.Listener +var proactive string + +func main() { + dotenv.Load() + + args := os.Args + yaml := fmt.Sprintf("../neighbour-%s.yaml", args[1]) + config := parseYAML(yaml) + initNodeFromConfig(config) + connectionString := os.Getenv("AZ_STORAGE_CONNECTION_STRING") + helper.ConnectToStorage(connectionString) + + fmt.Printf("Loaded yaml file: %s\n", yaml) + + fmt.Println("Server running...") + var err error + server, err = net.Listen(ServerType, config.Address+":"+ServerPort) + if err != nil { + fmt.Println("Error listening:", err.Error()) + os.Exit(1) + } + defer server.Close() + fmt.Println("Listening on " + config.Address + ":" + ServerPort) + fmt.Println("Waiting for client...") + for { + connection, err := server.Accept() + if err != nil { + fmt.Println("Error accepting: ", err.Error()) + os.Exit(1) + } + fmt.Println("client connected: \n" + connection.RemoteAddr().String()) + go processClient(connection) + } +} + +func initNodeFromConfig(config Config) { + node = helper.CurrentNode{ + Address: config.Address, + Neighbours: config.Neighbours, + Reached: false, + Count: 0, + TotalVotes: 0, + CorrectVotes: 0, + Container: fmt.Sprintf("container-%d", config.Id), + } +} + +func processClient(connection net.Conn) { + buffer := make([]byte, 2048) + mlen, err := connection.Read(buffer) + //receive incoming message + if err != nil { + fmt.Println("Error reading:", err.Error()) + } + + var m helper.Message + err = json.Unmarshal(buffer[:mlen], &m) + if err != nil { + panic(err) + } + proactive = m.Proactive + fmt.Printf("%s\n", m.String()) + + switch m.Action { + case helper.MCreateTransaction: + createTransaction(connection, m.Transaction) + break + case helper.MListTransactions: + listTransactions(connection) + break + default: + break + } + + //send outgoing message + //_, err = connection.Write([]byte("Thanks! Got your message:" + string(buffer[:mlen]))) + connection.Close() +} + +func parseYAML(file string) Config { + yamlFile, err := os.ReadFile(file) + if err != nil { + panic(err) + } + err = yaml.Unmarshal(yamlFile, &config) + if err != nil { + panic(err) + } + return config +} + +func createTransaction(connection net.Conn, t helper.Transaction) { + if !isTransactionKnown(t) { + AppendTransactionToStorage(node.Container, TransactionsFileName, t) + broadcastTransaction(connection, t) + } +} + +func AppendTransactionToStorage(container string, name string, t helper.Transaction) { + //read transactions.json from object storage + //parse json into arrays + //append to array + //parse array into json + //store on object storage + data := helper.ReadFromStorage(container, TransactionsFileName) + var transactions []helper.Transaction + err := json.Unmarshal(data, &transactions) + if err != nil { + panic(err) + } + transactions = append(transactions, t) + data, err = json.Marshal(transactions) + if err != nil { + panic(err) + } + helper.WriteToStorage(container, TransactionsFileName, data) +} + +func listTransactions(connection net.Conn) { + m := helper.Message{ + Action: helper.MListTransactions, + Proactive: proactive, + Transactions: transactions, + } + data, err := json.Marshal(m) + if err != nil { + panic(err) + } + connection.Write(data) + + defer connection.Close() +} + +func broadcastTransaction(connection net.Conn, t helper.Transaction) { + fmt.Printf("Starting transaction broadcast\n") + + sendTransactionToNeighbours(t) + + connection.Write([]byte("Transaction broadcasted to all nodes on the network.\n")) + defer connection.Close() + +} + +func sendTransactionToNeighbours(t helper.Transaction) { + for _, n := range node.Neighbours { + sendTransactionToServer(t, n.Address) + } +} + +func sendTransactionToServer(t helper.Transaction, address string) { + fmt.Printf("Sending transaction %s to server [%s]\n", t.String(), address) + connection, err := net.Dial(ServerType, fmt.Sprintf("%s:%s", address, ServerPort)) + if err != nil { + panic(err) + } + + m := helper.Message{ + Action: helper.MCreateTransaction, + Transaction: t, + Proactive: proactive, + } + data, err := json.Marshal(m) + if err != nil { + panic(err) + } + _, err = connection.Write(data) + defer connection.Close() + +} + +func isTransactionKnown(t helper.Transaction) bool { + for _, transaction := range transactions { + if transaction.Id == t.Id { + fmt.Printf("Transaction already exists\n") + return true + } + } + return false +} diff --git a/global/server/test.json b/global/server/test.json new file mode 100644 index 0000000000000000000000000000000000000000..928198104245dc2934591a4f28edd762155a7553 --- /dev/null +++ b/global/server/test.json @@ -0,0 +1,16 @@ +{ + "transactions": [ + { + "id": 1, + "from": "Peiry", + "to": "Troller", + "amount": 6000 + }, + { + "id": 2, + "from": "Birner", + "to": "Peiry", + "amount": 2000 + } + ] +} \ No newline at end of file diff --git a/global/transactions.json b/global/transactions.json new file mode 100644 index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc --- /dev/null +++ b/global/transactions.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/local/client/client b/local/client/client index b46252df67a8891fb296573d94545c3e76d80fb1..148abe574bb02ca79291198a6ee49c98f5661939 100755 Binary files a/local/client/client and b/local/client/client differ diff --git a/local/server/server b/local/server/server index 25ed6495d57d4b136f22180d8b68eec849929d32..d3255cdb2108c23028987174f3dbbcfaef17c0be 100755 Binary files a/local/server/server and b/local/server/server differ