diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b29f27 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +tests diff --git a/peer/README.md b/peer/README.md index 9f577ca..678cc06 100644 --- a/peer/README.md +++ b/peer/README.md @@ -19,3 +19,53 @@ Needs the config.json file: "serverport": "3000" } ``` + + +## Peer REST API +- GET / + - Returns the peer.ID (where peer.ID = hash(peer.IP + ":" + peer.Port)) + + +- GET /peers + - Returns the peer outcomingPeersList (the peers which the peer have connection) + +```json +{ + "PeerID": "VOnL-15rFsUiCnRoyGFksKvWKcwNBRz5iarRem0Ilvo=", + "peerslist": [ + { + "id": "VOnL-15rFsUiCnRoyGFksKvWKcwNBRz5iarRem0Ilvo=", + "ip": "127.0.0.1", + "port": "3000", + "role": "server", + "conn": null + }, + { + "id": "Lk9jEP1YcOAzl51yY61GdWADNe35_g5Teh12JeguHhA=", + "ip": "127.0.0.1", + "port": "3003", + "role": "client", + "conn": {} + }, + { + "id": "xj78wuyN2_thFBsXOUXnwij4L8vualxQ9GnVRK6RS4c=", + "ip": "127.0.0.1", + "port": "3005", + "role": "client", + "conn": {} + } + ], + "date": "0001-01-01T00:00:00Z" +} +``` + +- POST /register + - Adds the address (pubK of the user) to the blockchain + + +## TODO +- When a peer connects to the network, sends his last Block, and receives the new Blocks from this last Block +- Delete the peer from the peers list when the connection is closed +- REST: + - endpoint to get if the address is in the blockchain (to verify users) +- parameters Date or LastUpdate on the structs needs to be updated values diff --git a/peer/blockchain.go b/peer/blockchain.go new file mode 100644 index 0000000..154d098 --- /dev/null +++ b/peer/blockchain.go @@ -0,0 +1,69 @@ +package main + +import ( + "errors" + "time" +) + +type Block struct { + Hash string `json:"hash"` + Height int64 `json:"height"` + Date time.Time `json:"date"` + PreviousHash string `json:"previoushash"` + NextHash string `json:"nexthash"` + Data []Address `json:"data"` + Emitter string `json:"emitter"` //the ID of the peer that has emmited the block +} + +type Blockchain struct { + GenesisBlock string `json:"genesisblock"` + LastUpdate time.Time `json:"lastupdate"` + Blocks []Block `json:"blocks"` +} + +var blockchain Blockchain + +func (bc *Blockchain) getBlockByHash(hash string) (Block, error) { + for _, block := range bc.Blocks { + if block.Hash == hash { + return block, nil + } + } + var b Block + return b, errors.New("Block Hash not found") +} + +func (bc *Blockchain) createBlock(address Address) Block { + var b Block + b.Height = int64(len(bc.Blocks)) + if len(bc.Blocks) == 0 { + b.Height = 0 + } else { + b.PreviousHash = bc.Blocks[len(bc.Blocks)-1].Hash + } + b.Date = time.Now() + b.Data = append(b.Data, address) + b.Emitter = runningPeer.ID + + b.Hash = hashBlock(b) + return b +} + +func (bc *Blockchain) blockExists(block Block) bool { + for _, b := range bc.Blocks { + if b.Hash == block.Hash { + return true + } + } + return false +} +func (bc *Blockchain) addBlock(block Block) error { + if len(bc.Blocks) > 0 { + bc.Blocks[len(bc.Blocks)-1].NextHash = block.Hash + } else { + bc.GenesisBlock = block.Hash + } + bc.Blocks = append(bc.Blocks, block) + + return nil +} diff --git a/peer/connections.go b/peer/connections.go index dbd30d6..8aeb805 100644 --- a/peer/connections.go +++ b/peer/connections.go @@ -57,7 +57,8 @@ func handleConn(conn net.Conn, connPeer Peer) { log.Println("handling conn: " + conn.RemoteAddr().String()) //reply to the conn, send the peerList var msg Msg - msg = msg.construct("PeersList", "here my outcomingPeersList", outcomingPeersList) + msg.construct("PeersList", "here my outcomingPeersList") + msg.PeersList = outcomingPeersList msgB := msg.toBytes() _, err := conn.Write(msgB) check(err) diff --git a/peer/main.go b/peer/main.go index dd2f1e0..9cd38a3 100644 --- a/peer/main.go +++ b/peer/main.go @@ -5,7 +5,6 @@ import ( "math/rand" "net" "os" - "strconv" "time" "github.com/fatih/color" @@ -32,9 +31,10 @@ func main() { //read configuration file readConfig("config.json") - runningPeer.ID = strconv.Itoa(randInt(1, 1000)) //0 is reserved for server + //runningPeer.ID = strconv.Itoa(randInt(1, 1000)) //0 is reserved for server runningPeer.IP = config.IP runningPeer.Port = config.Port + runningPeer.ID = hashPeer(runningPeer) runningPeer.Role = "client" go runRestServer() @@ -45,7 +45,8 @@ func main() { color.Yellow("Running as p2p server") runningPeer.Role = "server" runningPeer.Port = config.ServerPort - runningPeer.ID = "0" + runningPeer.ID = hashPeer(runningPeer) + //runningPeer.ID = "0" } } thisPeerID = runningPeer.ID @@ -58,12 +59,13 @@ func main() { go acceptPeers(runningPeer) } if runningPeer.Role == "client" { - var newPeer Peer - newPeer.ID = "0" - newPeer.IP = config.ServerIP - newPeer.Port = config.ServerPort - newPeer.Role = "server" - connectToPeer(newPeer) + var serverPeer Peer + //serverPeer.ID = "0" + serverPeer.IP = config.ServerIP + serverPeer.Port = config.ServerPort + serverPeer.ID = hashPeer(serverPeer) + serverPeer.Role = "server" + connectToPeer(serverPeer) go acceptPeers(runningPeer) } diff --git a/peer/messages.go b/peer/messages.go index c947ff7..f7ed337 100644 --- a/peer/messages.go +++ b/peer/messages.go @@ -14,6 +14,7 @@ type Msg struct { Date time.Time `json:"date"` Content string `json:"content"` PeersList PeersList `json:"peerslist"` + Block Block `json:"block"` } func messageHandler(peer Peer, msg Msg) { @@ -57,18 +58,22 @@ func messageHandler(peer Peer, msg Msg) { */ printPeersList() break + case "Block": + if !blockchain.blockExists(msg.Block) { + blockchain.addBlock(msg.Block) + propagateBlock(msg.Block) + } + break default: log.Println("Msg.Type not supported") break } } -func (msg Msg) construct(msgtype string, msgcontent string, peersList PeersList) Msg { +func (msg *Msg) construct(msgtype string, msgcontent string) { msg.Type = msgtype msg.Content = msgcontent - msg.PeersList = peersList msg.Date = time.Now() - return msg } func (msg Msg) toBytes() []byte { msgS, err := json.Marshal(msg) diff --git a/peer/peersList.go b/peer/peers.go similarity index 80% rename from peer/peersList.go rename to peer/peers.go index dbd8869..82d0abd 100644 --- a/peer/peersList.go +++ b/peer/peers.go @@ -74,22 +74,16 @@ func propagatePeersList(p Peer) { if peer.ID != p.ID && p.ID != "" { color.Yellow(peer.ID + " - " + p.ID) var msg Msg - msg = msg.construct("PeersList", "here my outcomingPeersList", outcomingPeersList) + msg.construct("PeersList", "here my outcomingPeersList") + msg.PeersList = outcomingPeersList msgB := msg.toBytes() _, err := peer.Conn.Write(msgB) check(err) } else { - //to the peer that has sent the peerList, we send our ID - /* - var msg Msg - var pl PeersList - msg = msg.construct("MyID", runningPeer.ID, pl) - msgB := msg.toBytes() - _, err := p.Conn.Write(msgB) - check(err) - */ + //to the peer that has sent the peerList, we send our PeersList var msg Msg - msg = msg.construct("PeersList_Response", "here my outcomingPeersList", outcomingPeersList) + msg.construct("PeersList_Response", "here my outcomingPeersList") + msg.PeersList = outcomingPeersList msgB := msg.toBytes() _, err := peer.Conn.Write(msgB) check(err) @@ -123,3 +117,18 @@ func printPeersList() { } fmt.Println("") } + +//send the block to all the peers of the outcomingPeersList +func propagateBlock(b Block) { + //prepare the msg to send to all connected peers + var msg Msg + msg.construct("Block", "new block") + msg.Block = b + msgB := msg.toBytes() + for _, peer := range outcomingPeersList.Peers { + if peer.Conn != nil { + _, err := peer.Conn.Write(msgB) + check(err) + } + } +} diff --git a/peer/restServerConfig.go b/peer/restConfig.go similarity index 100% rename from peer/restServerConfig.go rename to peer/restConfig.go diff --git a/peer/restRoutes.go b/peer/restRoutes.go new file mode 100755 index 0000000..a01682b --- /dev/null +++ b/peer/restRoutes.go @@ -0,0 +1,67 @@ +package main + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/fatih/color" +) + +type Routes []Route + +var routes = Routes{ + Route{ + "Index", + "GET", + "/", + Index, + }, + Route{ + "GetPeers", + "GET", + "/peers", + GetPeers, + }, + Route{ + "PostUser", + "POST", + "/register", + PostUser, + }, +} + +type Address struct { + Address string `json:"address"` //the pubK of the user, to perform logins +} + +func Index(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, runningPeer.ID) +} +func GetPeers(w http.ResponseWriter, r *http.Request) { + jResp, err := json.Marshal(outcomingPeersList) + check(err) + fmt.Fprintln(w, string(jResp)) +} +func PostUser(w http.ResponseWriter, r *http.Request) { + + decoder := json.NewDecoder(r.Body) + var address Address + err := decoder.Decode(&address) + if err != nil { + panic(err) + } + defer r.Body.Close() + fmt.Println(address) + color.Blue(address.Address) + + //TODO add the verification of the address, to decide if it's accepted to create a new Block + block := blockchain.createBlock(address) + blockchain.addBlock(block) + + go propagateBlock(block) + + jResp, err := json.Marshal(blockchain) + check(err) + fmt.Fprintln(w, string(jResp)) +} diff --git a/peer/restServerRoutes.go b/peer/restServerRoutes.go deleted file mode 100755 index afcd23f..0000000 --- a/peer/restServerRoutes.go +++ /dev/null @@ -1,22 +0,0 @@ -package main - -import ( - "fmt" - "net/http" -) - -type Routes []Route - -var routes = Routes{ - Route{ - "Index", - "GET", - "/", - Index, - }, -} - -func Index(w http.ResponseWriter, r *http.Request) { - //ipFilter(w, r) - fmt.Fprintln(w, runningPeer.ID) -} diff --git a/peer/tests.sh b/peer/tests.sh new file mode 100644 index 0000000..67fbca0 --- /dev/null +++ b/peer/tests.sh @@ -0,0 +1 @@ +curl -X POST -F address="thisisasampleaddress" http://127.0.0.1:3002/register \ No newline at end of file diff --git a/peer/utils.go b/peer/utils.go index c9f62f1..5e2a16d 100644 --- a/peer/utils.go +++ b/peer/utils.go @@ -1,6 +1,9 @@ package main import ( + "crypto/sha256" + "encoding/base64" + "encoding/json" "math/rand" "net" "strings" @@ -27,3 +30,22 @@ func randInt(min int, max int) int { r := rand.Intn(max-min) + min return r } +func hashPeer(p Peer) string { + /*peerJson, err := json.Marshal(p) + check(err) + peerString := string(peerJson)*/ + peerString := p.IP + ":" + p.Port + + h := sha256.New() + h.Write([]byte(peerString)) + return base64.URLEncoding.EncodeToString(h.Sum(nil)) +} +func hashBlock(b Block) string { + blockJson, err := json.Marshal(b) + check(err) + blockString := string(blockJson) + + h := sha256.New() + h.Write([]byte(blockString)) + return base64.URLEncoding.EncodeToString(h.Sum(nil)) +}