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 package main
31
32 import (
33 "crypto/sha256"
34 "encoding/hex"
35 "encoding/json"
36 "io"
37 "log"
38 "net/http"
39
40 "strconv"
41 "sync"
42 "time"
43 "github.com/davecgh/go-spew/spew"
44 "github.com/gorilla/mux"
45
46 )
47
48
49
50
51
52
53
54 type Block struct {
55 Index int
56 Timestamp string
57 BPM int
58 Hash string
59 PrevHash string
60 }
61
62
63 var Blockchain []Block
64
65
66 type Message struct {
67 BPM int
68 }
69
70 var mutex = &sync.Mutex{}
71
72 func main() {
73
74
75
76
77
78
79 go func() {
80 t := time.Now()
81 genesisBlock := Block{}
82 genesisBlock = Block{0, t.String(), 0, calculateHash(genesisBlock), ""}
83 spew.Dump(genesisBlock)
84 mutex.Lock()
85
86
87
88 Blockchain = append(Blockchain, genesisBlock)
89 mutex.Unlock()
90 }()
91 log.Fatal(run())
92
93 }
94
95
96
97 func run() error {
98 mux := makeMuxRouter()
99
100 const httpPort = "2703"
101 log.Println("HTTP Server Listening on port :", httpPort)
102 s := &http.Server{
103 Addr: ":" + httpPort,
104 Handler: mux,
105 ReadTimeout: 10 * time.Second,
106 WriteTimeout: 10 * time.Second,
107 MaxHeaderBytes: 1 << 20,
108 }
109 if err := s.ListenAndServe(); err != nil {
110 return err
111 }
112 return nil
113 }
114
115
116 func makeMuxRouter() http.Handler {
117 muxRouter := mux.NewRouter()
118
119 muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
120
121 muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
122 return muxRouter
123 }
124
125
126 func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
127 bytes, err := json.MarshalIndent(Blockchain, "", " ")
128 if err != nil {
129 http.Error(w, err.Error(), http.StatusInternalServerError)
130 return
131 }
132 io.WriteString(w, string(bytes))
133 }
134
135
136 func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
137 w.Header().Set("Content-Type", "application/json")
138 var m Message
139
140 decoder := json.NewDecoder(r.Body)
141 if err := decoder.Decode(&m); err != nil {
142 respondWithJSON(w, r, http.StatusBadRequest, r.Body)
143 return
144 }
145 defer r.Body.Close()
146
147 mutex.Lock()
148 newBlock := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
149 mutex.Unlock()
150
151 if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
152 Blockchain = append(Blockchain, newBlock)
153 spew.Dump(Blockchain)
154 }
155 respondWithJSON(w, r, http.StatusCreated, newBlock)
156 }
157
158 func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
159 response, err := json.MarshalIndent(payload, "", " ")
160 if err != nil {
161 w.WriteHeader(http.StatusInternalServerError)
162 w.Write([]byte("HTTP 500: Internal Server Error"))
163 return
164 }
165 w.WriteHeader(code)
166 w.Write(response)
167 }
168
169
170 func isBlockValid(newBlock, oldBlock Block) bool {
171 if oldBlock.Index+1 != newBlock.Index {
172 return false
173 }
174 if oldBlock.Hash != newBlock.PrevHash {
175 return false
176 }
177 if calculateHash(newBlock) != newBlock.Hash {
178 return false
179 }
180 return true
181 }
182
183
184 func calculateHash(block Block) string {
185 record := strconv.Itoa(block.Index) + block.Timestamp + strconv.Itoa(block.BPM) + block.PrevHash
186 h := sha256.New()
187 h.Write([]byte(record))
188 hashed := h.Sum(nil)
189 return hex.EncodeToString(hashed)
190 }
191
192
193 func generateBlock(oldBlock Block, BPM int) Block {
194 var newBlock Block
195 t := time.Now()
196 newBlock.Index = oldBlock.Index + 1
197 newBlock.PrevHash = oldBlock.Hash
198 newBlock.Timestamp = t.String()
199 newBlock.BPM = BPM
200 newBlock.Hash = calculateHash(newBlock)
201 return newBlock
202 }
203
204