Skip to content
This repository was archived by the owner on Feb 25, 2023. It is now read-only.

Commit 5bb0d7c

Browse files
committed
middleware: tunnel electrum through the noise channel
1 parent 740d71b commit 5bb0d7c

6 files changed

Lines changed: 121 additions & 18 deletions

File tree

middleware/cmd/middleware/main.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package main
44

55
import (
66
"flag"
7+
"fmt"
78
"log"
89
"net/http"
910

@@ -22,6 +23,8 @@ func main() {
2223
bbbConfigScript := flag.String("bbbconfigscript", "/opt/shift/scripts/bbb-config.sh", "Path to the bbb-config file that allows setting system configuration")
2324
flag.Parse()
2425

26+
electrsAddress := fmt.Sprintf("localhost:%s", *electrsRPCPort)
27+
2528
argumentMap := make(map[string]string)
2629
argumentMap["bitcoinRPCUser"] = *bitcoinRPCUser
2730
argumentMap["bitcoinRPCPassword"] = *bitcoinRPCPassword
@@ -43,7 +46,7 @@ func main() {
4346
middleware := middleware.NewMiddleware(argumentMap)
4447
log.Println("--------------- Started middleware --------------")
4548

46-
handlers := handlers.NewHandlers(middleware, *dataDir)
49+
handlers := handlers.NewHandlers(middleware, *dataDir, electrsAddress)
4750
log.Println("Binding middleware api to port 8845")
4851

4952
if err := http.ListenAndServe(":8845", handlers.Router); err != nil {
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package electrum
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"net"
7+
8+
"github.com/ethereum/go-ethereum/log"
9+
)
10+
11+
// Electrum makes a connection to an Electrum server and proxies messages.
12+
type Electrum struct {
13+
connection net.Conn
14+
onMessageReceived func([]byte)
15+
}
16+
17+
// NewElectrum creates a new Electrum instance and tries to connect to the server.
18+
func NewElectrum(address string, onMessageReceived func([]byte)) (*Electrum, error) {
19+
connection, err := net.Dial("tcp", address)
20+
if err != nil {
21+
return nil, err
22+
}
23+
electrum := &Electrum{
24+
connection: connection,
25+
onMessageReceived: onMessageReceived,
26+
}
27+
go electrum.read()
28+
return electrum, nil
29+
}
30+
31+
func (electrum *Electrum) read() {
32+
reader := bufio.NewReader(electrum.connection)
33+
for {
34+
line, err := reader.ReadBytes(byte('\n'))
35+
if err != nil {
36+
log.Error(fmt.Sprintf("electrum read error: %v", err))
37+
break
38+
}
39+
electrum.onMessageReceived(line)
40+
}
41+
}
42+
43+
// Send sends a raw message to the Electrum server.
44+
func (electrum *Electrum) Send(msg []byte) error {
45+
_, err := electrum.connection.Write(msg)
46+
return err
47+
}

middleware/src/handlers/handlers.go

Lines changed: 42 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,19 @@ import (
77
"sync"
88

99
middleware "github.com/digitalbitbox/bitbox-base/middleware/src"
10+
"github.com/digitalbitbox/bitbox-base/middleware/src/electrum"
1011
noisemanager "github.com/digitalbitbox/bitbox-base/middleware/src/noise"
1112
"github.com/digitalbitbox/bitbox-base/middleware/src/rpcserver"
1213

1314
"github.com/gorilla/mux"
1415
"github.com/gorilla/websocket"
1516
)
1617

18+
const (
19+
opElectrum = byte('e')
20+
opRPC = byte('r')
21+
)
22+
1723
// Middleware provides an interface to the middleware package.
1824
type Middleware interface {
1925
// Start triggers the main middleware event loop that emits events to be caught by the handlers.
@@ -30,6 +36,7 @@ type Handlers struct {
3036
upgrader websocket.Upgrader
3137
middleware Middleware
3238
middlewareEvents <-chan []byte
39+
electrumAddress string
3340

3441
noiseConfig *noisemanager.NoiseConfig
3542
nClients int
@@ -38,16 +45,21 @@ type Handlers struct {
3845
}
3946

4047
// NewHandlers returns a handler instance.
41-
func NewHandlers(middlewareInstance Middleware, dataDir string) *Handlers {
48+
func NewHandlers(
49+
middlewareInstance Middleware,
50+
dataDir string,
51+
electrumAddress string,
52+
) *Handlers {
4253
router := mux.NewRouter()
4354

4455
handlers := &Handlers{
45-
middleware: middlewareInstance,
46-
Router: router,
47-
upgrader: websocket.Upgrader{},
48-
noiseConfig: noisemanager.NewNoiseConfig(dataDir),
49-
nClients: 0,
50-
clientsMap: make(map[int]chan<- []byte),
56+
middleware: middlewareInstance,
57+
electrumAddress: electrumAddress,
58+
Router: router,
59+
upgrader: websocket.Upgrader{},
60+
noiseConfig: noisemanager.NewNoiseConfig(dataDir),
61+
nClients: 0,
62+
clientsMap: make(map[int]chan<- []byte),
5163
}
5264
handlers.Router.HandleFunc("/", handlers.rootHandler).Methods("GET")
5365
handlers.Router.HandleFunc("/ws", handlers.wsHandler)
@@ -96,11 +108,32 @@ func (handlers *Handlers) wsHandler(w http.ResponseWriter, r *http.Request) {
96108
log.Println(err.Error() + "Noise connection failed to initialize")
97109
return
98110
}
99-
server := rpcserver.NewRPCServer(handlers.middleware)
111+
writeChan := make(chan []byte)
112+
onElectrumMessageReceived := func(msg []byte) {
113+
writeChan <- append([]byte{opElectrum}, msg...)
114+
}
115+
electrumClient, err := electrum.NewElectrum(handlers.electrumAddress, onElectrumMessageReceived)
116+
if err != nil {
117+
log.Println(err.Error() + "Electrum connection failed to initialize")
118+
return
119+
}
100120

121+
server := rpcserver.NewRPCServer(
122+
handlers.middleware,
123+
electrumClient,
124+
)
125+
go func() {
126+
for {
127+
msg := <-server.RPCConnection.WriteChan()
128+
writeChan <- append([]byte{opRPC}, msg...)
129+
}
130+
}()
101131
handlers.mu.Lock()
102132
handlers.clientsMap[handlers.nClients] = server.RPCConnection.WriteChan()
103-
handlers.runWebsocket(ws, server.RPCConnection.ReadChan(), server.RPCConnection.WriteChan(), handlers.nClients)
133+
onMessageReceived := func(msg []byte) {
134+
server.RPCConnection.ReadChan() <- msg
135+
}
136+
handlers.runWebsocket(ws, onMessageReceived, writeChan, handlers.nClients)
104137
handlers.nClients++
105138
handlers.mu.Unlock()
106139
go server.Serve()

middleware/src/handlers/websocket.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,13 @@ const (
1616
// It takes four arguments, a websocket connection, a read and a write channel.
1717
//
1818
// The goroutines close client upon exit or dues to a send/receive error.
19-
func (handlers *Handlers) runWebsocket(client *websocket.Conn, readChan chan<- []byte, writeChan <-chan []byte, clientID int) {
19+
func (handlers *Handlers) runWebsocket(
20+
client *websocket.Conn,
21+
onMessageReceived func([]byte),
22+
writeChan <-chan []byte,
23+
clientID int) {
2024

21-
const maxMessageSize = 512
25+
const maxMessageSize = 1024 * 1024
2226
// this channel is used to break the write loop, when the read loop breaks
2327
closeChan := make(chan struct{})
2428

@@ -46,7 +50,7 @@ func (handlers *Handlers) runWebsocket(client *websocket.Conn, readChan chan<- [
4650
}
4751
if msg[0] == opICanHasPairinVerificashun {
4852
msg = handlers.noiseConfig.CheckVerification()
49-
err = client.WriteMessage(websocket.TextMessage, msg)
53+
err = client.WriteMessage(websocket.BinaryMessage, msg)
5054
if err != nil {
5155
log.Println("Error, websocket failed to write channel hash verification message")
5256
}
@@ -59,7 +63,7 @@ func (handlers *Handlers) runWebsocket(client *websocket.Conn, readChan chan<- [
5963
return
6064
}
6165
log.Println(string(messageDecrypted))
62-
readChan <- messageDecrypted
66+
onMessageReceived(messageDecrypted)
6367
}
6468
}
6569

@@ -77,7 +81,7 @@ func (handlers *Handlers) runWebsocket(client *websocket.Conn, readChan chan<- [
7781
_ = client.WriteMessage(websocket.CloseMessage, []byte{})
7882
return
7983
}
80-
err := client.WriteMessage(websocket.TextMessage, handlers.noiseConfig.Encrypt(message))
84+
err := client.WriteMessage(websocket.BinaryMessage, handlers.noiseConfig.Encrypt(message))
8185
if err != nil {
8286
log.Println("Error, websocket closed unexpectedly in the writing loop")
8387
_ = client.WriteMessage(websocket.CloseMessage, []byte{})

middleware/src/middleware.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,10 @@ func (middleware *Middleware) ResyncBitcoin() ResyncBitcoinResponse {
134134

135135
// SystemEnv returns a GetEnvResponse struct in response to a rpcserver request
136136
func (middleware *Middleware) SystemEnv() GetEnvResponse {
137-
response := GetEnvResponse{Network: middleware.environment.Network, ElectrsRPCPort: middleware.environment.ElectrsRPCPort}
137+
response := GetEnvResponse{
138+
Network: middleware.environment.Network,
139+
ElectrsRPCPort: middleware.environment.ElectrsRPCPort,
140+
}
138141
return response
139142
}
140143

middleware/src/rpcserver/rpcserver.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func (conn *rpcConn) Read(p []byte) (n int, err error) {
3434
}
3535

3636
func (conn *rpcConn) Write(p []byte) (n int, err error) {
37-
conn.writeChan <- append([]byte("r"), p...)
37+
conn.writeChan <- p
3838
return len(p), nil
3939
}
4040

@@ -49,16 +49,22 @@ type Middleware interface {
4949
SampleInfo() middleware.SampleInfoResponse
5050
}
5151

52+
type Electrum interface {
53+
Send(msg []byte) error
54+
}
55+
5256
// RPCServer provides rpc calls to the middleware
5357
type RPCServer struct {
5458
middleware Middleware
59+
electrum Electrum
5560
RPCConnection *rpcConn
5661
}
5762

5863
// NewRPCServer returns a new RPCServer
59-
func NewRPCServer(middleware Middleware) *RPCServer {
64+
func NewRPCServer(middleware Middleware, electrum Electrum) *RPCServer {
6065
server := &RPCServer{
6166
middleware: middleware,
67+
electrum: electrum,
6268
RPCConnection: newRPCConn(),
6369
}
6470
err := rpc.Register(server)
@@ -90,6 +96,13 @@ func (server *RPCServer) GetSampleInfo(args int, reply *middleware.SampleInfoRes
9096
return nil
9197
}
9298

99+
// ElectrumSend sends a message to Electrum on the connection owned by the client.
100+
func (server *RPCServer) ElectrumSend(
101+
args struct{ Msg []byte },
102+
reply *struct{}) error {
103+
return server.electrum.Send(args.Msg)
104+
}
105+
93106
func (server *RPCServer) Serve() {
94107
rpc.ServeConn(server.RPCConnection)
95108
}

0 commit comments

Comments
 (0)