168 lines
3.8 KiB
Go
168 lines
3.8 KiB
Go
// Package utils provides utilities that are needed for the functioning of Kerma
|
|
package utils
|
|
|
|
import (
|
|
"bufio"
|
|
"errors"
|
|
"kerma/helpers"
|
|
"kerma/models"
|
|
"net"
|
|
"time"
|
|
|
|
"github.com/docker/go/canonical/json"
|
|
)
|
|
|
|
/*
|
|
A Client represents a Kerma client with the following properties:
|
|
- State - the current state of the application
|
|
- Handler - a TCPHandler that is used to handle requests to and from remote peers
|
|
- Logger
|
|
*/
|
|
type Client struct {
|
|
State *models.State
|
|
Handler TCPHandler
|
|
Logger helpers.Logger
|
|
}
|
|
|
|
/*
|
|
Construct creates a new client with the specified state and handler objects
|
|
*/
|
|
func (c *Client) Construct(state *models.State, h TCPHandler) {
|
|
c.State = state
|
|
c.Handler = h
|
|
}
|
|
|
|
/*
|
|
InitHandshake initiates a handshake on the specified connection
|
|
*/
|
|
func (c *Client) InitHandshake(peerName string) {
|
|
var resp models.Hello
|
|
resp.Construct()
|
|
respJSON, _ := json.MarshalCanonical(resp)
|
|
|
|
c.Handler.Output(respJSON)
|
|
c.Handler.Conn.SetDeadline(time.Now().Add(time.Duration(c.State.Config.ConnTimeout) * time.Second))
|
|
in, err := bufio.NewReader(c.Handler.Conn).ReadBytes('\n')
|
|
if err != nil {
|
|
c.Logger.Error(err.Error())
|
|
if errors.Is(err, net.ErrClosed) {
|
|
c.State.RemovePeerByName(peerName)
|
|
} else {
|
|
c.Handler.Fail("Failed reading message")
|
|
}
|
|
return
|
|
}
|
|
|
|
msg, err := c.Handler.Input(in)
|
|
if err != nil {
|
|
c.Logger.Error(err.Error())
|
|
c.Handler.Fail("Failed parsing message")
|
|
return
|
|
}
|
|
|
|
msgJSON, err := json.MarshalCanonical(msg)
|
|
if err != nil {
|
|
c.Logger.Error("Failed parsing message " + err.Error())
|
|
c.Handler.Fail("Failed parsing message")
|
|
return
|
|
}
|
|
|
|
if msgType, ok := msg["type"]; ok {
|
|
if msgType == "hello" {
|
|
var helloReq models.Hello
|
|
err := helloReq.UnmarshalJSON(msgJSON)
|
|
|
|
if err == nil {
|
|
c.Logger.Info("Constructing handshake with " + c.Handler.GetRemotePeer())
|
|
c.State.CompleteHandshake(peerName)
|
|
} else {
|
|
c.Handler.Fail("Hello response invalid")
|
|
return
|
|
}
|
|
} else {
|
|
c.Handler.Fail("Response type not expected " + msgType.(string))
|
|
return
|
|
}
|
|
} else {
|
|
c.Handler.Fail("Response type not specified")
|
|
return
|
|
}
|
|
}
|
|
|
|
/*
|
|
DiscoverPeers requests remote peers from a known peer
|
|
*/
|
|
func (c *Client) DiscoverPeers(peerName string) {
|
|
if c.State.CheckForHandshake(peerName) {
|
|
var req models.Generic
|
|
req.BuildPeerRequest()
|
|
reqJSON, _ := req.MarshalJson()
|
|
c.Handler.Output(reqJSON)
|
|
}
|
|
}
|
|
|
|
/*
|
|
DiscoverChainTip requests chaintip from a known peer
|
|
*/
|
|
func (c *Client) DiscoverChainTip(peerName string) {
|
|
if c.State.CheckForHandshake(peerName) {
|
|
var req models.Generic
|
|
req.BuildChainTipRequest()
|
|
reqJSON, _ := req.MarshalJson()
|
|
c.Handler.Output(reqJSON)
|
|
}
|
|
}
|
|
|
|
/*
|
|
DiscoverMempool requests mempool from a known peer
|
|
*/
|
|
func (c *Client) DiscoverMempool(peerName string) {
|
|
if c.State.CheckForHandshake(peerName) {
|
|
var req models.Generic
|
|
req.BuildMempoolRequest()
|
|
reqJSON, _ := req.MarshalJson()
|
|
c.Handler.Output(reqJSON)
|
|
}
|
|
}
|
|
|
|
/*
|
|
DiscoverObject requests remote objects from a known peer
|
|
*/
|
|
func (c *Client) DiscoverObject(peerName string, objectID string) {
|
|
if c.State.CheckForHandshake(peerName) {
|
|
var req models.ObjectWrapper
|
|
req.BuildObjectRequest(objectID)
|
|
reqJSON, _ := req.MarshalJson()
|
|
c.Handler.Output(reqJSON)
|
|
}
|
|
}
|
|
|
|
/*
|
|
GossipBlocks sends information about local blocks to peer
|
|
*/
|
|
func (c *Client) GossipBlocks(peerName string) {
|
|
if c.State.CheckForHandshake(peerName) {
|
|
for _, block := range c.State.Chain {
|
|
var req models.ObjectWrapper
|
|
bid := block.GetID()
|
|
req.BuildGossipObject(bid)
|
|
reqJSON, _ := req.MarshalJson()
|
|
c.Handler.Output(reqJSON)
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
GossipTransactions sends information about local transactions to peer
|
|
*/
|
|
func (c *Client) GossipTransactions(peerName string) {
|
|
if c.State.CheckForHandshake(peerName) {
|
|
for txid := range c.State.Transactions {
|
|
var req models.ObjectWrapper
|
|
req.BuildGossipObject(txid)
|
|
reqJSON, _ := req.MarshalJson()
|
|
c.Handler.Output(reqJSON)
|
|
}
|
|
}
|
|
}
|