forked from restaking-cloud/mev-plus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
apis.go
231 lines (192 loc) · 7.61 KB
/
apis.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
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
package externalvalidatorproxy
import (
"context"
"fmt"
"net/http"
commonTypes "github.com/bsn-eng/pon-golang-types/common"
"github.com/attestantio/go-builder-client/spec"
apiv1 "github.com/attestantio/go-builder-client/api/v1"
)
const (
// Builder API paths
pathStatus = "/eth/v1/builder/status"
pathRegisterValidator = "/eth/v1/builder/validators"
pathGetHeader = "/eth/v1/builder/header/%d/%s/%s"
pathGetPayload = "/eth/v1/builder/blinded_blocks"
)
func (p *ExternalValidatorProxyService) Status() error {
p.log.Info("Checking External Validator Proxy service status")
var respErr error
proxyRespCh := make(chan error, len(p.cfg.Addresses))
for _, proxy := range p.cfg.Addresses {
go func(proxy string) {
url := proxy + pathStatus
code, err := SendHTTPRequest(context.Background(), p.httpClient, http.MethodGet, url, nil, nil)
if err != nil {
p.log.WithError(err).Warnf("Error while calling proxy's status endpoint: %s", proxy)
proxyRespCh <- err
return
} else if code == http.StatusNoContent {
proxyRespCh <- nil
return
} else if code != http.StatusOK {
p.log.WithError(err).Warnf("Error while calling proxy's status endpoint: %s", proxy)
proxyRespCh <- fmt.Errorf("status code %d", code)
return
}
proxyRespCh <- nil
}(proxy.String())
}
for i := 0; i < len(p.cfg.Addresses); i++ {
respErr = <-proxyRespCh
if respErr == nil {
// If any of the proxies is up,
return nil
}
}
return respErr
}
func (p *ExternalValidatorProxyService) RegisterValidator(payload []apiv1.SignedValidatorRegistration) error {
p.log.Info("Registering validator with External Validator Proxy service")
var respErr error
proxyRespCh := make(chan error, len(p.cfg.Addresses))
for _, proxy := range p.cfg.Addresses {
go func(proxy string) {
url := proxy + pathRegisterValidator
code, err := SendHTTPRequest(context.Background(), p.httpClient, http.MethodPost, url, payload, nil)
if err != nil {
p.log.WithError(err).Warnf("Error while calling proxy's validator registration endpoint: %s", proxy)
proxyRespCh <- err
return
} else if code == http.StatusNoContent {
proxyRespCh <- nil
return
} else if code != http.StatusOK {
p.log.WithError(err).Warnf("Error while calling proxy's validator registration endpoint: %s", proxy)
proxyRespCh <- fmt.Errorf("status code %d", code)
return
}
proxyRespCh <- nil
}(proxy.String())
}
for i := 0; i < len(p.cfg.Addresses); i++ {
respErr = <-proxyRespCh
if respErr == nil {
// If any of the proxies is up,
return nil
}
}
return respErr
}
func (p *ExternalValidatorProxyService) GetHeader(slot uint64, parentHash, pubkey string) (res []spec.VersionedSignedBuilderBid, err error) {
p.log.Info("Getting header from External Validator Proxy service")
var respErr error
proxyErrRespCh := make(chan error, len(p.cfg.Addresses))
proxyRespCh := make(chan spec.VersionedSignedBuilderBid, len(p.cfg.Addresses))
for _, proxy := range p.cfg.Addresses {
go func(proxy string) {
url := proxy + fmt.Sprintf(pathGetHeader, slot, parentHash, pubkey)
response := new(spec.VersionedSignedBuilderBid)
code, err := SendHTTPRequest(context.Background(), p.httpClient, http.MethodGet, url, nil, response)
if err != nil {
p.log.WithError(err).Warnf("Error while calling proxy's get header endpoint: %s", proxy)
proxyErrRespCh <- err
proxyRespCh <- spec.VersionedSignedBuilderBid{}
return
} else if code != http.StatusOK {
p.log.WithError(err).Warnf("Error while calling proxy's get header endpoint: %s", proxy)
proxyErrRespCh <- fmt.Errorf("status code %d", code)
proxyRespCh <- spec.VersionedSignedBuilderBid{}
return
}
proxyRespCh <- *response
proxyErrRespCh <- nil
}(proxy.String())
}
for i := 0; i < len(p.cfg.Addresses); i++ {
respErr = <-proxyErrRespCh
resp := <-proxyRespCh
if respErr == nil {
// Check if the response is empty
if resp.IsEmpty() {
continue
}
// If any of the proxies returns a header append it to the response
res = append(res, resp)
}
}
if len(res) == 0 {
// If none of the proxies returns a header, return an error that may have occured
// If no error occured, return a generic error
if respErr == nil {
return res, fmt.Errorf("no header returned")
}
return res, respErr
}
return res, nil
}
func (p *ExternalValidatorProxyService) GetPayload(VersionedSignedBlindedBeaconBlock *commonTypes.VersionedSignedBlindedBeaconBlock) (versionedExecutionPayload []commonTypes.VersionedExecutionPayloadWithVersionName, err error) {
p.log.Info("Getting payload from External Validator Proxy service")
baseSignedBlindedBeaconBlock, err := VersionedSignedBlindedBeaconBlock.ToBaseSignedBlindedBeaconBlock()
if err != nil {
return versionedExecutionPayload, err
}
var respErr error
proxyErrRespCh := make(chan error, len(p.cfg.Addresses))
proxyRespCh := make(chan commonTypes.VersionedExecutionPayloadWithVersionName, len(p.cfg.Addresses))
for _, proxy := range p.cfg.Addresses {
go func(proxy string) {
url := proxy + pathGetPayload
response := new(commonTypes.VersionedExecutionPayloadWithVersionName)
code, err := SendHTTPRequestWithRetries(context.Background(), p.httpClient, http.MethodPost, url, VersionedSignedBlindedBeaconBlock, response, p.cfg.RequestMaxRetries, p.log)
if err != nil {
p.log.WithError(err).Debugf("Error while calling proxy's get payload endpoint: %s", proxy)
proxyErrRespCh <- err
proxyRespCh <- commonTypes.VersionedExecutionPayloadWithVersionName{}
return
} else if code != http.StatusOK {
// Do not log an error here as the proxy software may throw an error about
// an invalid payload header that was not provided by that proxy since not tracking
// headers accross proxies like relays, as two proxies may get the same header from being
// connected to the relay or not depending on users configurations
proxyErrRespCh <- fmt.Errorf("status code %d", code)
proxyRespCh <- commonTypes.VersionedExecutionPayloadWithVersionName{}
return
}
proxyRespCh <- *response
proxyErrRespCh <- nil
}(proxy.String())
}
for i := 0; i < len(p.cfg.Addresses); i++ {
respErr = <-proxyErrRespCh
resp := <-proxyRespCh
if respErr == nil {
// Check if the response is empty
if resp.VersionName == "" {
continue
}
baseExecutionPayload, err := resp.VersionedExecutionPayload.ToBaseExecutionPayload()
if err != nil {
p.log.WithError(err).Debugf("Error while extracting baseExecutionPayload from proxy's get payload endpoint: %s", p.cfg.Addresses[i].String())
continue
}
// check if hash matches
if baseExecutionPayload.BlockHash.String() != baseSignedBlindedBeaconBlock.Message.Body.ExecutionPayloadHeader.BlockHash.String() {
err := fmt.Errorf("blockHash returned from proxy's get payload endpoint %s does not match the one provided %s", baseExecutionPayload.BlockHash.String(), baseSignedBlindedBeaconBlock.Message.Body.ExecutionPayloadHeader.BlockHash.String())
p.log.WithError(err).Debugf("Wrong blockHash returned from proxy's get payload endpoint: %s", p.cfg.Addresses[i].String())
continue
}
// If any of the proxies returns a payload append it to the response
versionedExecutionPayload = append(versionedExecutionPayload, resp)
}
}
if len(versionedExecutionPayload) == 0 {
// If none of the proxies returns a payload, return an error that may have occured
// If no error occured, return a generic error
if respErr == nil {
return versionedExecutionPayload, fmt.Errorf("no payload returned")
}
return versionedExecutionPayload, respErr
}
return versionedExecutionPayload, nil
}