blockchain_go/server.go

186 lines
3.2 KiB
Go

package main
import (
"bytes"
"encoding/gob"
"fmt"
"io"
"io/ioutil"
"log"
"net"
)
const protocol = "tcp"
const dnsNodeID = "3000"
const nodeVersion = 1
const commandLength = 12
var nodeAddress string
var knownNodes []string
type addr struct {
AddrList []string
}
type verack struct {
}
type verzion struct {
Version int
AddrFrom string
}
func commandToBytes(command string) []byte {
var bytes [commandLength]byte
for i, c := range command {
bytes[i] = byte(c)
}
return bytes[:]
}
func bytesToCommand(bytes []byte) string {
var command []byte
for _, b := range bytes {
if b != 0x0 {
command = append(command, b)
}
}
return fmt.Sprintf("%s", command)
}
func extractCommand(request []byte) []byte {
return request[:commandLength]
}
func sendAddr(address string) {
nodes := addr{knownNodes}
nodes.AddrList = append(nodes.AddrList, nodeAddress)
payload := gobEncode(nodes)
request := append(commandToBytes("addr"), payload...)
sendData(address, request)
}
func sendData(addr string, data []byte) {
conn, err := net.Dial(protocol, addr)
if err != nil {
log.Panic(err)
}
defer conn.Close()
_, err = io.Copy(conn, bytes.NewReader(data))
if err != nil {
log.Panic(err)
}
}
func sendVersion(addr string) {
payload := gobEncode(verzion{nodeVersion, nodeAddress})
request := append(commandToBytes("version"), payload...)
sendData(addr, request)
}
func sendVrack(addr string) {
payload := gobEncode(verack{})
request := append(commandToBytes("verack"), payload...)
sendData(addr, request)
}
func handleAddr(request []byte) {
var buff bytes.Buffer
var payload addr
buff.Write(request[commandLength:])
dec := gob.NewDecoder(&buff)
err := dec.Decode(&payload)
if err != nil {
log.Panic(err)
}
knownNodes = append(knownNodes, payload.AddrList...)
fmt.Printf("There are %d known nodes now!\n", len(knownNodes))
}
func handleVersion(request []byte) {
var buff bytes.Buffer
var payload verzion
buff.Write(request[commandLength:])
dec := gob.NewDecoder(&buff)
err := dec.Decode(&payload)
if err != nil {
log.Panic(err)
}
sendVrack(payload.AddrFrom)
sendAddr(payload.AddrFrom)
knownNodes = append(knownNodes, payload.AddrFrom)
}
func handleConnection(conn net.Conn, bc *Blockchain) {
request, err := ioutil.ReadAll(conn)
if err != nil {
log.Panic(err)
}
command := bytesToCommand(request[:commandLength])
fmt.Printf("Received %s command\n", command)
switch command {
case "addr":
handleAddr(request)
case "version":
handleVersion(request)
case "verack":
//
default:
fmt.Println("Unknown command!")
}
conn.Close()
}
// StartServer starts a node
func StartServer(nodeID string) {
nodeAddress = fmt.Sprintf("localhost:%s", nodeID)
ln, err := net.Listen(protocol, nodeAddress)
if err != nil {
log.Panic(err)
}
defer ln.Close()
if nodeID != dnsNodeID {
sendVersion(fmt.Sprintf("localhost:%s", dnsNodeID))
}
bc := NewBlockchain(nodeID)
for {
conn, err := ln.Accept()
if err != nil {
log.Panic(err)
}
go handleConnection(conn, bc)
}
}
func gobEncode(data interface{}) []byte {
var buff bytes.Buffer
enc := gob.NewEncoder(&buff)
err := enc.Encode(data)
if err != nil {
log.Panic(err)
}
return buff.Bytes()
}