@ -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 |