github.com/mavryk-network/mvgo@v1.19.9/rpc/monitor.go (about) 1 // Copyright (c) 2020-2022 Blockwatch Data Inc. 2 // Author: alex@blockwatch.cc 3 4 package rpc 5 6 import ( 7 "context" 8 "encoding/binary" 9 "errors" 10 "io" 11 "time" 12 13 "github.com/mavryk-network/mvgo/mavryk" 14 ) 15 16 var ErrMonitorClosed = errors.New("monitor closed") 17 18 type Monitor interface { 19 New() interface{} 20 Send(ctx context.Context, val interface{}) 21 Err(error) 22 Closed() <-chan struct{} 23 Close() 24 } 25 26 // BootstrappedBlock represents bootstrapped block stream message 27 type BootstrappedBlock struct { 28 Block mavryk.BlockHash `json:"block"` 29 Timestamp time.Time `json:"timestamp"` 30 } 31 32 type BootstrapMonitor struct { 33 result chan *BootstrappedBlock 34 closed chan struct{} 35 err error 36 } 37 38 // make sure BootstrapMonitor implements Monitor interface 39 var _ Monitor = (*BootstrapMonitor)(nil) 40 41 func NewBootstrapMonitor() *BootstrapMonitor { 42 return &BootstrapMonitor{ 43 result: make(chan *BootstrappedBlock), 44 closed: make(chan struct{}), 45 } 46 } 47 48 func (m *BootstrapMonitor) New() interface{} { 49 return &BootstrappedBlock{} 50 } 51 52 func (m *BootstrapMonitor) Send(ctx context.Context, val interface{}) { 53 select { 54 case <-m.closed: 55 return 56 default: 57 } 58 select { 59 case <-ctx.Done(): 60 case <-m.closed: 61 case m.result <- val.(*BootstrappedBlock): 62 } 63 } 64 65 func (m *BootstrapMonitor) Recv(ctx context.Context) (*BootstrappedBlock, error) { 66 select { 67 case <-ctx.Done(): 68 return nil, ctx.Err() 69 case <-m.closed: 70 err := m.err 71 if err == nil { 72 err = ErrMonitorClosed 73 } 74 return nil, err 75 case res, ok := <-m.result: 76 if !ok { 77 if m.err != nil { 78 return nil, m.err 79 } 80 return nil, io.EOF 81 } 82 return res, nil 83 } 84 } 85 86 func (m *BootstrapMonitor) Err(err error) { 87 m.err = err 88 m.Close() 89 } 90 91 func (m *BootstrapMonitor) Closed() <-chan struct{} { 92 return m.closed 93 } 94 95 func (m *BootstrapMonitor) Close() { 96 select { 97 case <-m.closed: 98 return 99 default: 100 } 101 close(m.closed) 102 close(m.result) 103 } 104 105 // BlockHeaderLogEntry is a log entry returned for a new block when monitoring 106 type BlockHeaderLogEntry struct { 107 Hash mavryk.BlockHash `json:"hash"` 108 Level int64 `json:"level"` 109 Proto int `json:"proto"` 110 Predecessor mavryk.BlockHash `json:"predecessor"` 111 Timestamp time.Time `json:"timestamp"` 112 ValidationPass int `json:"validation_pass"` 113 OperationsHash mavryk.OpListListHash `json:"operations_hash"` 114 Fitness []mavryk.HexBytes `json:"fitness"` 115 Context mavryk.ContextHash `json:"context"` 116 ProtocolData mavryk.HexBytes `json:"protocol_data"` 117 } 118 119 func (h *BlockHeader) LogEntry() *BlockHeaderLogEntry { 120 return &BlockHeaderLogEntry{ 121 Hash: h.Hash, 122 Level: h.Level, 123 Proto: h.Proto, 124 Predecessor: h.Predecessor, 125 Timestamp: h.Timestamp, 126 ValidationPass: h.ValidationPass, 127 OperationsHash: h.OperationsHash, 128 Fitness: h.Fitness, 129 Context: h.Context, 130 ProtocolData: mavryk.HexBytes(h.ProtocolData()), 131 } 132 } 133 134 func (l BlockHeaderLogEntry) Round() int { 135 return int(binary.BigEndian.Uint32(l.ProtocolData[32:])) 136 } 137 138 func (l BlockHeaderLogEntry) PayloadHash() (h mavryk.PayloadHash) { 139 copy(h[:], l.ProtocolData[:]) 140 return 141 } 142 143 func (l BlockHeaderLogEntry) Pow() (h mavryk.HexBytes) { 144 h.UnmarshalBinary(l.ProtocolData[36:44]) 145 return 146 } 147 148 func (b *Block) LogEntry() *BlockHeaderLogEntry { 149 e := b.Header.LogEntry() 150 e.Hash = b.Hash 151 return e 152 } 153 154 type BlockHeaderMonitor struct { 155 result chan *BlockHeaderLogEntry 156 closed chan struct{} 157 err error 158 } 159 160 // make sure BlockHeaderMonitor implements Monitor interface 161 var _ Monitor = (*BlockHeaderMonitor)(nil) 162 163 func NewBlockHeaderMonitor() *BlockHeaderMonitor { 164 return &BlockHeaderMonitor{ 165 result: make(chan *BlockHeaderLogEntry), 166 closed: make(chan struct{}), 167 } 168 } 169 170 func (m *BlockHeaderMonitor) New() interface{} { 171 return &BlockHeaderLogEntry{} 172 } 173 174 func (m *BlockHeaderMonitor) Send(ctx context.Context, val interface{}) { 175 select { 176 case <-m.closed: 177 return 178 default: 179 } 180 select { 181 case <-ctx.Done(): 182 case <-m.closed: 183 case m.result <- val.(*BlockHeaderLogEntry): 184 } 185 } 186 187 func (m *BlockHeaderMonitor) Recv(ctx context.Context) (*BlockHeaderLogEntry, error) { 188 select { 189 case <-ctx.Done(): 190 return nil, ctx.Err() 191 case <-m.closed: 192 err := m.err 193 if err == nil { 194 err = ErrMonitorClosed 195 } 196 return nil, err 197 case res, ok := <-m.result: 198 if !ok { 199 if m.err != nil { 200 return nil, m.err 201 } 202 return nil, io.EOF 203 } 204 return res, nil 205 } 206 } 207 208 func (m *BlockHeaderMonitor) Err(err error) { 209 m.err = err 210 m.Close() 211 } 212 213 func (m *BlockHeaderMonitor) Close() { 214 select { 215 case <-m.closed: 216 return 217 default: 218 } 219 close(m.closed) 220 close(m.result) 221 } 222 223 func (m *BlockHeaderMonitor) Closed() <-chan struct{} { 224 return m.closed 225 } 226 227 // MempoolMonitor is a monitor for the Tezos mempool. Note that the connection 228 // resets every time a new head is attached to the chain. MempoolMonitor is 229 // closed with an error in this case and cannot be reused after close. 230 // 231 // The Tezos mempool re-evaluates all operations and potentially updates their state 232 // when the head block changes. This applies to operations in lists branch_delayed 233 // and branch_refused. After reorg, operations already included in a previous block 234 // may enter the mempool again. 235 type MempoolMonitor struct { 236 result chan *[]*Operation 237 closed chan struct{} 238 err error 239 } 240 241 // make sure MempoolMonitor implements Monitor interface 242 var _ Monitor = (*MempoolMonitor)(nil) 243 244 func NewMempoolMonitor() *MempoolMonitor { 245 return &MempoolMonitor{ 246 result: make(chan *[]*Operation), 247 closed: make(chan struct{}), 248 } 249 } 250 251 func (m *MempoolMonitor) New() interface{} { 252 slice := make([]*Operation, 0) 253 return &slice 254 } 255 256 func (m *MempoolMonitor) Send(ctx context.Context, val interface{}) { 257 select { 258 case <-m.closed: 259 return 260 default: 261 } 262 select { 263 case <-ctx.Done(): 264 case <-m.closed: 265 case m.result <- val.(*[]*Operation): 266 } 267 } 268 269 func (m *MempoolMonitor) Recv(ctx context.Context) ([]*Operation, error) { 270 select { 271 case <-ctx.Done(): 272 return nil, ctx.Err() 273 case <-m.closed: 274 err := m.err 275 if err == nil { 276 err = ErrMonitorClosed 277 } 278 return nil, err 279 case res, ok := <-m.result: 280 if !ok { 281 if m.err != nil { 282 return nil, m.err 283 } 284 return nil, io.EOF 285 } 286 return *res, nil 287 } 288 } 289 290 func (m *MempoolMonitor) Err(err error) { 291 m.err = err 292 m.Close() 293 } 294 295 func (m *MempoolMonitor) Close() { 296 select { 297 case <-m.closed: 298 return 299 default: 300 } 301 close(m.closed) 302 close(m.result) 303 } 304 305 func (m *MempoolMonitor) Closed() <-chan struct{} { 306 return m.closed 307 } 308 309 // NetworkPeerLogEntry represents peer log entry 310 type NetworkPeerLogEntry struct { 311 NetworkAddress 312 Kind string `json:"kind"` 313 Timestamp time.Time `json:"timestamp"` 314 } 315 316 type NetworkPeerMonitor struct { 317 result chan *NetworkPeerLogEntry 318 closed chan struct{} 319 err error 320 } 321 322 // make sure NetworkPeerMonitor implements Monitor interface 323 var _ Monitor = (*NetworkPeerMonitor)(nil) 324 325 func NewNetworkPeerMonitor() *NetworkPeerMonitor { 326 return &NetworkPeerMonitor{ 327 result: make(chan *NetworkPeerLogEntry), 328 closed: make(chan struct{}), 329 } 330 } 331 332 func (m *NetworkPeerMonitor) New() interface{} { 333 return &NetworkPeerLogEntry{} 334 } 335 336 func (m *NetworkPeerMonitor) Send(ctx context.Context, val interface{}) { 337 select { 338 case <-m.closed: 339 return 340 default: 341 } 342 select { 343 case <-ctx.Done(): 344 case <-m.closed: 345 case m.result <- val.(*NetworkPeerLogEntry): 346 } 347 } 348 349 func (m *NetworkPeerMonitor) Recv(ctx context.Context) (*NetworkPeerLogEntry, error) { 350 select { 351 case <-ctx.Done(): 352 return nil, ctx.Err() 353 case <-m.closed: 354 err := m.err 355 if err == nil { 356 err = ErrMonitorClosed 357 } 358 return nil, err 359 case res, ok := <-m.result: 360 if !ok { 361 if m.err != nil { 362 return nil, m.err 363 } 364 return nil, io.EOF 365 } 366 return res, nil 367 } 368 } 369 370 func (m *NetworkPeerMonitor) Err(err error) { 371 m.err = err 372 m.Close() 373 } 374 375 func (m *NetworkPeerMonitor) Close() { 376 select { 377 case <-m.closed: 378 return 379 default: 380 } 381 close(m.closed) 382 close(m.result) 383 } 384 385 func (m *NetworkPeerMonitor) Closed() <-chan struct{} { 386 return m.closed 387 } 388 389 // NetworkPointLogEntry represents point's log entry 390 type NetworkPointLogEntry struct { 391 Kind NetworkPointState `json:"kind"` 392 Timestamp time.Time `json:"timestamp"` 393 } 394 395 type NetworkPointMonitor struct { 396 result chan *NetworkPointLogEntry 397 closed chan struct{} 398 err error 399 } 400 401 // make sure NetworkPointMonitor implements Monitor interface 402 var _ Monitor = (*NetworkPointMonitor)(nil) 403 404 func NewNetworkPointMonitor() *NetworkPointMonitor { 405 return &NetworkPointMonitor{ 406 result: make(chan *NetworkPointLogEntry), 407 closed: make(chan struct{}), 408 } 409 } 410 411 func (m *NetworkPointMonitor) New() interface{} { 412 return &NetworkPointLogEntry{} 413 } 414 415 func (m *NetworkPointMonitor) Send(ctx context.Context, val interface{}) { 416 select { 417 case <-m.closed: 418 return 419 default: 420 } 421 select { 422 case <-ctx.Done(): 423 case <-m.closed: 424 case m.result <- val.(*NetworkPointLogEntry): 425 } 426 } 427 428 func (m *NetworkPointMonitor) Recv(ctx context.Context) (*NetworkPointLogEntry, error) { 429 select { 430 case <-ctx.Done(): 431 return nil, ctx.Err() 432 case <-m.closed: 433 err := m.err 434 if err == nil { 435 err = ErrMonitorClosed 436 } 437 return nil, err 438 case res, ok := <-m.result: 439 if !ok { 440 if m.err != nil { 441 return nil, m.err 442 } 443 return nil, io.EOF 444 } 445 return res, nil 446 } 447 } 448 449 func (m *NetworkPointMonitor) Err(err error) { 450 m.err = err 451 m.Close() 452 } 453 454 func (m *NetworkPointMonitor) Close() { 455 select { 456 case <-m.closed: 457 return 458 default: 459 } 460 close(m.closed) 461 close(m.result) 462 } 463 464 func (m *NetworkPointMonitor) Closed() <-chan struct{} { 465 return m.closed 466 } 467 468 // MonitorBootstrapped reads from the bootstrapped blocks stream http://protocol.mavryk.org/mainnet/api/rpc.html#get-monitor-bootstrapped 469 func (c *Client) MonitorBootstrapped(ctx context.Context, monitor *BootstrapMonitor) error { 470 return c.GetAsync(ctx, "monitor/bootstrapped", monitor) 471 } 472 473 // MonitorBlockHeader reads from the chain heads stream http://protocol.mavryk.org/mainnet/api/rpc.html#get-monitor-heads-chain-id 474 func (c *Client) MonitorBlockHeader(ctx context.Context, monitor *BlockHeaderMonitor) error { 475 return c.GetAsync(ctx, "monitor/heads/main", monitor) 476 } 477 478 // MonitorMempool reads from the chain heads stream http://protocol.mavryk.org/mainnet/api/rpc.html#get-monitor-heads-chain-id 479 func (c *Client) MonitorMempool(ctx context.Context, monitor *MempoolMonitor) error { 480 return c.GetAsync(ctx, "chains/main/mempool/monitor_operations", monitor) 481 } 482 483 // MonitorNetworkPointLog monitors network events related to an `IP:addr`. 484 // https://protocol.mavryk.org/mainnet/api/rpc.html#get-network-peers-peer-id-log 485 func (c *Client) MonitorNetworkPointLog(ctx context.Context, address string, monitor *NetworkPointMonitor) error { 486 return c.GetAsync(ctx, "network/points/"+address+"/log?monitor", monitor) 487 } 488 489 // MonitorNetworkPeerLog monitors network events related to a given peer. 490 // https://protocol.mavryk.org/mainnet/api/rpc.html#get-network-peers-peer-id-log 491 func (c *Client) MonitorNetworkPeerLog(ctx context.Context, peerID string, monitor *NetworkPeerMonitor) error { 492 return c.GetAsync(ctx, "network/peers/"+peerID+"/log?monitor", monitor) 493 }