mirror of
https://github.com/arnaucube/bc.git
synced 2026-02-07 11:06:40 +01:00
first commit
This commit is contained in:
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# bc
|
||||
|
||||
Own p2p network and own blockchain libraries written in Go, to develop own decentralized apps.
|
||||
4
memory.md
Normal file
4
memory.md
Normal file
@@ -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
|
||||
1
peer/.gitignore
vendored
Normal file
1
peer/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
*.data
|
||||
73
peer/README.md
Normal file
73
peer/README.md
Normal file
@@ -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
|
||||
20
peer/blockchainlib/block.go
Normal file
20
peer/blockchainlib/block.go
Normal 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))
|
||||
}
|
||||
155
peer/blockchainlib/blockchain.go
Normal file
155
peer/blockchainlib/blockchain.go
Normal file
@@ -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
|
||||
}
|
||||
15
peer/blockchainlib/errors.go
Normal file
15
peer/blockchainlib/errors.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
6
peer/config.json
Executable file
6
peer/config.json
Executable file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"ip": "127.0.0.1",
|
||||
"serverip": "127.0.0.1",
|
||||
"serverport": "3000",
|
||||
"serverrestport": "3002"
|
||||
}
|
||||
15
peer/errors.go
Executable file
15
peer/errors.go
Executable file
@@ -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)
|
||||
}
|
||||
}
|
||||
24
peer/log.go
Executable file
24
peer/log.go
Executable file
@@ -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)
|
||||
}
|
||||
48
peer/main.go
Normal file
48
peer/main.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
81
peer/p2plib/connections.go
Normal file
81
peer/p2plib/connections.go
Normal file
@@ -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()*/
|
||||
}
|
||||
15
peer/p2plib/errors.go
Normal file
15
peer/p2plib/errors.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
43
peer/p2plib/init.go
Normal file
43
peer/p2plib/init.go
Normal file
@@ -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
|
||||
|
||||
}
|
||||
78
peer/p2plib/messages.go
Normal file
78
peer/p2plib/messages.go
Normal file
@@ -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
|
||||
}
|
||||
164
peer/p2plib/peers.go
Normal file
164
peer/p2plib/peers.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
38
peer/p2plib/utils.go
Normal file
38
peer/p2plib/utils.go
Normal file
@@ -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))
|
||||
}
|
||||
26
peer/readConfig.go
Executable file
26
peer/readConfig.go
Executable file
@@ -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)
|
||||
}
|
||||
47
peer/restConfig.go
Executable file
47
peer/restConfig.go
Executable file
@@ -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
|
||||
}
|
||||
141
peer/restRoutes.go
Executable file
141
peer/restRoutes.go
Executable file
@@ -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))
|
||||
}
|
||||
20
peer/restServer.go
Normal file
20
peer/restServer.go
Normal file
@@ -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)))
|
||||
}
|
||||
1
peer/tests.sh
Normal file
1
peer/tests.sh
Normal file
@@ -0,0 +1 @@
|
||||
curl -X POST http://127.0.0.1:3002/register -d '{\"address\": \"sampleaddress\"}'
|
||||
16
runTmuxTestPeers.sh
Normal file
16
runTmuxTestPeers.sh
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user