@ -0,0 +1,3 @@ |
|||
# bc |
|||
|
|||
Own p2p network and own blockchain libraries written in Go, to develop own decentralized apps. |
@ -0,0 +1,4 @@ |
|||
- p2plib/messages.go / MessageHandler |
|||
put in a way that the messages handlers can be created from the main code (not from the package) |
|||
|
|||
- subsitute the REST functions by the messages in the MessageHandler |
@ -0,0 +1 @@ |
|||
*.data |
@ -0,0 +1,73 @@ |
|||
# Peer |
|||
|
|||
To run as a normal peer: |
|||
``` |
|||
./peer |
|||
``` |
|||
|
|||
To run as a p2p server: |
|||
``` |
|||
./peer server |
|||
``` |
|||
|
|||
Needs the config.json file: |
|||
```json |
|||
{ |
|||
"ip": "127.0.0.1", |
|||
"port": "3001", |
|||
"serverip": "127.0.0.1", |
|||
"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 --> DONE with REST petitions, maybe is better with tcp conn |
|||
- Delete the peer from the peers list when the connection is closed --> DONE |
|||
- 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 |
|||
- implement rsa encryption between peers |
|||
- store blockchain in a .data file |
@ -0,0 +1,20 @@ |
|||
package blockchainlib |
|||
|
|||
import ( |
|||
"crypto/sha256" |
|||
"encoding/base64" |
|||
"encoding/json" |
|||
"log" |
|||
) |
|||
|
|||
func HashBlock(b Block) string { |
|||
blockJson, err := json.Marshal(b) |
|||
if err != nil { |
|||
log.Println(err) |
|||
} |
|||
blockString := string(blockJson) |
|||
|
|||
h := sha256.New() |
|||
h.Write([]byte(blockString)) |
|||
return base64.URLEncoding.EncodeToString(h.Sum(nil)) |
|||
} |
@ -0,0 +1,155 @@ |
|||
package blockchainlib |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"errors" |
|||
"fmt" |
|||
"io/ioutil" |
|||
"net/http" |
|||
"time" |
|||
|
|||
"github.com/fatih/color" |
|||
) |
|||
|
|||
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 []string `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(data string) 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, data) |
|||
//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 bc.BlockExists(block) { |
|||
return errors.New("[Error adding Block]: Block already exists in the Blockchain") |
|||
} |
|||
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) |
|||
|
|||
bc.SaveToDisk() |
|||
|
|||
return nil |
|||
} |
|||
|
|||
func (bc *Blockchain) ReconstructBlockchainFromBlock(urlAPI string, h string) { |
|||
color.Yellow("reconstructing the blockchain from last block in memory") |
|||
var block Block |
|||
var err error |
|||
|
|||
block, err = bc.GetBlockByHash(h) |
|||
check(err) |
|||
|
|||
if h == "" { |
|||
//no genesis block yet
|
|||
color.Green(urlAPI + "/blocks/genesis") |
|||
res, err := http.Get(urlAPI + "/blocks/genesis") |
|||
check(err) |
|||
body, err := ioutil.ReadAll(res.Body) |
|||
check(err) |
|||
err = json.Unmarshal(body, &block) |
|||
check(err) |
|||
color.Yellow("[New Block]: " + block.Hash) |
|||
err = bc.AddBlock(block) |
|||
check(err) |
|||
} else { |
|||
block.NextHash = h |
|||
} |
|||
|
|||
for block.NextHash != "" && block.Hash != "" { |
|||
res, err := http.Get(urlAPI + "/blocks/next/" + block.Hash) |
|||
check(err) |
|||
body, err := ioutil.ReadAll(res.Body) |
|||
check(err) |
|||
err = json.Unmarshal(body, &block) |
|||
check(err) |
|||
if block.Hash != "" { |
|||
color.Yellow("[New Block]: " + block.Hash) |
|||
err = bc.AddBlock(block) |
|||
check(err) |
|||
} |
|||
} |
|||
bc.Print() |
|||
} |
|||
|
|||
func (bc *Blockchain) Print() { |
|||
color.Green("Printing Blockchain stored in memory") |
|||
color.Green("Genesis Block: " + bc.GenesisBlock) |
|||
for _, b := range bc.Blocks { |
|||
color.Green("Block height:") |
|||
fmt.Println(b.Height) |
|||
color.Green("Hash: " + b.Hash) |
|||
color.Green("Date: " + b.Date.String()) |
|||
color.Green("---") |
|||
} |
|||
} |
|||
|
|||
func (bc *Blockchain) ReadFromDisk() error { |
|||
file, err := ioutil.ReadFile("blockchain.data") |
|||
if err != nil { |
|||
return err |
|||
} |
|||
content := string(file) |
|||
json.Unmarshal([]byte(content), &bc) |
|||
return nil |
|||
} |
|||
|
|||
func (bc *Blockchain) SaveToDisk() error { |
|||
bytesBlockchain, err := json.Marshal(bc) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
err = ioutil.WriteFile("blockchain.data", bytesBlockchain, 0644) |
|||
if err != nil { |
|||
return err |
|||
} |
|||
return nil |
|||
} |
@ -0,0 +1,15 @@ |
|||
package blockchainlib |
|||
|
|||
import ( |
|||
"log" |
|||
"runtime" |
|||
) |
|||
|
|||
func check(err error) { |
|||
if err != nil { |
|||
_, fn, line, _ := runtime.Caller(1) |
|||
log.Println(line) |
|||
log.Println(fn) |
|||
log.Println(err) |
|||
} |
|||
} |
@ -0,0 +1,6 @@ |
|||
{ |
|||
"ip": "127.0.0.1", |
|||
"serverip": "127.0.0.1", |
|||
"serverport": "3000", |
|||
"serverrestport": "3002" |
|||
} |
@ -0,0 +1,15 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"log" |
|||
"runtime" |
|||
) |
|||
|
|||
func check(err error) { |
|||
if err != nil { |
|||
_, fn, line, _ := runtime.Caller(1) |
|||
log.Println(line) |
|||
log.Println(fn) |
|||
log.Println(err) |
|||
} |
|||
} |
@ -0,0 +1,24 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"io" |
|||
"log" |
|||
"os" |
|||
"strings" |
|||
"time" |
|||
) |
|||
|
|||
func savelog() { |
|||
timeS := time.Now().String() |
|||
_ = os.Mkdir("logs", os.ModePerm) |
|||
//next 3 lines are to avoid windows filesystem errors
|
|||
timeS = strings.Replace(timeS, " ", "_", -1) |
|||
timeS = strings.Replace(timeS, ".", "-", -1) |
|||
timeS = strings.Replace(timeS, ":", "-", -1) |
|||
logFile, err := os.OpenFile("logs/log-"+timeS+".log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0666) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
mw := io.MultiWriter(os.Stdout, logFile) |
|||
log.SetOutput(mw) |
|||
} |
@ -0,0 +1,48 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"fmt" |
|||
"os" |
|||
"time" |
|||
|
|||
blockchainlib "./blockchainlib" |
|||
p2plib "./p2plib" |
|||
"github.com/fatih/color" |
|||
) |
|||
|
|||
var tp p2plib.ThisPeer |
|||
var blockchain blockchainlib.Blockchain |
|||
|
|||
func main() { |
|||
|
|||
if len(os.Args) < 3 { |
|||
color.Red("need to call:") |
|||
color.Red("./peer client 3001 3002") |
|||
os.Exit(3) |
|||
} |
|||
|
|||
color.Blue("Starting Peer") |
|||
//read configuration file
|
|||
readConfig("config.json") |
|||
|
|||
//read the stored blockchain
|
|||
err := blockchain.ReadFromDisk() |
|||
check(err) |
|||
blockchain.Print() |
|||
|
|||
tp = p2plib.InitializePeer(os.Args[1], "127.0.0.1", |
|||
os.Args[2], os.Args[3], config.ServerIP, config.ServerPort) |
|||
|
|||
if tp.RunningPeer.Role == "client" { |
|||
color.Red("http://" + config.IP + ":" + config.ServerRESTPort) |
|||
fmt.Println(blockchain.GenesisBlock) |
|||
blockchain.ReconstructBlockchainFromBlock("http://"+config.IP+":"+config.ServerRESTPort, blockchain.GenesisBlock) |
|||
} |
|||
color.Blue("initialized") |
|||
go runRestServer() |
|||
|
|||
fmt.Println(tp.Running) |
|||
for tp.Running { |
|||
time.Sleep(1000 * time.Millisecond) |
|||
} |
|||
} |
@ -0,0 +1,81 @@ |
|||
package p2plib |
|||
|
|||
import ( |
|||
"bufio" |
|||
"fmt" |
|||
"log" |
|||
"net" |
|||
|
|||
"github.com/fatih/color" |
|||
) |
|||
|
|||
func (tp *ThisPeer) AcceptPeers(peer Peer) { |
|||
fmt.Println("accepting peers at: " + peer.Port) |
|||
l, err := net.Listen("tcp", peer.IP+":"+peer.Port) |
|||
if err != nil { |
|||
log.Println("Error accepting peers. Listening port: " + peer.Port) |
|||
tp.Running = false |
|||
} |
|||
for tp.Running { |
|||
conn, err := l.Accept() |
|||
if err != nil { |
|||
log.Println("Error accepting peers. Error accepting connection") |
|||
tp.Running = false |
|||
} |
|||
var newPeer Peer |
|||
newPeer.IP = GetIPFromConn(conn) |
|||
newPeer.Port = GetPortFromConn(conn) |
|||
newPeer.Conn = conn |
|||
globalTP.PeersConnections.Incoming = AppendPeerIfNoExist(globalTP.PeersConnections.Incoming, newPeer) |
|||
go HandleConn(conn, newPeer) |
|||
} |
|||
} |
|||
func ConnectToPeer(peer Peer) { |
|||
color.Green("connecting to new peer") |
|||
log.Println("Connecting to new peer: " + peer.IP + ":" + peer.Port) |
|||
conn, err := net.Dial("tcp", peer.IP+":"+peer.Port) |
|||
if err != nil { |
|||
log.Println("Error connecting to: " + peer.IP + ":" + peer.Port) |
|||
return |
|||
} |
|||
peer.Conn = conn |
|||
globalTP.PeersConnections.Outcoming = AppendPeerIfNoExist(globalTP.PeersConnections.Outcoming, peer) |
|||
go HandleConn(conn, peer) |
|||
} |
|||
func HandleConn(conn net.Conn, connPeer Peer) { |
|||
connRunning := true |
|||
log.Println("handling conn: " + conn.RemoteAddr().String()) |
|||
//reply to the conn, send the peerList
|
|||
var msg Msg |
|||
msg.Construct("PeersList", "here my outcomingPeersList") |
|||
msg.PeersList = globalTP.PeersConnections.Outcoming |
|||
msgB := msg.ToBytes() |
|||
_, err := conn.Write(msgB) |
|||
if err != nil { |
|||
log.Println(err) |
|||
} |
|||
|
|||
for connRunning { |
|||
/* |
|||
buffer := make([]byte, 1024) |
|||
bytesRead, err := conn.Read(buffer) |
|||
*/ |
|||
newmsg, err := bufio.NewReader(conn).ReadString('\n') |
|||
if err != nil { |
|||
log.Println(err) |
|||
connRunning = false |
|||
} else { |
|||
var msg Msg |
|||
//msg = msg.createFromBytes([]byte(string(buffer[0:bytesRead])))
|
|||
msg = msg.CreateFromBytes([]byte(newmsg)) |
|||
MessageHandler(connPeer, msg) |
|||
} |
|||
} |
|||
//TODO add that if the peer closed is the p2p server, show a warning message at the peer
|
|||
log.Println("Peer: " + conn.RemoteAddr().String() + " connection closed") |
|||
conn.Close() |
|||
//TODO delete the peer from the outcomingPeersList --> DONE
|
|||
DeletePeerFromPeersList(connPeer, &globalTP.PeersConnections.Outcoming) |
|||
/*color.Yellow("peer deleted, current peerList:") |
|||
PrintPeersList()*/ |
|||
} |
@ -0,0 +1,15 @@ |
|||
package p2plib |
|||
|
|||
import ( |
|||
"log" |
|||
"runtime" |
|||
) |
|||
|
|||
func check(err error) { |
|||
if err != nil { |
|||
_, fn, line, _ := runtime.Caller(1) |
|||
log.Println(line) |
|||
log.Println(fn) |
|||
log.Println(err) |
|||
} |
|||
} |
@ -0,0 +1,43 @@ |
|||
package p2plib |
|||
|
|||
import ( |
|||
"fmt" |
|||
"math/rand" |
|||
"time" |
|||
) |
|||
|
|||
func InitializePeer(role, ip, port, restport, serverip, serverport string) ThisPeer { |
|||
//initialize some vars
|
|||
rand.Seed(time.Now().Unix()) |
|||
|
|||
var tp ThisPeer |
|||
tp.Running = true |
|||
tp.RunningPeer.Role = role |
|||
tp.RunningPeer.Port = port |
|||
tp.RunningPeer.RESTPort = restport |
|||
tp.RunningPeer.ID = HashPeer(tp.RunningPeer) |
|||
|
|||
tp.ID = tp.RunningPeer.ID |
|||
globalTP.PeersConnections.Outcoming.PeerID = tp.RunningPeer.ID |
|||
fmt.Println(tp.RunningPeer) |
|||
//outcomingPeersList.Peers = append(outcomingPeersList.Peers, peer.RunningPeer)
|
|||
globalTP.PeersConnections.Outcoming = AppendPeerIfNoExist(globalTP.PeersConnections.Outcoming, tp.RunningPeer) |
|||
fmt.Println(globalTP.PeersConnections.Outcoming) |
|||
|
|||
if tp.RunningPeer.Role == "server" { |
|||
go tp.AcceptPeers(tp.RunningPeer) |
|||
} |
|||
if tp.RunningPeer.Role == "client" { |
|||
var serverPeer Peer |
|||
serverPeer.IP = serverip |
|||
serverPeer.Port = serverport |
|||
serverPeer.Role = "server" |
|||
serverPeer.ID = HashPeer(serverPeer) |
|||
go tp.AcceptPeers(tp.RunningPeer) |
|||
ConnectToPeer(serverPeer) |
|||
} |
|||
globalTP = tp |
|||
|
|||
return tp |
|||
|
|||
} |
@ -0,0 +1,78 @@ |
|||
package p2plib |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"log" |
|||
"time" |
|||
|
|||
"github.com/fatih/color" |
|||
) |
|||
|
|||
type Msg struct { |
|||
Type string `json:"type"` |
|||
Date time.Time `json:"date"` |
|||
Content string `json:"content"` |
|||
PeersList PeersList `json:"peerslist"` |
|||
Data []byte `json:"data"` |
|||
} |
|||
|
|||
func MessageHandler(peer Peer, msg Msg) { |
|||
|
|||
log.Println("[New msg]") |
|||
log.Println(msg) |
|||
|
|||
switch msg.Type { |
|||
case "Hi": |
|||
color.Yellow(msg.Type) |
|||
color.Green(msg.Content) |
|||
break |
|||
case "PeersList": |
|||
color.Blue("newPeerslist") |
|||
fmt.Println(msg.PeersList) |
|||
color.Red("PeersList") |
|||
|
|||
UpdateNetworkPeersList(peer.Conn, msg.PeersList) |
|||
PropagatePeersList(peer) |
|||
PrintPeersList() |
|||
break |
|||
case "PeersList_Response": |
|||
//for the moment is not beeing used
|
|||
color.Blue("newPeerslist, from PeersList_Response") |
|||
fmt.Println(msg.PeersList) |
|||
color.Red("PeersList_Response") |
|||
|
|||
UpdateNetworkPeersList(peer.Conn, msg.PeersList) |
|||
PropagatePeersList(peer) |
|||
PrintPeersList() |
|||
break |
|||
case "Block": |
|||
/*//TODO check if the block is signed by an autorized emitter
|
|||
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) { |
|||
msg.Type = msgtype |
|||
msg.Content = msgcontent |
|||
msg.Date = time.Now() |
|||
} |
|||
func (msg Msg) ToBytes() []byte { |
|||
msgS, err := json.Marshal(msg) |
|||
check(err) |
|||
l := string(msgS) + "\n" |
|||
r := []byte(l) |
|||
return r |
|||
} |
|||
func (msg Msg) CreateFromBytes(bytes []byte) Msg { |
|||
err := json.Unmarshal(bytes, &msg) |
|||
check(err) |
|||
return msg |
|||
} |
@ -0,0 +1,164 @@ |
|||
package p2plib |
|||
|
|||
import ( |
|||
"fmt" |
|||
"net" |
|||
"time" |
|||
|
|||
"github.com/fatih/color" |
|||
) |
|||
|
|||
type Peer struct { |
|||
ID string `json:"id"` //in the future, this will be the peer hash
|
|||
IP string `json:"ip"` |
|||
Port string `json:"port"` |
|||
RESTPort string `json:"restport"` |
|||
Role string `json:"role"` //client or server
|
|||
Conn net.Conn `json:"conn"` |
|||
} |
|||
|
|||
type PeersList struct { |
|||
PeerID string |
|||
Peers []Peer `json:"peerslist"` |
|||
Date time.Time `json:"date"` |
|||
} |
|||
|
|||
type PeersConnections struct { |
|||
Incoming PeersList |
|||
Outcoming PeersList |
|||
Network PeersList //the peers that have been received in the lists from other peers
|
|||
} |
|||
|
|||
type ThisPeer struct { |
|||
Running bool |
|||
ID string |
|||
RunningPeer Peer |
|||
PeersConnections PeersConnections |
|||
} |
|||
|
|||
var globalTP ThisPeer |
|||
|
|||
func PeerIsInPeersList(p Peer, pl []Peer) int { |
|||
r := -1 |
|||
for i, peer := range pl { |
|||
if peer.IP+":"+peer.Port == p.IP+":"+p.Port { |
|||
r = i |
|||
} |
|||
} |
|||
return r |
|||
} |
|||
|
|||
func DeletePeerFromPeersList(p Peer, pl *PeersList) { |
|||
i := PeerIsInPeersList(p, pl.Peers) |
|||
if i != -1 { |
|||
//delete peer from pl.Peers
|
|||
pl.Peers = append(pl.Peers[:i], pl.Peers[i+1:]...) |
|||
} |
|||
} |
|||
func AppendPeerIfNoExist(pl PeersList, p Peer) PeersList { |
|||
i := PeerIsInPeersList(p, pl.Peers) |
|||
if i == -1 { |
|||
pl.Peers = append(pl.Peers, p) |
|||
} |
|||
return pl |
|||
} |
|||
func UpdateNetworkPeersList(conn net.Conn, newPeersList PeersList) { |
|||
for _, peer := range newPeersList.Peers { |
|||
if GetIPPortFromConn(conn) == peer.IP+":"+peer.Port { |
|||
peer.ID = newPeersList.PeerID |
|||
color.Yellow(peer.ID) |
|||
} |
|||
i := PeerIsInPeersList(peer, globalTP.PeersConnections.Network.Peers) |
|||
if i == -1 { |
|||
globalTP.PeersConnections.Network.Peers = append(globalTP.PeersConnections.Network.Peers, peer) |
|||
} else { |
|||
fmt.Println(globalTP.PeersConnections.Network.Peers[i]) |
|||
globalTP.PeersConnections.Network.Peers[i].ID = peer.ID |
|||
} |
|||
} |
|||
} |
|||
func SearchPeerAndUpdate(p Peer) { |
|||
for _, peer := range globalTP.PeersConnections.Outcoming.Peers { |
|||
color.Red(p.IP + ":" + p.Port) |
|||
color.Yellow(peer.IP + ":" + peer.Port) |
|||
if p.IP+":"+p.Port == peer.IP+":"+peer.Port { |
|||
peer.ID = p.ID |
|||
} |
|||
} |
|||
} |
|||
|
|||
//send the outcomingPeersList to all the peers except the peer p that has send the outcomingPeersList
|
|||
func PropagatePeersList(p Peer) { |
|||
for _, peer := range globalTP.PeersConnections.Network.Peers { |
|||
if peer.Conn != nil { |
|||
if peer.ID != p.ID && p.ID != "" { |
|||
color.Yellow(peer.ID + " - " + p.ID) |
|||
var msg Msg |
|||
msg.Construct("PeersList", "here my outcomingPeersList") |
|||
msg.PeersList = globalTP.PeersConnections.Outcoming |
|||
msgB := msg.ToBytes() |
|||
_, err := peer.Conn.Write(msgB) |
|||
check(err) |
|||
} else { |
|||
/* |
|||
for the moment, this is not being called, due that in the IncomingPeersList, |
|||
there is no peer.ID, so in the comparation wih the peer that has send the |
|||
peersList, is comparing ID with "", so nevere enters this 'else' section |
|||
|
|||
maybe it's not needed. TODO check if it's needed the PeerList_Response |
|||
For the moment is working without it |
|||
*/ |
|||
//to the peer that has sent the peerList, we send our PeersList
|
|||
|
|||
var msg Msg |
|||
msg.Construct("PeersList_Response", "here my outcomingPeersList") |
|||
msg.PeersList = globalTP.PeersConnections.Outcoming |
|||
msgB := msg.ToBytes() |
|||
_, err := peer.Conn.Write(msgB) |
|||
check(err) |
|||
} |
|||
} else { |
|||
//connect to peer
|
|||
if peer.ID != p.ID && peer.ID != globalTP.RunningPeer.ID { |
|||
if PeerIsInPeersList(peer, globalTP.PeersConnections.Outcoming.Peers) == -1 { |
|||
color.Red("no connection, connecting to peer: " + peer.Port) |
|||
ConnectToPeer(peer) |
|||
} |
|||
} |
|||
|
|||
} |
|||
} |
|||
} |
|||
func PrintPeersList() { |
|||
fmt.Println("") |
|||
color.Blue("runningPeer.ID: " + globalTP.RunningPeer.ID) |
|||
color.Green("OUTCOMING PEERSLIST:") |
|||
for _, peer := range globalTP.PeersConnections.Outcoming.Peers { |
|||
fmt.Println(peer) |
|||
} |
|||
color.Green("INCOMING PEERSLIST:") |
|||
for _, peer := range globalTP.PeersConnections.Incoming.Peers { |
|||
fmt.Println(peer) |
|||
} |
|||
|
|||
color.Green("NETWORK PEERSLIST:") |
|||
for _, peer := range globalTP.PeersConnections.Network.Peers { |
|||
fmt.Println(peer) |
|||
} |
|||
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) |
|||
} |
|||
} |
|||
}*/ |
@ -0,0 +1,38 @@ |
|||
package p2plib |
|||
|
|||
import ( |
|||
"crypto/sha256" |
|||
"encoding/base64" |
|||
"math/rand" |
|||
"net" |
|||
"strings" |
|||
) |
|||
|
|||
func GetIPPortFromConn(conn net.Conn) string { |
|||
ip := GetIPFromConn(conn) |
|||
port := GetPortFromConn(conn) |
|||
return ip + ":" + port |
|||
} |
|||
func GetIPFromConn(conn net.Conn) string { |
|||
s := conn.RemoteAddr().String() |
|||
s = strings.Split(s, ":")[0] |
|||
s = strings.Trim(s, ":") |
|||
return s |
|||
} |
|||
func GetPortFromConn(conn net.Conn) string { |
|||
s := conn.RemoteAddr().String() |
|||
s = strings.Split(s, ":")[1] |
|||
s = strings.Trim(s, ":") |
|||
return s |
|||
} |
|||
func RandInt(min int, max int) int { |
|||
r := rand.Intn(max-min) + min |
|||
return r |
|||
} |
|||
func HashPeer(p Peer) string { |
|||
peerString := p.IP + ":" + p.Port |
|||
|
|||
h := sha256.New() |
|||
h.Write([]byte(peerString)) |
|||
return base64.URLEncoding.EncodeToString(h.Sum(nil)) |
|||
} |
@ -0,0 +1,26 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"io/ioutil" |
|||
) |
|||
|
|||
//Config reads the config
|
|||
type Config struct { |
|||
IP string `json:"ip"` |
|||
Port string `json:"port"` |
|||
RestIP string `json:"restip"` |
|||
RESTPort string `json:"restport"` |
|||
ServerIP string `json:"serverip"` |
|||
ServerPort string `json:"serverport"` |
|||
ServerRESTPort string `json:"serverrestport"` |
|||
} |
|||
|
|||
var config Config |
|||
|
|||
func readConfig(path string) { |
|||
file, err := ioutil.ReadFile(path) |
|||
check(err) |
|||
content := string(file) |
|||
json.Unmarshal([]byte(content), &config) |
|||
} |
@ -0,0 +1,47 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"log" |
|||
"net/http" |
|||
"time" |
|||
|
|||
"github.com/gorilla/mux" |
|||
) |
|||
|
|||
type Route struct { |
|||
Name string |
|||
Method string |
|||
Pattern string |
|||
HandlerFunc http.HandlerFunc |
|||
} |
|||
|
|||
func Logger(inner http.Handler, name string) http.Handler { |
|||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
|||
start := time.Now() |
|||
|
|||
inner.ServeHTTP(w, r) |
|||
|
|||
log.Printf( |
|||
"%s\t%s\t%s\t%s", |
|||
r.Method, |
|||
r.RequestURI, |
|||
name, |
|||
time.Since(start), |
|||
) |
|||
}) |
|||
} |
|||
func NewRouter() *mux.Router { |
|||
router := mux.NewRouter().StrictSlash(true) |
|||
for _, route := range routes { |
|||
var handler http.Handler |
|||
handler = route.HandlerFunc |
|||
handler = Logger(handler, route.Name) |
|||
|
|||
router. |
|||
Methods(route.Method). |
|||
Path(route.Pattern). |
|||
Name(route.Name). |
|||
Handler(handler) |
|||
} |
|||
return router |
|||
} |
@ -0,0 +1,141 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"encoding/json" |
|||
"fmt" |
|||
"net/http" |
|||
|
|||
blockchainlib "./blockchainlib" |
|||
p2plib "./p2plib" |
|||
"github.com/fatih/color" |
|||
"github.com/gorilla/mux" |
|||
) |
|||
|
|||
type Routes []Route |
|||
|
|||
var routes = Routes{ |
|||
Route{ |
|||
"Index", |
|||
"GET", |
|||
"/", |
|||
Index, |
|||
}, |
|||
Route{ |
|||
"GetPeers", |
|||
"GET", |
|||
"/peers", |
|||
GetPeers, |
|||
}, |
|||
Route{ |
|||
"PostUser", |
|||
"POST", |
|||
"/register", |
|||
PostUser, |
|||
}, |
|||
Route{ |
|||
"GenesisBlock", |
|||
"GET", |
|||
"/blocks/genesis", |
|||
GenesisBlock, |
|||
}, |
|||
Route{ |
|||
"NextBlock", |
|||
"GET", |
|||
"/blocks/next/{blockhash}", |
|||
NextBlock, |
|||
}, |
|||
Route{ |
|||
"LastBlock", |
|||
"GET", |
|||
"/blocks/last", |
|||
LastBlock, |
|||
}, |
|||
} |
|||
|
|||
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, tp.ID) |
|||
} |
|||
func GetPeers(w http.ResponseWriter, r *http.Request) { |
|||
jResp, err := json.Marshal(tp.PeersConnections.Outcoming) |
|||
check(err) |
|||
fmt.Fprintln(w, string(jResp)) |
|||
} |
|||
func PostUser(w http.ResponseWriter, r *http.Request) { |
|||
|
|||
decoder := json.NewDecoder(r.Body) |
|||
var address string |
|||
err := decoder.Decode(&address) |
|||
if err != nil { |
|||
panic(err) |
|||
} |
|||
defer r.Body.Close() |
|||
fmt.Println(address) |
|||
color.Blue(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)) |
|||
} |
|||
|
|||
func PropagateBlock(b blockchainlib.Block) { |
|||
//prepare the msg to send to all connected peers
|
|||
var msg p2plib.Msg |
|||
msg.Construct("Block", "new block") |
|||
bJson, err := json.Marshal(b) |
|||
check(err) |
|||
|
|||
msg.Data = []byte(bJson) |
|||
msgB := msg.ToBytes() |
|||
for _, peer := range tp.PeersConnections.Outcoming.Peers { |
|||
if peer.Conn != nil { |
|||
_, err := peer.Conn.Write(msgB) |
|||
check(err) |
|||
} |
|||
} |
|||
} |
|||
|
|||
func GenesisBlock(w http.ResponseWriter, r *http.Request) { |
|||
var genesis blockchainlib.Block |
|||
if len(blockchain.Blocks) >= 0 { |
|||
genesis = blockchain.Blocks[0] |
|||
} |
|||
|
|||
jResp, err := json.Marshal(genesis) |
|||
check(err) |
|||
fmt.Fprintln(w, string(jResp)) |
|||
} |
|||
|
|||
func NextBlock(w http.ResponseWriter, r *http.Request) { |
|||
vars := mux.Vars(r) |
|||
blockhash := vars["blockhash"] |
|||
|
|||
currBlock, err := blockchain.GetBlockByHash(blockhash) |
|||
check(err) |
|||
nextBlock, err := blockchain.GetBlockByHash(currBlock.NextHash) |
|||
check(err) |
|||
|
|||
jResp, err := json.Marshal(nextBlock) |
|||
check(err) |
|||
fmt.Fprintln(w, string(jResp)) |
|||
} |
|||
|
|||
func LastBlock(w http.ResponseWriter, r *http.Request) { |
|||
var genesis blockchainlib.Block |
|||
if len(blockchain.Blocks) > 0 { |
|||
genesis = blockchain.Blocks[len(blockchain.Blocks)-1] |
|||
} |
|||
|
|||
jResp, err := json.Marshal(genesis) |
|||
check(err) |
|||
fmt.Fprintln(w, string(jResp)) |
|||
} |
@ -0,0 +1,20 @@ |
|||
package main |
|||
|
|||
import ( |
|||
"log" |
|||
"net/http" |
|||
|
|||
"github.com/gorilla/handlers" |
|||
) |
|||
|
|||
func runRestServer() { |
|||
//run API
|
|||
log.Println("server running") |
|||
log.Print("port: ") |
|||
log.Println(config.RESTPort) |
|||
router := NewRouter() |
|||
headersOk := handlers.AllowedHeaders([]string{"X-Requested-With", "Access-Control-Allow-Origin"}) |
|||
originsOk := handlers.AllowedOrigins([]string{"*"}) |
|||
methodsOk := handlers.AllowedMethods([]string{"GET", "HEAD", "POST", "PUT", "OPTIONS"}) |
|||
log.Fatal(http.ListenAndServe(":"+config.RESTPort, handlers.CORS(originsOk, headersOk, methodsOk)(router))) |
|||
} |
@ -0,0 +1 @@ |
|||
curl -X POST http://127.0.0.1:3002/register -d '{\"address\": \"sampleaddress\"}' |
@ -0,0 +1,16 @@ |
|||
SESSION='peersTest' |
|||
|
|||
tmux new-session -d -s $SESSION |
|||
tmux split-window -d -t 0 -v |
|||
tmux split-window -d -t 1 -h |
|||
tmux split-window -d -t 0 -h |
|||
|
|||
tmux send-keys -t 0 'cd peer && go run *.go server 3001 3002' enter |
|||
sleep 2 |
|||
tmux send-keys -t 1 "curl -X POST http://127.0.0.1:3002/register -d '{\"address\": \"firstaddress\"}'" enter |
|||
sleep 1 |
|||
tmux send-keys -t 1 'cd peer && go run *.go client 3003 3004' enter |
|||
tmux send-keys -t 2 'cd peer && go run *.go client 3005 3006' enter |
|||
tmux send-keys -t 3 'cd peer && go run *.go client 3007 3008' enter |
|||
|
|||
tmux attach |