observer-bot/entry.go

119 lines
2.9 KiB
Go

package main
import (
"context"
"fmt"
"git.gammaspectra.live/P2Pool/p2pool-observer/index"
"git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/sidechain"
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
"golang.org/x/exp/slices"
"net/url"
"nhooyr.io/websocket"
"sync"
"time"
)
type channelEntry struct {
ApiEndpoint string
Channel string
Name string
PreviousBlocks foundBlocks
LastWindowMainDifficulty struct {
Height uint64
Difficulty uint64
}
PoolInfo map[string]any
Consensus *sidechain.Consensus
Tip *index.SideBlock
Window map[types.Hash]*index.SideBlock
ChainLock sync.RWMutex
Ws *websocket.Conn
}
func (c *channelEntry) DesiredPruneDistance() uint64 {
//prune after a whole day
/*pruneDistance := (3600 * 24) / c.Consensus.TargetBlockTime
if pruneDistance < c.Consensus.ChainWindowSize {
pruneDistance = c.Consensus.ChainWindowSize
}*/
return c.Consensus.ChainWindowSize
}
func (c *channelEntry) SideBlockUnclesByTemplateId(id types.Hash) chan *index.SideBlock {
result := make(chan *index.SideBlock)
go func() {
defer close(result)
if b := c.SideBlockByTemplateId(id); b != nil && !b.IsUncle() {
for _, u := range b.Uncles {
if uncle := c.SideBlockByTemplateId(u.TemplateId); uncle != nil {
result <- uncle
}
}
}
}()
return result
}
func (c *channelEntry) SideBlockByTemplateId(id types.Hash) *index.SideBlock {
if b, ok := c.Window[id]; ok {
return b
} else if b = getTypeFromAPI[index.SideBlock](c.ApiEndpoint, "/api/block_by_id/"+id.String()); b != nil {
c.Window[b.TemplateId] = b
}
return nil
}
func (c *channelEntry) DifficultyFromHeight(height uint64) types.Difficulty {
if d := getTypeFromAPI[types.Difficulty](c.ApiEndpoint, fmt.Sprintf("/api/main_difficulty_by_height/%d", height)); d != nil {
return *d
}
return types.ZeroDifficulty
}
func (c *channelEntry) openWebSocket() {
if c.ApiEndpoint == "" {
c.Ws = nil
return
}
ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
defer cancel()
u, _ := url.Parse(c.ApiEndpoint)
if u.Scheme == "https" {
conn, _, err := websocket.Dial(ctx, fmt.Sprintf("wss://%s/api/events", u.Host), nil)
if err != nil {
c.Ws = nil
return
}
c.Ws = conn
} else {
conn, _, err := websocket.Dial(ctx, fmt.Sprintf("ws://%s/api/events", u.Host), nil)
if err != nil {
c.Ws = nil
return
}
c.Ws = conn
}
}
func (c *channelEntry) pruneBlocks() {
pruneDistance := c.DesiredPruneDistance()
inChainFromTip := make([]types.Hash, 0, pruneDistance*2)
for cur := c.Tip; cur != nil && (c.Tip.EffectiveHeight-cur.EffectiveHeight) <= pruneDistance; cur = c.Window[cur.ParentTemplateId] {
inChainFromTip = append(inChainFromTip, cur.TemplateId)
for _, u := range cur.Uncles {
inChainFromTip = append(inChainFromTip, u.TemplateId)
}
}
for k := range c.Window {
if !slices.Contains(inChainFromTip, k) {
delete(c.Window, k)
}
}
}