add: integrate raft consensus layer, ethereum and filecoin rpc into node

This commit is contained in:
bahadylbekov 2020-08-07 05:42:09 +03:00
parent fa848cff3a
commit ab2f8639c4
10 changed files with 629 additions and 65 deletions

View File

@ -28,9 +28,7 @@ type RaftConsensus struct {
}
type RaftState struct {
From string
To string
Amount string
Value string
}
func NewRaftConsensus() *RaftConsensus {
@ -42,8 +40,8 @@ func NewRaftConsensus() *RaftConsensus {
}
func (raftConsensus *RaftConsensus) NewState(from, to, amount string) {
raftConsensus.State = &RaftState{from, to, amount}
func (raftConsensus *RaftConsensus) NewState(value string) {
raftConsensus.State = &RaftState{value}
}
func (raftConsensus *RaftConsensus) NewConsensus(op consensus.Op) {
@ -65,8 +63,8 @@ func (raftConsensus *RaftConsensus) GetConsensusState(consensus *libp2praft.Cons
return raftConsensus.State, nil
}
func (raftConsensus *RaftConsensus) UpdateState(from, to, amount string) error {
raftConsensus.NewState(from, to, amount)
func (raftConsensus *RaftConsensus) UpdateState(value string) error {
raftConsensus.NewState(value)
// CommitState() blocks until the state has been
// agreed upon by everyone
@ -189,9 +187,9 @@ func (raftConsensus *RaftConsensus) StartConsensus(host host.Host, peers []peer.
time.Sleep(5 * time.Second)
}
func (raft *RaftConsensus) UpdateConsensus(from, to, amount string) {
func (raft *RaftConsensus) UpdateConsensus(value string) {
if raft.Actor.IsLeader() {
if err := raft.UpdateState(from, to, amount); err != nil {
if err := raft.UpdateState(value); err != nil {
raft.Logger.Fatal("Failed to update state", err)
}
} else {

5
go.mod
View File

@ -7,6 +7,9 @@ require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/edsrzf/mmap-go v1.0.0 // indirect
github.com/ethereum/go-ethereum v1.9.5
github.com/filecoin-project/go-address v0.0.2-0.20200504173055-8b6f2fb2b3ef
github.com/filecoin-project/lotus v0.4.2
github.com/filecoin-project/specs-actors v0.8.1-0.20200723200253-a3c01bc62f99
github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 // indirect
github.com/hashicorp/raft v1.1.2
github.com/ipfs/go-log v1.0.4
@ -30,7 +33,7 @@ require (
github.com/status-im/keycard-go v0.0.0-20200402102358-957c09536969 // indirect
github.com/tyler-smith/go-bip39 v1.0.2 // indirect
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect
golang.org/x/crypto v0.0.0-20200423211502-4bdfaf469ed5
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
)
replace github.com/libp2p/go-libp2p-raft v0.1.5 => github.com/ItalyPaleAle/go-libp2p-raft v0.1.6-0.20200703060436-0b37aa16095e

488
go.sum

File diff suppressed because it is too large Load Diff

View File

@ -4,6 +4,7 @@ import (
"encoding/json"
"sync"
"github.com/Secured-Finance/p2p-oracle-node/rpcclient"
mapset "github.com/deckarep/golang-set"
"github.com/ipfs/go-log"
peer "github.com/libp2p/go-libp2p-core/peer"
@ -21,10 +22,10 @@ type Handler struct {
}
// TextMessage is more end-user model of regular text messages
type TextMessage struct {
Topic string `json:"topic"`
Body string `json:"body"`
From peer.ID `json:"from"`
type EventMessage struct {
Topic string `json:"topic"`
Body *rpcclient.OracleEvent `json:"body"`
From peer.ID `json:"from"`
}
func NewHandler(pb *pubsub.PubSub, oracleTopic string, peerID peer.ID, networkTopics mapset.Set) *Handler {
@ -41,7 +42,7 @@ func NewHandler(pb *pubsub.PubSub, oracleTopic string, peerID peer.ID, networkTo
return handler
}
func (h *Handler) HandleIncomingMessage(topic string, msg pubsub.Message, handleTextMessage func(TextMessage)) {
func (h *Handler) HandleIncomingMessage(topic string, msg pubsub.Message, handleTextMessage func(EventMessage)) {
fromPeerID, err := peer.IDFromBytes(msg.From)
if err != nil {
h.Logger.Warn("Error occurred when reading message from field...")
@ -59,17 +60,17 @@ func (h *Handler) HandleIncomingMessage(topic string, msg pubsub.Message, handle
switch message.Flag {
// Getting regular message
case FlagGenericMessage:
textMessage := TextMessage{
eventMessage := EventMessage{
Topic: topic,
Body: message.Body,
From: fromPeerID,
}
handleTextMessage(textMessage)
handleTextMessage(eventMessage)
// Getting topic request, answer topic response
case FlagTopicsRequest:
respond := &GetTopicsRespondMessage{
BaseMessage: BaseMessage{
Body: "",
Body: &rpcclient.OracleEvent{},
Flag: FlagTopicsResponse,
To: fromPeerID,
},
@ -110,6 +111,13 @@ func (h *Handler) HandleIncomingMessage(topic string, msg pubsub.Message, handle
h.Logger.Info("Greeting respond from " + fromPeerID.String() + ":" + message.From.String() + " in topic " + topic)
case FlagFarewell:
h.Logger.Info("Greeting respond from " + fromPeerID.String() + ":" + message.From.String() + " in topic " + topic)
case FlagEventMessage:
eventMessage := EventMessage{
Topic: topic,
Body: message.Body,
From: fromPeerID,
}
handleTextMessage(eventMessage)
default:
h.Logger.Info("\nUnknown message type: %#x\n", message.Flag)
}

View File

@ -3,6 +3,7 @@ package handler
import (
"encoding/json"
"github.com/Secured-Finance/p2p-oracle-node/rpcclient"
"github.com/libp2p/go-libp2p-core/peer"
)
@ -15,14 +16,15 @@ const (
FlagGreeting int = 0x5
FlagFarewell int = 0x6
FlagGreetingRespond int = 0x7
FlagEventMessage int = 0x8
)
// BaseMessage is the basic message format of our protocol
type BaseMessage struct {
Body string `json:"body"`
To peer.ID `json:"to"`
Flag int `json:"flag"`
From peer.ID `json:"from"`
Body *rpcclient.OracleEvent `json:"body"`
To peer.ID `json:"to"`
Flag int `json:"flag"`
From peer.ID `json:"from"`
}
// GetTopicsRespondMessage is the format of the message to answer of request for topics
@ -40,7 +42,7 @@ func (h *Handler) sendIdentityResponse(topic string, fromPeerID peer.ID) {
flag = FlagGreetingRespond
}
respond := &BaseMessage{
Body: "",
Body: &rpcclient.OracleEvent{},
Flag: flag,
From: "",
To: fromPeerID,
@ -63,45 +65,45 @@ func (h *Handler) sendIdentityResponse(topic string, fromPeerID peer.ID) {
// TODO: refactor with promise
func (h *Handler) RequestPeerIdentity(peerID peer.ID) {
requestPeersIdentity := &BaseMessage{
Body: "",
Body: &rpcclient.OracleEvent{},
To: peerID,
Flag: FlagIdentityRequest,
From: h.peerID,
}
h.sendMessageToServiceTopic(requestPeersIdentity)
h.SendMessageToServiceTopic(requestPeersIdentity)
}
// TODO: refactor
func (h *Handler) SendGreetingInTopic(topic string) {
greetingMessage := &BaseMessage{
Body: "",
Body: &rpcclient.OracleEvent{},
To: "",
Flag: FlagGreeting,
From: h.peerID,
}
h.sendMessageToTopic(topic, greetingMessage)
h.SendMessageToTopic(topic, greetingMessage)
}
// TODO: refactor
func (h *Handler) SendFarewellInTopic(topic string) {
farewellMessage := &BaseMessage{
Body: "",
Body: &rpcclient.OracleEvent{},
To: "",
Flag: FlagFarewell,
From: h.peerID,
}
h.sendMessageToTopic(topic, farewellMessage)
h.SendMessageToTopic(topic, farewellMessage)
}
// Sends marshaled message to the service topic
func (h *Handler) sendMessageToServiceTopic(message *BaseMessage) {
h.sendMessageToTopic(h.oracleTopic, message)
func (h *Handler) SendMessageToServiceTopic(message *BaseMessage) {
h.SendMessageToTopic(h.oracleTopic, message)
}
func (h *Handler) sendMessageToTopic(topic string, message *BaseMessage) {
func (h *Handler) SendMessageToTopic(topic string, message *BaseMessage) {
sendData, err := json.Marshal(message)
if err != nil {
h.Logger.Warn("Failed to send message to topic", err)

View File

@ -1,5 +1,7 @@
package handler
import "github.com/Secured-Finance/p2p-oracle-node/rpcclient"
// Get list of topics **this** node is subscribed to
func (h *Handler) GetTopics() []string {
topics := h.pb.GetTopics()
@ -9,11 +11,11 @@ func (h *Handler) GetTopics() []string {
// Requesting topics from **other** peers
func (h *Handler) RequestNetworkTopics() {
requestTopicsMessage := &BaseMessage{
Body: "",
Body: &rpcclient.OracleEvent{},
Flag: FlagTopicsRequest,
To: "",
From: h.peerID,
}
h.sendMessageToServiceTopic(requestTopicsMessage)
h.SendMessageToServiceTopic(requestTopicsMessage)
}

View File

@ -6,7 +6,10 @@ import (
"fmt"
"github.com/Secured-Finance/p2p-oracle-node/config"
consensus "github.com/Secured-Finance/p2p-oracle-node/consensus"
"github.com/Secured-Finance/p2p-oracle-node/handler"
"github.com/Secured-Finance/p2p-oracle-node/rpc"
"github.com/Secured-Finance/p2p-oracle-node/rpcclient"
mapset "github.com/deckarep/golang-set"
"github.com/ipfs/go-log"
"github.com/libp2p/go-libp2p"
@ -26,10 +29,14 @@ type Node struct {
handler *handler.Handler
Config *config.Config
Logger *log.ZapEventLogger
Consensus *consensus.RaftConsensus
Lotus *rpc.LotusClient
Ethereum *rpcclient.EthereumClient
}
func NewNode() *Node {
node := &Node{
OracleTopic: "p2p_oracle",
Config: config.NewConfig(),
Logger: log.Logger("rendezvous"),
networkTopics: mapset.NewSet(),

View File

@ -1,15 +1,19 @@
package node
import (
"bufio"
"context"
"encoding/json"
"io"
"os"
"io/ioutil"
"sync"
"time"
consensus "github.com/Secured-Finance/p2p-oracle-node/consensus"
"github.com/Secured-Finance/p2p-oracle-node/handler"
"github.com/Secured-Finance/p2p-oracle-node/rpc"
"github.com/Secured-Finance/p2p-oracle-node/rpcclient"
"github.com/filecoin-project/go-address"
lotusTypes "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/libp2p/go-libp2p-core/host"
peer "github.com/libp2p/go-libp2p-core/peer"
discovery "github.com/libp2p/go-libp2p-discovery"
@ -19,6 +23,30 @@ import (
"github.com/multiformats/go-multiaddr"
)
var (
LotusHost = ""
LotusJWT = ""
EthWsUrl = "wss://ropsten.infura.io/ws/v3/b9faa807bb814588bfdb3d6e94a37737"
EthUrl = "https://ropsten.infura.io/v3/b9faa807bb814588bfdb3d6e94a37737"
)
type LotusMessage struct {
Version int64
To address.Address
From address.Address
Nonce uint64
Value lotusTypes.BigInt
GasPrice lotusTypes.BigInt
GasLimit int64
Method abi.MethodNum
Params []byte
}
func (node *Node) readSub(subscription *pubsub.Subscription, incomingMessagesChan chan pubsub.Message) {
ctx := node.GlobalCtx
for {
@ -71,7 +99,7 @@ func (node *Node) newTopic(topic string) {
return
case msg := <-incomingMessages:
{
node.handler.HandleIncomingMessage(node.OracleTopic, msg, func(textMessage handler.TextMessage) {
node.handler.HandleIncomingMessage(node.OracleTopic, msg, func(textMessage handler.EventMessage) {
node.Logger.Info("%s \x1b[32m%s\x1b[0m> ", textMessage.From, textMessage.Body)
})
}
@ -83,7 +111,7 @@ func (node *Node) newTopic(topic string) {
// NOTE: we don't need to be subscribed to publish something
func (node *Node) writeTopic(topic string) {
ctx := node.GlobalCtx
stdReader := bufio.NewReader(os.Stdin)
// stdReader := bufio.NewReader(os.Stdin)
for {
select {
case <-ctx.Done():
@ -91,18 +119,8 @@ func (node *Node) writeTopic(topic string) {
default:
}
node.Logger.Info("> ")
text, err := stdReader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
node.Logger.Warn("Error reading from stdin", err)
return
}
message := &handler.BaseMessage{
Body: text,
Body: &rpcclient.OracleEvent{},
Flag: handler.FlagGenericMessage,
}
@ -191,7 +209,20 @@ func (node *Node) startPubSub(ctx context.Context, host host.Host) {
node.Logger.Info("Waiting for correct set up of PubSub...")
time.Sleep(3 * time.Second)
peers := node.Host.Peerstore().Peers()
consensus := consensus.NewRaftConsensus()
node.Consensus = consensus
node.Consensus.StartConsensus(node.Host, peers)
ethereum := rpcclient.NewEthereumClient()
node.Ethereum = ethereum
ethereum.Connect(ctx, EthUrl, "rpc")
ethereum.Connect(ctx, EthWsUrl, "websocket")
lotus := rpc.NewLotusClient(LotusHost, LotusJWT)
node.Lotus = lotus
incomingEvents := make(chan rpcclient.OracleEvent)
incomingMessages := make(chan pubsub.Message)
go func() {
@ -199,6 +230,7 @@ func (node *Node) startPubSub(ctx context.Context, host host.Host) {
node.GlobalCtxCancel()
}()
go node.readSub(subscription, incomingMessages)
go ethereum.SubscribeOnOracleEvents(ctx, "0x89d3A6151a9E608c51FF70E0F7f78a109949c2c1", incomingEvents)
go node.getNetworkTopics()
MainLoop:
@ -208,9 +240,23 @@ MainLoop:
break MainLoop
case msg := <-incomingMessages:
{
node.handler.HandleIncomingMessage(node.OracleTopic, msg, func(textMessage handler.TextMessage) {
node.handler.HandleIncomingMessage(node.OracleTopic, msg, func(textMessage handler.EventMessage) {
node.Logger.Info("%s > \x1b[32m%s\x1b[0m", textMessage.From, textMessage.Body)
node.Logger.Info("> ")
response, err := node.Lotus.GetMessage(textMessage.Body.RequestType)
if err != nil {
node.Logger.Warn("Failed to get transaction data from lotus node")
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
node.Logger.Warn("Failed to read lotus response")
}
var lotusMessage = new(LotusMessage)
if err := json.Unmarshal(body, &lotusMessage); err != nil {
node.Logger.Warn("Failed to unmarshal to get message request")
}
node.Consensus.UpdateConsensus(lotusMessage.Value.String())
})
}
case newPeer := <-peerChan:
@ -226,6 +272,16 @@ MainLoop:
node.Logger.Info("Connected to:", newPeer)
node.Logger.Info("> ")
}
case event := <-incomingEvents:
{
message := &handler.BaseMessage{
Body: &event,
Flag: handler.FlagEventMessage,
From: node.Host.ID(),
To: "",
}
node.handler.SendMessageToServiceTopic(message)
}
}
}

View File

@ -37,7 +37,7 @@ func NewLotusClient(host string, token string) *LotusClient {
}
}
func (c *LotusClient) GetMessage(r *http.Request, txHash string) (*http.Response, error) {
func (c *LotusClient) GetMessage(txHash string) (*http.Response, error) {
requestBody := NewRequestBody("Filecoin.GetMessage")
requestBody.Params = append(requestBody.Params, txHash)
body, err := json.Marshal(requestBody)

View File

@ -53,7 +53,7 @@ func NewEthereumClient() *EthereumClient {
return ethereumClient
}
func (c *EthereumClient) Connect(ctx context.Context, url string, connectionType string) error {
func (c *EthereumClient) Connect(ctx context.Context, url string, connectionType string) {
client, err := ethclient.Dial(url)
if err != nil {
c.Logger.Fatal(err)
@ -63,7 +63,6 @@ func (c *EthereumClient) Connect(ctx context.Context, url string, connectionType
} else {
c.HttpClient = client
}
return nil
}
// Balance returns the balance of the given ethereum address.
@ -76,7 +75,7 @@ func (c *EthereumClient) Balance(ctx context.Context, address string) (*big.Int,
return value, nil
}
func (c *EthereumClient) SendTransaction(private_key, to string, amount int64) string {
func (c *EthereumClient) SendTransaction(ctx context.Context, private_key, to string, amount int64) string {
privateKey, err := crypto.HexToECDSA(private_key)
if err != nil {
c.Logger.Fatal("Failed to parse private key", err)
@ -89,14 +88,14 @@ func (c *EthereumClient) SendTransaction(private_key, to string, amount int64) s
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, err := c.HttpClient.PendingNonceAt(context.Background(), fromAddress)
nonce, err := c.HttpClient.PendingNonceAt(ctx, fromAddress)
if err != nil {
c.Logger.Fatal("Failed to generate wallet nonce value", err)
}
value := big.NewInt(amount)
gasLimit := uint64(21000) // in units
gasPrice, err := c.HttpClient.SuggestGasPrice(context.Background())
gasPrice, err := c.HttpClient.SuggestGasPrice(ctx)
if err != nil {
c.Logger.Fatal("Failed to suggest new gas price", err)
}
@ -105,7 +104,7 @@ func (c *EthereumClient) SendTransaction(private_key, to string, amount int64) s
var data []byte
tx := types.NewTransaction(nonce, toAddress, value, gasLimit, gasPrice, data)
chainID, err := c.HttpClient.NetworkID(context.Background())
chainID, err := c.HttpClient.NetworkID(ctx)
if err != nil {
c.Logger.Fatal("Failed to get network ID", err)
}
@ -115,7 +114,7 @@ func (c *EthereumClient) SendTransaction(private_key, to string, amount int64) s
c.Logger.Fatal("Failed to sign transaction", err)
}
err = c.HttpClient.SendTransaction(context.Background(), signedTx)
err = c.HttpClient.SendTransaction(ctx, signedTx)
if err != nil {
c.Logger.Fatal("Failed to send signed transaction", err)
}
@ -147,7 +146,7 @@ func (c *EthereumClient) GenerateAddressFromPrivateKey(private_key string) strin
return address
}
func (c *EthereumClient) SubscribeOnOracleEvents(ctx context.Context, address string) {
func (c *EthereumClient) SubscribeOnOracleEvents(ctx context.Context, address string, incomingEventsChan chan OracleEvent) {
contractAddress := common.HexToAddress(address)
query := ethereum.FilterQuery{
@ -160,7 +159,7 @@ func (c *EthereumClient) SubscribeOnOracleEvents(ctx context.Context, address st
}
logs := make(chan types.Log)
sub, err := c.WsClient.SubscribeFilterLogs(context.Background(), query, logs)
sub, err := c.WsClient.SubscribeFilterLogs(ctx, query, logs)
if err != nil {
c.Logger.Fatal(err)
}
@ -176,7 +175,8 @@ func (c *EthereumClient) SubscribeOnOracleEvents(ctx context.Context, address st
if err != nil {
c.Logger.Fatal(err)
}
c.Logger.Info(event) // pointer to event log
c.Logger.Info(event)
incomingEventsChan <- event
}
}
}
@ -206,7 +206,7 @@ func (c *EthereumClient) importKeyStore(filePath string, password string) string
return account.Address.Hex()
}
func (c *EthereumClient) SendConsensusValues(private_key string, smartContractAddress string, reqID *big.Int, data string, callbackAddress common.Address, callbackMethodID [4]byte) string {
func (c *EthereumClient) SendConsensusValues(ctx context.Context, private_key string, smartContractAddress string, reqID *big.Int, data string, callbackAddress common.Address, callbackMethodID [4]byte) string {
privateKey, err := crypto.HexToECDSA(private_key)
if err != nil {
c.Logger.Fatal("Failed to generate private key", err)
@ -222,12 +222,12 @@ func (c *EthereumClient) SendConsensusValues(private_key string, smartContractAd
c.Logger.Info(hexutil.Encode(publicKeyBytes)[4:])
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
nonce, err := c.HttpClient.PendingNonceAt(context.Background(), fromAddress)
nonce, err := c.HttpClient.PendingNonceAt(ctx, fromAddress)
if err != nil {
c.Logger.Fatal(err)
}
gasPrice, err := c.HttpClient.SuggestGasPrice(context.Background())
gasPrice, err := c.HttpClient.SuggestGasPrice(ctx)
if err != nil {
c.Logger.Fatal(err)
}