This repository has been archived by the owner on Feb 25, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Define rough architecture for middleware
The functionality of the middleware is now split into three packages. The middleware package takes care of communicating with bitcoind and lightningd and emits an event if new data is received. The handlers package takes care of the communication with the bitbox-wallet-app. It upgrades connections to websocket, if requested, and starts the main middleware event loop. Events from the middleware are caught and passed into a websocket handler. The main package is what is compiled into a binary. It parses command line arguments and creates new middleware and handler instances. This commit also implements logging, where each package creates its own logrus logging instance.
- Loading branch information
1 parent
6111778
commit 1044516
Showing
5 changed files
with
237 additions
and
151 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
// Package main provides the entry point into the middleware and accepts command line arguments | ||
package main | ||
|
||
import ( | ||
"flag" | ||
"log" | ||
"net/http" | ||
|
||
middleware "github.com/digitalbitbox/bitbox-base/middleware/src" | ||
"github.com/digitalbitbox/bitbox-base/middleware/src/handlers" | ||
) | ||
|
||
func main() { | ||
bitcoinRPCUser := flag.String("rpcuser", "rpcuser", "Bitcoin rpc user name") | ||
bitcoinRPCPassword := flag.String("rpcpassword", "rpcpassword", "Bitcoin rpc password") | ||
bitcoinRPCPort := flag.String("rpcport", "8332", "Bitcoin rpc port, localhost is assumed as an address") | ||
lightningRPCPath := flag.String("lightning-rpc-path", "/home/bitcoin/.lightning/lightning-rpc", "Path to the lightning rpc unix socket") | ||
flag.Parse() | ||
|
||
logBeforeExit := func() { | ||
// Recover from all panics and log error before panicking again. | ||
if r := recover(); r != nil { | ||
// r is of type interface{} and thus we cannot use log.WithError(r). | ||
log.Printf("%v, error detected, shutting down", r) | ||
panic(r) | ||
} | ||
} | ||
defer logBeforeExit() | ||
middleware := middleware.NewMiddleware(*bitcoinRPCUser, *bitcoinRPCPassword, *bitcoinRPCPort, *lightningRPCPath) | ||
log.Println("--------------- Started middleware --------------") | ||
|
||
handlers := handlers.NewHandlers(middleware) | ||
log.Println("Binding middleware api to port 8845") | ||
|
||
if err := http.ListenAndServe(":8845", handlers.Router); err != nil { | ||
log.Println(err.Error() + " Failed to listen for HTTP") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// Package handlers implements an api for the bitbox-wallet-app to talk to. | ||
package handlers | ||
|
||
import ( | ||
"fmt" | ||
"log" | ||
"net/http" | ||
|
||
middleware "github.com/digitalbitbox/bitbox-base/middleware/src" | ||
"github.com/gorilla/mux" | ||
"github.com/gorilla/websocket" | ||
//"github.com/sirupsen/logrus" | ||
) | ||
|
||
// Middleware provides an interface to the middleware package. | ||
type Middleware interface { | ||
Start() <-chan *middleware.SampleInfo | ||
} | ||
|
||
// Handlers provides a web api | ||
type Handlers struct { | ||
Router *mux.Router | ||
clients map[*websocket.Conn]bool | ||
upgrader websocket.Upgrader //upgrader takes an http request and upgrades the connection with its origin to websocket | ||
//log log.Logger | ||
middleware Middleware | ||
//TODO(TheCharlatan): In future this event should have a generic interface (thus only containing raw json) | ||
middlewareEvents <-chan *middleware.SampleInfo | ||
} | ||
|
||
// NewHandlers returns a handler instance. | ||
func NewHandlers(middlewareInstance Middleware) *Handlers { | ||
router := mux.NewRouter() | ||
|
||
handlers := &Handlers{ | ||
middleware: middlewareInstance, | ||
Router: router, | ||
clients: make(map[*websocket.Conn]bool), | ||
upgrader: websocket.Upgrader{ | ||
CheckOrigin: func(r *http.Request) bool { | ||
return true | ||
}, | ||
}, | ||
//log: log.NewLogger(), | ||
} | ||
//log.WithField("group", "Handlers") | ||
handlers.Router.HandleFunc("/", handlers.rootHandler).Methods("GET") | ||
handlers.Router.HandleFunc("/ws", handlers.wsHandler) | ||
|
||
handlers.middlewareEvents = handlers.middleware.Start() | ||
go handlers.echo() | ||
return handlers | ||
} | ||
|
||
// rootHandler provides an endpoint to indicate that the middleware is online and able to handle requests. TODO(TheCharlatan): Define a better error-response system. In future, this should be the first step in an authentication procedure. | ||
func (handlers *Handlers) rootHandler(w http.ResponseWriter, r *http.Request) { | ||
_, err := w.Write([]byte("OK!!\n")) | ||
if err != nil { | ||
log.Println(err.Error() + " Failed to write response bytes in root handler") | ||
} | ||
} | ||
|
||
func (handlers *Handlers) wsHandler(w http.ResponseWriter, r *http.Request) { | ||
ws, err := handlers.upgrader.Upgrade(w, r, nil) | ||
if err != nil { | ||
log.Println(err.Error() + " Failed to upgrade connection") | ||
} | ||
|
||
// register client | ||
handlers.clients[ws] = true | ||
} | ||
|
||
func (handlers *Handlers) echo() { | ||
var i = 0 | ||
for { | ||
i++ | ||
val := <-handlers.middlewareEvents | ||
blockinfo := fmt.Sprintf("%d %f %d %s", val.Blocks, val.Difficulty, i, val.LightningAlias) | ||
// send to every client that is currently connected | ||
//fmt.Println(blockinfo) | ||
for client := range handlers.clients { | ||
log.Println(blockinfo) | ||
err := client.WriteMessage(websocket.TextMessage, []byte(blockinfo)) | ||
if err != nil { | ||
log.Println(err.Error() + " Unexpected websocket error") | ||
client.Close() | ||
delete(handlers.clients, client) | ||
} | ||
} | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
// Package middleware emits events with data from services running on the base. | ||
package middleware | ||
|
||
import ( | ||
"log" | ||
"time" | ||
|
||
"github.com/btcsuite/btcd/rpcclient" | ||
lightning "github.com/fiatjaf/lightningd-gjson-rpc" | ||
//"github.com/sirupsen/logrus" | ||
) | ||
|
||
// SampleInfo holds sample information from c-lightning and bitcoind. It is temporary for testing purposes. | ||
type SampleInfo struct { | ||
Blocks int64 `json:"blocks"` | ||
Difficulty float64 `json:"difficulty"` | ||
LightningAlias string `json:"lightning_alias"` | ||
} | ||
|
||
// Middleware connects to services on the base with provided parrameters and emits events for the handler. | ||
type Middleware struct { | ||
info SampleInfo | ||
events chan *SampleInfo | ||
bitcoinRPCUser, bitcoinRPCPassword, bitcoinRPCPort string | ||
lightningRPCPath string | ||
} | ||
|
||
// NewMiddleware creates a new instance of the middleware | ||
func NewMiddleware(bitcoinRPCUser, bitcoinRPCPassword, bitcoinRPCPort, lightningRPCPath string) *Middleware { | ||
middleware := &Middleware{ | ||
events: make(chan *SampleInfo), | ||
bitcoinRPCUser: bitcoinRPCUser, | ||
bitcoinRPCPassword: bitcoinRPCPassword, | ||
bitcoinRPCPort: bitcoinRPCPort, | ||
lightningRPCPath: lightningRPCPath, | ||
info: SampleInfo{ | ||
Blocks: 0, | ||
Difficulty: 0.0, | ||
LightningAlias: "disconnected", | ||
}, | ||
} | ||
|
||
return middleware | ||
} | ||
|
||
// demoBitcoinRPC is a function that demonstrates a connection to bitcoind. Currently it gets the blockcount and difficulty and writes it into the SampleInfo. | ||
func (middleware *Middleware) demoBitcoinRPC(bitcoinRPCUser, bitcoinRPCPassword, bitcoinRPCPort string) { | ||
connCfg := rpcclient.ConnConfig{ | ||
HTTPPostMode: true, | ||
DisableTLS: true, | ||
Host: "127.0.0.1:" + bitcoinRPCPort, | ||
User: bitcoinRPCUser, | ||
Pass: bitcoinRPCPassword, | ||
} | ||
client, err := rpcclient.New(&connCfg, nil) | ||
if err != nil { | ||
log.Println(err.Error() + " Failed to create new bitcoind rpc client") | ||
} | ||
//client is shutdown/deconstructed again as soon as this function returns | ||
defer client.Shutdown() | ||
|
||
//Get current block count. | ||
var blockCount int64 | ||
blockCount, err = client.GetBlockCount() | ||
if err != nil { | ||
log.Println(err.Error() + " No blockcount received") | ||
} else { | ||
middleware.info.Blocks = blockCount | ||
} | ||
blockChainInfo, err := client.GetBlockChainInfo() | ||
if err != nil { | ||
log.Println(err.Error() + " GetBlockChainInfo rpc call failed") | ||
} else { | ||
middleware.info.Difficulty = blockChainInfo.Difficulty | ||
} | ||
|
||
} | ||
|
||
// demoCLightningRPC demonstrates a connection with lightnind. Currently it gets the lightningd alias and writes it into the SampleInfo. | ||
func (middleware *Middleware) demoCLightningRPC(lightningRPCPath string) { | ||
ln := &lightning.Client{ | ||
Path: lightningRPCPath, | ||
} | ||
|
||
nodeinfo, err := ln.Call("getinfo") | ||
if err != nil { | ||
log.Println(err.Error() + " Lightningd getinfo called failed.") | ||
} else { | ||
middleware.info.LightningAlias = nodeinfo.Get("alias").String() | ||
} | ||
} | ||
|
||
func (middleware *Middleware) rpcLoop() { | ||
for { | ||
middleware.demoBitcoinRPC(middleware.bitcoinRPCUser, middleware.bitcoinRPCPassword, middleware.bitcoinRPCPort) | ||
middleware.demoCLightningRPC(middleware.lightningRPCPath) | ||
middleware.events <- &middleware.info | ||
time.Sleep(5 * time.Second) | ||
} | ||
} | ||
|
||
// Start gives a trigger for the handler to start the rpc event loop | ||
func (middleware *Middleware) Start() <-chan *SampleInfo { | ||
go middleware.rpcLoop() | ||
return middleware.events | ||
} |