-
Notifications
You must be signed in to change notification settings - Fork 4
/
handler.go
132 lines (101 loc) · 3.07 KB
/
handler.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package main
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
)
const (
SecretHeader = "Voltage-Secret"
Status = "status"
WaitingUnlock = "waiting_unlock"
LNDUnlockPath = "/v1/unlockwallet"
)
type unlockEvent struct {
API string `json:"api"`
Kind string `json:"type"`
Details unlockDetails `json:"details"`
}
type unlockDetails struct {
Status string `json:"status"`
}
type lndUnlock struct {
WalletPassword string `json:"wallet_password"`
StatelessInit bool `json:"stateless_init"`
}
type handler struct {
ctx context.Context
secret string
nodeAPI string
walletPassword string
}
func newHandler(ctx context.Context, secret, nodeAPI, walletPassword string) *handler {
return &handler{ctx, secret, nodeAPI, walletPassword}
}
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
if value := r.Header.Get(SecretHeader); value != h.secret {
log.Printf("secret '%s' from header didn't match expected secret\n", value)
w.WriteHeader(http.StatusForbidden)
return
}
var event unlockEvent
if err := json.NewDecoder(r.Body).Decode(&event); err != nil {
log.Println("error reading the request body")
w.WriteHeader(http.StatusBadRequest)
return
}
h.handleEvent(r.Context(), w, event)
}
func (h *handler) handleEvent(ctx context.Context, w http.ResponseWriter, event unlockEvent) {
if event.API != h.nodeAPI {
http.Error(w, fmt.Sprintf("api '%s' does not match expected api", event.API), http.StatusBadRequest)
return
}
if event.Kind != Status {
http.Error(w, fmt.Sprintf("event type '%s' does not match expected type", event.Kind), http.StatusBadRequest)
return
}
if event.Details.Status != WaitingUnlock {
http.Error(
w,
fmt.Sprintf("event details status '%s' does not match expected status", event.Details.Status),
http.StatusBadRequest)
return
}
body, err := json.Marshal(lndUnlock{
WalletPassword: base64.StdEncoding.EncodeToString([]byte(h.walletPassword)),
StatelessInit: true,
})
if err != nil {
http.Error(w, fmt.Sprintf("error marshalling the request to lnd: %s", err), http.StatusInternalServerError)
return
}
endpoint := url.URL{Scheme: "https", Host: h.nodeAPI + ":8080", Path: LNDUnlockPath}
req, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), bytes.NewBuffer(body))
if err != nil {
http.Error(w, fmt.Sprintf("error building the request to lnd: %s", err), http.StatusInternalServerError)
return
}
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
http.Error(w, fmt.Sprintf("error sending the request to lnd: %s", err), http.StatusInternalServerError)
return
}
defer res.Body.Close()
if !(res.StatusCode >= 200 && res.StatusCode < 300) {
body, _ := io.ReadAll(res.Body)
http.Error(w, fmt.Sprintf("error returned from unlocking LND: %s", body), http.StatusBadRequest)
return
}
w.WriteHeader(http.StatusOK)
}