-
Notifications
You must be signed in to change notification settings - Fork 0
/
target.go
219 lines (200 loc) · 6.57 KB
/
target.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
package main
import (
"context"
"regexp"
"time"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"github.com/ybbus/jsonrpc/v3"
)
var agentShortVersionPattern = regexp.MustCompile(`^(.+)\+.*`)
type (
Status int
Target struct {
hf *heyFil `json:"-"`
ID string `json:"id,omitempty"`
Status Status `json:"status,omitempty"`
AddrInfo *peer.AddrInfo `json:"addr_info,omitempty"`
LastChecked time.Time `json:"last_checked,omitempty"`
ErrMessage string `json:"err,omitempty"`
Err error `json:"-"`
Topic string `json:"topic,omitempty"`
HeadProtocol protocol.ID `json:"head_protocol,omitempty"`
Head cid.Cid `json:"head,omitempty"`
KnownByIndexer bool `json:"known_by_indexer,omitempty"`
Protocols []protocol.ID `json:"protocols,omitempty"`
AgentVersion string `json:"agent_version,omitempty"`
AgentShortVersion string `json:"agent_short_version,omitempty"`
Transports *TransportsQueryResponse `json:"transports,omitempty"`
StateMinerPower *StateMinerPowerResp `json:"state_miner_power,omitempty"`
DealCount int64 `json:"deal_count,omitempty"`
DealCountWithinDay int64 `json:"deal_count_within_day,omitempty"`
DealCountWithinWeek int64 `json:"deal_count_within_week,omitempty"`
}
)
const (
StatusUnknown Status = iota
StatusOK
StatusAPICallFailed
StatusInternalError
StatusUnknownRPCError
StatusNotMiner
StatusUnreachable
StatusUnaddressable
StatusUnindexed
StatusTopicMismatch
StatusEmptyHead
StatusGetHeadError
StatusAnnounceError
StatusUnidentifiable
StatusNoAddrInfo
)
func (t *Target) check(ctx context.Context) *Target {
defer func() {
t.LastChecked = time.Now()
if t.Err != nil {
t.ErrMessage = t.Err.Error()
}
}()
logger := logger.With("miner", t.ID)
counts := t.hf.dealStats.getDealCounts(t.ID)
t.DealCount = counts.count
t.DealCountWithinDay = counts.countWithinDay
t.DealCountWithinWeek = counts.countWithinWeek
// Get state miner power
t.StateMinerPower, t.Err = t.hf.stateMinerPower(ctx, t.ID)
if t.Err != nil {
logger.Warnw("Failed to get state miner power", "err", t.Err)
// Reset target error and proceed. Because, there are other checks to do and we
// care less about recording this failure relative to other ones.
t.Err = nil
}
// Get address for miner ID from FileCoin API.
t.AddrInfo, t.Err = t.hf.stateMinerInfo(ctx, t.ID)
switch e := t.Err.(type) {
case nil:
switch {
case t.AddrInfo == nil:
t.Status = StatusNoAddrInfo
return t
case t.AddrInfo.ID == "" && len(t.AddrInfo.Addrs) == 0:
t.Status = StatusNoAddrInfo
return t
case t.AddrInfo.ID == "":
t.Status = StatusUnidentifiable
return t
case len(t.AddrInfo.Addrs) == 0:
t.Status = StatusUnaddressable
return t
default:
logger.Debugw("Discovered Addrs for miner", "addrs", t.AddrInfo)
logger = logger.With("peerID", t.AddrInfo.ID)
}
case *jsonrpc.HTTPError:
logger.Debugw("HTTP error while getting state miner info", "status", e.Code, "err", t.Err)
t.Status = StatusAPICallFailed
return t
case *jsonrpc.RPCError:
logger.Debugw("RPC API error while getting state miner info", "code", e.Code, "err", t.Err)
switch e.Code {
case 1:
t.Status = StatusNotMiner
default:
logger.Warn("RPC API error code missing miner status mapping", "code", e.Code, "err", t.Err)
t.Status = StatusUnknownRPCError
}
return t
default:
logger.Debugw("failed to get state miner info", "err", t.Err)
t.Status = StatusInternalError
return t
}
// Check if the target is known by the indexer now that it has a non-nil addrinfo.
var err error
t.KnownByIndexer, err = t.hf.isKnownByIndexer(ctx, t.AddrInfo.ID)
if err != nil {
logger.Errorw("failed to check if target is known by indexer", "err", err)
}
if t.Err = t.hf.h.Connect(ctx, *t.AddrInfo); t.Err != nil {
t.Status = StatusUnreachable
return t
}
if t.Protocols, t.Err = t.hf.h.Peerstore().GetProtocols(t.AddrInfo.ID); t.Err != nil {
t.Status = StatusUnreachable
return t
}
// Get the remote peer agent version, but proceed with other checks if we fail to get it.
var ok bool
if anyAgentVersion, err := t.hf.h.Peerstore().Get(t.AddrInfo.ID, "AgentVersion"); err != nil {
logger.Warnw("Failed to get agent version", "err", err)
} else if t.AgentVersion, ok = anyAgentVersion.(string); ok {
t.AgentShortVersion = agentShortVersionPattern.ReplaceAllString(t.AgentVersion, "$1")
} else if !ok {
logger.Warnw("Non-string agent version", "agentVersion", anyAgentVersion)
}
if t.supportsProtocolID(transportsProtocolID) {
// Do not populate t.Err with the error that may be returned by queryTransports.
// Instead, silently proceed to IPNI related head checking, etc.
tctx, cancel := context.WithTimeout(ctx, t.hf.queryTransportsTimeout)
defer cancel()
if t.Transports, err = t.hf.queryTransports(tctx, t.AddrInfo.ID); err != nil {
logger.Warnw("Failed to query transports", "err", err)
}
}
// Check if the target is an index provider on the expected topic.
var supportsHeadProtocol bool
for _, pid := range t.Protocols {
if supportsHeadProtocol, t.Topic = t.hf.findHeadProtocolMatches(string(pid)); supportsHeadProtocol {
t.HeadProtocol = pid
break
}
}
if !supportsHeadProtocol {
t.Status = StatusUnindexed
return t
}
if t.Topic != t.hf.topic {
t.Status = StatusTopicMismatch
return t
}
// Check if there is a non-empty head CID and if so announce it.
switch t.Head, t.Err = t.hf.getHead(ctx, t.AddrInfo, t.HeadProtocol); {
case t.Err != nil:
t.Status = StatusGetHeadError
case cid.Undef.Equals(t.Head):
t.Status = StatusEmptyHead
default:
if t.Err = t.hf.announce(ctx, t.AddrInfo, t.Head); t.Err != nil {
t.Status = StatusAnnounceError
} else {
t.Status = StatusOK
}
}
return t
}
func (t *Target) hasPeerID(pid peer.ID) bool {
switch {
case t.AddrInfo != nil && t.AddrInfo.ID == pid:
return true
case t.Transports != nil:
for _, tp := range t.Transports.Protocols {
for _, ma := range tp.Addresses {
if addr, err := peer.AddrInfoFromP2pAddr(ma); err != nil {
continue
} else if addr.ID == pid {
return true
}
}
}
}
return false
}
func (t *Target) supportsProtocolID(pid protocol.ID) bool {
for _, id := range t.Protocols {
if id == pid {
return true
}
}
return false
}