git.gammaspectra.live/P2Pool/p2pool-observer@v0.0.0-20240403172525-d7dfbf7231c8/p2pool/api/p2poolapi.go (about) 1 package api 2 3 import ( 4 "bytes" 5 "errors" 6 "git.gammaspectra.live/P2Pool/p2pool-observer/monero/block" 7 "git.gammaspectra.live/P2Pool/p2pool-observer/monero/randomx" 8 "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain" 9 p2pooltypes "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/types" 10 "git.gammaspectra.live/P2Pool/p2pool-observer/types" 11 "git.gammaspectra.live/P2Pool/p2pool-observer/utils" 12 "github.com/floatdrop/lru" 13 "io" 14 "net/http" 15 "net/netip" 16 "net/url" 17 "strconv" 18 "sync/atomic" 19 "time" 20 ) 21 22 type P2PoolApi struct { 23 Host string 24 Client *http.Client 25 consensus atomic.Pointer[sidechain.Consensus] 26 derivationCache sidechain.DerivationCacheInterface 27 difficultyByHeightCache *lru.LRU[uint64, types.Difficulty] 28 } 29 30 func NewP2PoolApi(host string) *P2PoolApi { 31 return &P2PoolApi{ 32 Host: host, 33 Client: &http.Client{ 34 Timeout: time.Second * 15, 35 }, 36 derivationCache: sidechain.NewDerivationLRUCache(), 37 difficultyByHeightCache: lru.New[uint64, types.Difficulty](1024), 38 } 39 } 40 41 func (p *P2PoolApi) WaitSync() (err error) { 42 defer func() { 43 if r := recover(); r != nil { 44 err = errors.New("panicked") 45 } 46 }() 47 status := p.Status() 48 for ; p == nil || !p.Status().Synchronized; status = p.Status() { 49 if p == nil { 50 utils.Logf("API", "Not synchronized (nil), waiting five seconds") 51 } else { 52 utils.Logf("API", "Not synchronized (height %d, id %s, blocks %d), waiting five seconds", status.Height, status.Id, status.Blocks) 53 } 54 time.Sleep(time.Second * 5) 55 } 56 utils.Logf("API", "SYNCHRONIZED (height %d, id %s, blocks %d)", status.Height, status.Id, status.Blocks) 57 utils.Logf("API", "Consensus id = %s\n", p.Consensus().Id) 58 return nil 59 } 60 61 func (p *P2PoolApi) WaitSyncStart() (err error) { 62 defer func() { 63 if r := recover(); r != nil { 64 err = errors.New("panicked") 65 } 66 }() 67 status := p.Status() 68 for ; p == nil || !p.Status().Synchronized; status = p.Status() { 69 if p == nil { 70 utils.Logf("API", "Not synchronized (nil), waiting one seconds") 71 } else { 72 utils.Logf("API", "Not synchronized (height %d, id %s, blocks %d)", status.Height, status.Id, status.Blocks) 73 break 74 } 75 time.Sleep(time.Second * 1) 76 } 77 if status.Synchronized { 78 utils.Logf("API", "SYNCHRONIZED (height %d, id %s, blocks %d)", status.Height, status.Id, status.Blocks) 79 } 80 utils.Logf("API", "Consensus id = %s\n", p.Consensus().Id) 81 return nil 82 } 83 84 func (p *P2PoolApi) InsertAlternate(b *sidechain.PoolBlock) { 85 buf, _ := b.MarshalBinary() 86 uri, _ := url.Parse(p.Host + "/archive/insert_alternate") 87 response, err := p.Client.Do(&http.Request{ 88 Method: "POST", 89 URL: uri, 90 Body: io.NopCloser(bytes.NewReader(buf)), 91 }) 92 if err != nil { 93 return 94 } 95 defer response.Body.Close() 96 } 97 98 func (p *P2PoolApi) LightByMainId(id types.Hash) *sidechain.PoolBlock { 99 if response, err := p.Client.Get(p.Host + "/archive/light_block_by_main_id/" + id.String()); err != nil { 100 return nil 101 } else { 102 defer response.Body.Close() 103 104 if buf, err := io.ReadAll(response.Body); err != nil { 105 return nil 106 } else { 107 b := &sidechain.PoolBlock{} 108 109 if err = utils.UnmarshalJSON(buf, &b); err != nil || b.ShareVersion() == sidechain.ShareVersion_None { 110 return nil 111 } 112 113 return b 114 } 115 } 116 } 117 118 func (p *P2PoolApi) LightByMainIdWithHint(id, templateIdHint types.Hash) *sidechain.PoolBlock { 119 if response, err := p.Client.Get(p.Host + "/archive/light_block_by_main_id/" + id.String() + "?templateIdHint=" + templateIdHint.String()); err != nil { 120 return nil 121 } else { 122 defer response.Body.Close() 123 124 if buf, err := io.ReadAll(response.Body); err != nil { 125 return nil 126 } else { 127 b := &sidechain.PoolBlock{} 128 129 if err = utils.UnmarshalJSON(buf, &b); err != nil || b.ShareVersion() == sidechain.ShareVersion_None { 130 return nil 131 } 132 133 return b 134 } 135 } 136 } 137 138 func (p *P2PoolApi) ByMainId(id types.Hash) *sidechain.PoolBlock { 139 if response, err := p.Client.Get(p.Host + "/archive/block_by_main_id/" + id.String()); err != nil { 140 return nil 141 } else { 142 defer response.Body.Close() 143 144 if buf, err := io.ReadAll(response.Body); err != nil { 145 return nil 146 } else { 147 var result p2pooltypes.P2PoolBinaryBlockResult 148 149 if err = utils.UnmarshalJSON(buf, &result); err != nil || result.Version == 0 { 150 return nil 151 } 152 153 b := &sidechain.PoolBlock{} 154 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, result.Blob); err != nil || int(b.ShareVersion()) != result.Version { 155 return nil 156 } 157 return b 158 } 159 } 160 } 161 162 func (p *P2PoolApi) ByMainIdWithHint(id, templateIdHint types.Hash) *sidechain.PoolBlock { 163 if response, err := p.Client.Get(p.Host + "/archive/block_by_main_id/" + id.String() + "?templateIdHint=" + templateIdHint.String()); err != nil { 164 return nil 165 } else { 166 defer response.Body.Close() 167 168 if buf, err := io.ReadAll(response.Body); err != nil { 169 return nil 170 } else { 171 var result p2pooltypes.P2PoolBinaryBlockResult 172 173 if err = utils.UnmarshalJSON(buf, &result); err != nil || result.Version == 0 { 174 return nil 175 } 176 177 b := &sidechain.PoolBlock{} 178 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, result.Blob); err != nil || int(b.ShareVersion()) != result.Version { 179 return nil 180 } 181 return b 182 } 183 } 184 } 185 186 func (p *P2PoolApi) LightByTemplateId(id types.Hash) sidechain.UniquePoolBlockSlice { 187 if response, err := p.Client.Get(p.Host + "/archive/light_blocks_by_template_id/" + id.String()); err != nil { 188 return nil 189 } else { 190 defer response.Body.Close() 191 192 if buf, err := io.ReadAll(response.Body); err != nil { 193 return nil 194 } else { 195 var result sidechain.UniquePoolBlockSlice 196 197 if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 { 198 return nil 199 } 200 201 return result 202 } 203 } 204 } 205 206 func (p *P2PoolApi) ByTemplateId(id types.Hash) *sidechain.PoolBlock { 207 if response, err := p.Client.Get(p.Host + "/sidechain/block_by_template_id/" + id.String()); err != nil { 208 return nil 209 } else { 210 defer response.Body.Close() 211 212 if buf, err := io.ReadAll(response.Body); err != nil { 213 return nil 214 } else { 215 var result p2pooltypes.P2PoolBinaryBlockResult 216 217 if err = utils.UnmarshalJSON(buf, &result); err != nil { 218 return nil 219 } else if result.Version == 0 { 220 // Fallback into archive 221 if response, err := p.Client.Get(p.Host + "/archive/blocks_by_template_id/" + id.String()); err != nil { 222 return nil 223 } else { 224 defer response.Body.Close() 225 226 if buf, err := io.ReadAll(response.Body); err != nil { 227 return nil 228 } else { 229 var result []p2pooltypes.P2PoolBinaryBlockResult 230 231 if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 { 232 return nil 233 } 234 235 for _, r := range result { 236 //Get first block that matches 237 if r.Version == 0 { 238 continue 239 } 240 b := &sidechain.PoolBlock{} 241 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil || int(b.ShareVersion()) != r.Version { 242 continue 243 } 244 return b 245 } 246 return nil 247 } 248 } 249 } 250 251 b := &sidechain.PoolBlock{} 252 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, result.Blob); err != nil { 253 return nil 254 } 255 return b 256 } 257 } 258 } 259 260 func (p *P2PoolApi) LightBySideHeight(height uint64) sidechain.UniquePoolBlockSlice { 261 if response, err := p.Client.Get(p.Host + "/archive/light_blocks_by_side_height/" + strconv.FormatUint(height, 10)); err != nil { 262 return nil 263 } else { 264 defer response.Body.Close() 265 266 if buf, err := io.ReadAll(response.Body); err != nil { 267 return nil 268 } else { 269 var result sidechain.UniquePoolBlockSlice 270 271 if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 { 272 return nil 273 } 274 275 return result 276 } 277 } 278 } 279 280 func (p *P2PoolApi) BySideHeight(height uint64) sidechain.UniquePoolBlockSlice { 281 if response, err := p.Client.Get(p.Host + "/sidechain/blocks_by_height/" + strconv.FormatUint(height, 10)); err != nil { 282 return nil 283 } else { 284 defer response.Body.Close() 285 286 if buf, err := io.ReadAll(response.Body); err != nil { 287 return nil 288 } else { 289 var result []p2pooltypes.P2PoolBinaryBlockResult 290 291 if err = utils.UnmarshalJSON(buf, &result); err != nil { 292 return nil 293 } else if len(result) == 0 { 294 // Fallback into archive 295 if response, err := p.Client.Get(p.Host + "/archive/blocks_by_side_height/" + strconv.FormatUint(height, 10)); err != nil { 296 return nil 297 } else { 298 defer response.Body.Close() 299 300 if buf, err := io.ReadAll(response.Body); err != nil { 301 return nil 302 } else { 303 var result []p2pooltypes.P2PoolBinaryBlockResult 304 305 if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 { 306 return nil 307 } 308 309 results := make([]*sidechain.PoolBlock, 0, len(result)) 310 for _, r := range result { 311 if r.Version == 0 { 312 return nil 313 } 314 b := &sidechain.PoolBlock{} 315 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 316 return nil 317 } 318 results = append(results, b) 319 } 320 return results 321 } 322 } 323 } 324 325 results := make([]*sidechain.PoolBlock, 0, len(result)) 326 for _, r := range result { 327 if r.Version == 0 { 328 return nil 329 } 330 b := &sidechain.PoolBlock{} 331 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 332 return nil 333 } 334 results = append(results, b) 335 } 336 return results 337 } 338 } 339 } 340 341 func (p *P2PoolApi) LightByMainHeight(height uint64) sidechain.UniquePoolBlockSlice { 342 if response, err := p.Client.Get(p.Host + "/archive/light_blocks_by_main_height/" + strconv.FormatUint(height, 10)); err != nil { 343 return nil 344 } else { 345 defer response.Body.Close() 346 347 if buf, err := io.ReadAll(response.Body); err != nil { 348 return nil 349 } else { 350 var result sidechain.UniquePoolBlockSlice 351 352 if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 { 353 return nil 354 } 355 356 return result 357 } 358 } 359 } 360 361 func (p *P2PoolApi) ByMainHeight(height uint64) sidechain.UniquePoolBlockSlice { 362 if response, err := p.Client.Get(p.Host + "/archive/blocks_by_main_height/" + strconv.FormatUint(height, 10)); err != nil { 363 return nil 364 } else { 365 defer response.Body.Close() 366 367 if buf, err := io.ReadAll(response.Body); err != nil { 368 return nil 369 } else { 370 var result []p2pooltypes.P2PoolBinaryBlockResult 371 372 if err = utils.UnmarshalJSON(buf, &result); err != nil || len(result) == 0 { 373 return nil 374 } 375 376 results := make([]*sidechain.PoolBlock, 0, len(result)) 377 for _, r := range result { 378 if r.Version == 0 { 379 return nil 380 } 381 b := &sidechain.PoolBlock{} 382 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 383 return nil 384 } 385 results = append(results, b) 386 } 387 return results 388 } 389 } 390 } 391 392 func (p *P2PoolApi) DifficultyByHeight(height uint64) types.Difficulty { 393 if v := p.difficultyByHeightCache.Get(height); v == nil { 394 if diff := p.MainDifficultyByHeight(height); diff != types.ZeroDifficulty { 395 p.difficultyByHeightCache.Set(height, diff) 396 return diff 397 } 398 return types.ZeroDifficulty 399 } else { 400 return *v 401 } 402 } 403 404 func (p *P2PoolApi) SeedByHeight(height uint64) types.Hash { 405 seedHeight := randomx.SeedHeight(height) 406 if v := p.MainHeaderByHeight(seedHeight); v != nil { 407 return v.Id 408 } 409 return types.ZeroHash 410 } 411 412 func (p *P2PoolApi) PeerList() []byte { 413 if response, err := p.Client.Get(p.Host + "/server/peerlist"); err != nil { 414 return nil 415 } else { 416 defer response.Body.Close() 417 buf, err := io.ReadAll(response.Body) 418 if err == nil { 419 return buf 420 } 421 } 422 423 return nil 424 } 425 426 func (p *P2PoolApi) ConnectionCheck(addrPort netip.AddrPort) *p2pooltypes.P2PoolConnectionCheckInformation[*sidechain.PoolBlock] { 427 if response, err := p.Client.Get(p.Host + "/server/connection_check/" + addrPort.String()); err != nil { 428 return nil 429 } else { 430 defer response.Body.Close() 431 432 if buf, err := io.ReadAll(response.Body); err != nil { 433 return nil 434 } else { 435 var result p2pooltypes.P2PoolConnectionCheckInformation[*sidechain.PoolBlock] 436 437 if err = utils.UnmarshalJSON(buf, &result); err != nil { 438 return nil 439 } 440 441 return &result 442 } 443 } 444 } 445 446 func (p *P2PoolApi) MinerData() *p2pooltypes.MinerData { 447 if response, err := p.Client.Get(p.Host + "/mainchain/miner_data"); err != nil { 448 return nil 449 } else { 450 defer response.Body.Close() 451 452 if buf, err := io.ReadAll(response.Body); err != nil { 453 return nil 454 } else { 455 var result p2pooltypes.MinerData 456 457 if err = utils.UnmarshalJSON(buf, &result); err != nil { 458 return nil 459 } 460 461 return &result 462 } 463 } 464 } 465 466 func (p *P2PoolApi) MainTip() *block.Header { 467 if response, err := p.Client.Get(p.Host + "/mainchain/tip"); err != nil { 468 return nil 469 } else { 470 defer response.Body.Close() 471 472 if buf, err := io.ReadAll(response.Body); err != nil { 473 return nil 474 } else { 475 var result block.Header 476 477 if err = utils.UnmarshalJSON(buf, &result); err != nil { 478 return nil 479 } 480 481 return &result 482 } 483 } 484 } 485 486 func (p *P2PoolApi) MainHeaderById(id types.Hash) *block.Header { 487 if response, err := p.Client.Get(p.Host + "/mainchain/header_by_id/" + id.String()); err != nil { 488 return nil 489 } else { 490 defer response.Body.Close() 491 492 if buf, err := io.ReadAll(response.Body); err != nil { 493 return nil 494 } else { 495 var result block.Header 496 497 if err = utils.UnmarshalJSON(buf, &result); err != nil { 498 return nil 499 } 500 501 return &result 502 } 503 } 504 } 505 506 func (p *P2PoolApi) MainHeaderByHeight(height uint64) *block.Header { 507 if response, err := p.Client.Get(p.Host + "/mainchain/header_by_height/" + strconv.FormatUint(height, 10)); err != nil { 508 return nil 509 } else { 510 defer response.Body.Close() 511 512 if buf, err := io.ReadAll(response.Body); err != nil { 513 return nil 514 } else { 515 var result block.Header 516 517 if err = utils.UnmarshalJSON(buf, &result); err != nil { 518 return nil 519 } 520 521 return &result 522 } 523 } 524 } 525 526 func (p *P2PoolApi) MainDifficultyByHeight(height uint64) types.Difficulty { 527 if response, err := p.Client.Get(p.Host + "/mainchain/difficulty_by_height/" + strconv.FormatUint(height, 10)); err != nil { 528 return types.ZeroDifficulty 529 } else { 530 defer response.Body.Close() 531 532 if buf, err := io.ReadAll(response.Body); err != nil { 533 return types.ZeroDifficulty 534 } else { 535 var result types.Difficulty 536 537 if err = utils.UnmarshalJSON(buf, &result); err != nil { 538 return types.ZeroDifficulty 539 } 540 541 return result 542 } 543 } 544 } 545 546 func (p *P2PoolApi) StateFromTemplateId(id types.Hash) (chain, uncles sidechain.UniquePoolBlockSlice) { 547 if response, err := p.Client.Get(p.Host + "/sidechain/state/" + id.String()); err != nil { 548 return nil, nil 549 } else { 550 defer response.Body.Close() 551 552 if buf, err := io.ReadAll(response.Body); err != nil { 553 return nil, nil 554 } else { 555 var result p2pooltypes.P2PoolSideChainStateResult 556 557 if err = utils.UnmarshalJSON(buf, &result); err != nil { 558 return nil, nil 559 } 560 561 chain = make([]*sidechain.PoolBlock, 0, len(result.Chain)) 562 uncles = make([]*sidechain.PoolBlock, 0, len(result.Uncles)) 563 564 for _, r := range result.Chain { 565 b := &sidechain.PoolBlock{} 566 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 567 return nil, nil 568 } 569 chain = append(chain, b) 570 } 571 572 for _, r := range result.Uncles { 573 b := &sidechain.PoolBlock{} 574 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 575 return nil, nil 576 } 577 uncles = append(uncles, b) 578 } 579 580 return chain, uncles 581 } 582 } 583 } 584 585 func (p *P2PoolApi) WindowFromTemplateId(id types.Hash) (chain, uncles sidechain.UniquePoolBlockSlice) { 586 if response, err := p.Client.Get(p.Host + "/archive/window_from_template_id/" + id.String()); err != nil { 587 return nil, nil 588 } else { 589 defer response.Body.Close() 590 591 if buf, err := io.ReadAll(response.Body); err != nil { 592 return nil, nil 593 } else { 594 var result p2pooltypes.P2PoolSideChainStateResult 595 596 if err = utils.UnmarshalJSON(buf, &result); err != nil { 597 return nil, nil 598 } 599 600 chain = make([]*sidechain.PoolBlock, 0, len(result.Chain)) 601 uncles = make([]*sidechain.PoolBlock, 0, len(result.Uncles)) 602 603 for _, r := range result.Chain { 604 b := &sidechain.PoolBlock{} 605 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 606 return nil, nil 607 } 608 chain = append(chain, b) 609 } 610 611 for _, r := range result.Uncles { 612 b := &sidechain.PoolBlock{} 613 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 614 return nil, nil 615 } 616 uncles = append(uncles, b) 617 } 618 619 return chain, uncles 620 } 621 } 622 } 623 624 func (p *P2PoolApi) StateFromTip() (chain, uncles sidechain.UniquePoolBlockSlice) { 625 if response, err := p.Client.Get(p.Host + "/sidechain/state/tip"); err != nil { 626 return nil, nil 627 } else { 628 defer response.Body.Close() 629 630 if buf, err := io.ReadAll(response.Body); err != nil { 631 return nil, nil 632 } else { 633 var result p2pooltypes.P2PoolSideChainStateResult 634 635 if err = utils.UnmarshalJSON(buf, &result); err != nil { 636 return nil, nil 637 } 638 639 chain = make([]*sidechain.PoolBlock, 0, len(result.Chain)) 640 uncles = make([]*sidechain.PoolBlock, 0, len(result.Uncles)) 641 642 for _, r := range result.Chain { 643 b := &sidechain.PoolBlock{} 644 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 645 return nil, nil 646 } 647 chain = append(chain, b) 648 } 649 650 for _, r := range result.Uncles { 651 b := &sidechain.PoolBlock{} 652 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, r.Blob); err != nil { 653 return nil, nil 654 } 655 uncles = append(uncles, b) 656 } 657 658 return chain, uncles 659 } 660 } 661 } 662 663 func (p *P2PoolApi) Tip() *sidechain.PoolBlock { 664 if response, err := p.Client.Get(p.Host + "/sidechain/tip"); err != nil { 665 return nil 666 } else { 667 defer response.Body.Close() 668 669 if buf, err := io.ReadAll(response.Body); err != nil { 670 return nil 671 } else { 672 var result p2pooltypes.P2PoolBinaryBlockResult 673 674 if err = utils.UnmarshalJSON(buf, &result); err != nil { 675 return nil 676 } 677 678 if result.Version == 0 { 679 return nil 680 } 681 b := &sidechain.PoolBlock{} 682 if err = b.UnmarshalBinary(p.Consensus(), p.derivationCache, result.Blob); err != nil { 683 return nil 684 } 685 return b 686 } 687 } 688 } 689 690 func (p *P2PoolApi) getConsensus() *sidechain.Consensus { 691 if response, err := p.Client.Get(p.Host + "/sidechain/consensus"); err != nil { 692 return nil 693 } else { 694 defer response.Body.Close() 695 696 if buf, err := io.ReadAll(response.Body); err != nil { 697 return nil 698 } else { 699 c, _ := sidechain.NewConsensusFromJSON(buf) 700 701 return c 702 } 703 } 704 } 705 706 func (p *P2PoolApi) Consensus() *sidechain.Consensus { 707 if c := p.consensus.Load(); c == nil { 708 if c = p.getConsensus(); c != nil { 709 p.consensus.Store(c) 710 } 711 return c 712 } else { 713 return c 714 } 715 } 716 717 func (p *P2PoolApi) Status() *p2pooltypes.P2PoolSideChainStatusResult { 718 if response, err := p.Client.Get(p.Host + "/sidechain/status"); err != nil { 719 return nil 720 } else { 721 defer response.Body.Close() 722 723 if buf, err := io.ReadAll(response.Body); err != nil { 724 return nil 725 } else { 726 result := &p2pooltypes.P2PoolSideChainStatusResult{} 727 728 if err = utils.UnmarshalJSON(buf, result); err != nil { 729 return nil 730 } 731 732 return result 733 } 734 } 735 }