/
message.go
188 lines (148 loc) · 6.22 KB
/
message.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package blockchain
import (
"encoding/json"
"errors"
"fmt"
"log"
"net"
"time"
)
// Message is the struct that is marshalled/demarshalled between peers to communicate
type Message struct {
From PeerAddress `json:"from"`
Command string `json:"command"`
Data Data `json:"data,omitempty"`
}
// UnmarshalJSON is a custom JSON unmarshaller
func (m *Message) UnmarshalJSON(bytes []byte) error {
var result map[string]interface{}
err := json.Unmarshal(bytes, &result)
if err != nil {
log.Printf("Error unmarshalling message: %v\n", err)
return err
}
// fmt.Printf("DEBUG - Unmarshal result is: %+v\n", result)
// Unmarshal the data of the Peer who sent the message
from := result["from"].(map[string]interface{})
address := from["address"].(map[string]interface{})
ip := net.ParseIP(address["IP"].(string))
port := int(address["Port"].(float64))
newAddress := net.UDPAddr{IP: ip, Port: port}
lastMessageTime := from["lastMessageTime"].(string)
parsedLastMessageTime, err := time.Parse(time.RFC3339, lastMessageTime)
if err != nil {
log.Println("Failed to parse time:", err.Error())
}
newPeer := PeerAddress{Address: newAddress, LastMessageTime: parsedLastMessageTime}
// Umarshall the command
command := result["command"].(string)
// Unmarshal the data
data := result["data"]
var dataStruct Data
if data != nil {
dataObject := data.(map[string]interface{})
if val, ok := dataObject["from"]; ok {
// Then the data is a transaction, so unmarshal into a Transaction struct
from := val.(string)
to := dataObject["to"].(string)
amount := int(dataObject["amount"].(float64))
signature := dataObject["signature"].(string)
dataStruct = Transaction{From: from, To: to, Amount: amount, Signature: signature}
} else if val, ok := dataObject["chainCopy"]; ok {
// Then the data is a chain copy, so unmarshal into a ChainCopy struct
list := val.([]interface{})
newChain := Chain{ChainCopy: []Block{}}
//Umarshal the chain
for _, block := range list {
blockMap := block.(map[string]interface{})
dataMap := blockMap["Data"].(map[string]interface{})
// We can assume that the Data will be of type Transaction, for now
from := dataMap["from"].(string)
to := dataMap["to"].(string)
amount := int(dataMap["amount"].(float64))
signature := dataMap["signature"].(string)
newTransaction := Transaction{From: from, To: to, Amount: amount, Signature: signature}
// Umarshal the rest of the block
index := int(blockMap["Index"].(float64))
timestamp := blockMap["Timestamp"].(string)
prevHash := blockMap["PrevHash"].(string)
hash := blockMap["Hash"].(string)
nonce := int(blockMap["Nonce"].(float64))
newBlock := Block{Data: newTransaction, Index: index, Timestamp: timestamp, PrevHash: prevHash, Hash: hash, Nonce: nonce}
newChain.ChainCopy = append(newChain.ChainCopy, newBlock)
}
dataStruct = newChain
} else if val, ok := dataObject["list"]; ok {
// Then the data is a list of peer chains, so unmarshal into a PeerChains struct
if val != nil {
newList := PeerChains{List: [][]Block{}}
list := val.([]interface{})
for _, chain := range list {
tempList := []Block{}
//Umarshal the chain
for _, block := range chain.([]interface{}) {
blockMap := block.(map[string]interface{})
dataMap := blockMap["Data"].(map[string]interface{})
// We can assume that the Data will be of type Transaction, for now
from := dataMap["from"].(string)
to := dataMap["to"].(string)
amount := int(dataMap["amount"].(float64))
signature := dataMap["signature"].(string)
newTransaction := Transaction{From: from, To: to, Amount: amount, Signature: signature}
// Umarshal the rest of the block
index := int(blockMap["Index"].(float64))
timestamp := blockMap["Timestamp"].(string)
prevHash := blockMap["PrevHash"].(string)
hash := blockMap["Hash"].(string)
nonce := int(blockMap["Nonce"].(float64))
newBlock := Block{Data: newTransaction, Index: index, Timestamp: timestamp, PrevHash: prevHash, Hash: hash, Nonce: nonce}
tempList = append(tempList, newBlock)
}
newList.List = append(newList.List, tempList)
}
dataStruct = newList
} else {
// Else if temp is nil, the Middlware sent an empty list, thus there are no existing peer chains, return
// an empty PeerChains struct
fmt.Println("No existing peer chains.")
dataStruct = PeerChains{}
}
} else if val, ok := dataObject["stake"]; ok {
// Then the data is a lottery entry, so unmarshal into a LotteryEntry struct
stake := int(val.(float64))
dataStruct = LotteryEntry{Stake: stake, Peer: newPeer}
} else if val, ok := dataObject["block"]; ok {
// Then the data is a candidate, so unmarshal into a CandidateBlock struct
blockMap := val.(map[string]interface{})
dataMap := blockMap["Data"].(map[string]interface{})
// We can assume that the Data will be of type Transaction, for now
from := dataMap["from"].(string)
to := dataMap["to"].(string)
amount := int(dataMap["amount"].(float64))
signature := dataMap["signature"].(string)
newTransaction := Transaction{From: from, To: to, Amount: amount, Signature: signature}
index := int(blockMap["Index"].(float64))
timestamp := blockMap["Timestamp"].(string)
prevHash := blockMap["PrevHash"].(string)
hash := blockMap["Hash"].(string)
nonce := int(blockMap["Nonce"].(float64))
candidateBlock := Block{Data: newTransaction, Index: index, Timestamp: timestamp, PrevHash: prevHash, Hash: hash, Nonce: nonce}
dataStruct = CandidateBlock{Block: candidateBlock, Miner: newPeer}
} else if val, ok := dataObject["x"]; ok {
// Then the data is a lottery entry, so unmarshal into a LotteryEntry struct
x := val.(string)
y := dataObject["y"].(string)
dataStruct = PublicKey{X: x, Y: y}
} else {
// Else the data is of an unsupported type
err := errors.New("error unmarshalling Data object: unsupported type")
return err
}
}
m.Command = command
m.From = newPeer
m.Data = dataStruct
return nil
}
// TODO: Lot's of duplicate code here.
// Create helper functions that can be called and reused in different data cases. Including unmarshalling a transaction, a block, and a chain