-
Notifications
You must be signed in to change notification settings - Fork 5
/
http.go
179 lines (147 loc) · 4.23 KB
/
http.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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package gsi
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"net"
"net/http"
"cosmossdk.io/core/transaction"
"cosmossdk.io/server/v2/appmanager"
"github.com/cosmos/cosmos-sdk/codec"
"github.com/gordian-engine/gordian/gcrypto"
"github.com/gordian-engine/gordian/tm/tmp2p/tmlibp2p"
"github.com/gordian-engine/gordian/tm/tmstore"
"github.com/gorilla/mux"
)
type HTTPServer struct {
done chan struct{}
}
type HTTPServerConfig struct {
Listener net.Listener
FinalizationStore tmstore.FinalizationStore
MirrorStore tmstore.MirrorStore
CryptoRegistry *gcrypto.Registry
Libp2pHost *tmlibp2p.Host
Libp2pconn *tmlibp2p.Connection
AppManager appmanager.AppManager[transaction.Tx]
TxCodec transaction.Codec[transaction.Tx]
Codec codec.Codec
TxBuffer *SDKTxBuf
}
func NewHTTPServer(ctx context.Context, log *slog.Logger, cfg HTTPServerConfig) *HTTPServer {
srv := &http.Server{
Handler: newMux(log, cfg),
BaseContext: func(net.Listener) context.Context {
return ctx
},
}
h := &HTTPServer{
done: make(chan struct{}),
}
go h.serve(log, cfg.Listener, srv)
go h.waitForShutdown(ctx, srv)
return h
}
func (h *HTTPServer) Wait() {
<-h.done
}
func (h *HTTPServer) waitForShutdown(ctx context.Context, srv *http.Server) {
select {
case <-h.done:
// h.serve returned on its own, nothing left to do here.
return
case <-ctx.Done():
// Forceful shutdown. We could probably log any returned error on this.
_ = srv.Close()
}
}
func (h *HTTPServer) serve(log *slog.Logger, ln net.Listener, srv *http.Server) {
defer close(h.done)
if err := srv.Serve(ln); err != nil {
if errors.Is(err, net.ErrClosed) || errors.Is(err, http.ErrServerClosed) {
log.Info("HTTP server shutting down")
} else {
log.Info("HTTP server shutting down due to error", "err", err)
}
}
}
func newMux(log *slog.Logger, cfg HTTPServerConfig) http.Handler {
r := mux.NewRouter()
r.HandleFunc("/blocks/watermark", handleBlocksWatermark(log, cfg)).Methods("GET")
r.HandleFunc("/validators", handleValidators(log, cfg)).Methods("GET")
setDebugRoutes(log, cfg, r)
setCompatRoutes(log, cfg, r)
return r
}
func handleBlocksWatermark(log *slog.Logger, cfg HTTPServerConfig) func(w http.ResponseWriter, req *http.Request) {
ms := cfg.MirrorStore
return func(w http.ResponseWriter, req *http.Request) {
vh, vr, ch, cr, err := ms.NetworkHeightRound(req.Context())
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// TODO: this should probably be an exported type somewhere.
var currentBlock struct {
VotingHeight uint64
VotingRound uint32
CommittingHeight uint64
CommittingRound uint32
}
currentBlock.VotingHeight = vh
currentBlock.VotingRound = vr
currentBlock.CommittingHeight = ch
currentBlock.CommittingRound = cr
if err := json.NewEncoder(w).Encode(currentBlock); err != nil {
log.Warn("Failed to marshal current block", "err", err)
return
}
}
}
func handleValidators(log *slog.Logger, cfg HTTPServerConfig) func(w http.ResponseWriter, req *http.Request) {
ms := cfg.MirrorStore
fs := cfg.FinalizationStore
reg := cfg.CryptoRegistry
return func(w http.ResponseWriter, req *http.Request) {
_, _, committingHeight, _, err := ms.NetworkHeightRound(req.Context())
if err != nil {
http.Error(
w,
fmt.Sprintf("failed to get committing height: %v", err),
http.StatusInternalServerError,
)
return
}
_, _, valSet, _, err := fs.LoadFinalizationByHeight(req.Context(), committingHeight)
if err != nil {
http.Error(
w,
fmt.Sprintf("failed to load finalization: %v", err),
http.StatusInternalServerError,
)
return
}
vals := valSet.Validators
// Now we have the validators at the committing height.
type jsonValidator struct {
PubKey []byte
Power uint64
}
var resp struct {
FinalizationHeight uint64
Validators []jsonValidator
}
resp.FinalizationHeight = committingHeight
resp.Validators = make([]jsonValidator, len(vals))
for i, v := range vals {
resp.Validators[i].Power = v.Power
resp.Validators[i].PubKey = reg.Marshal(v.PubKey)
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Warn("Failed to marshal validators response", "err", err)
return
}
}
}