radiance/pkg/blockhash/blockhash.go

123 lines
2.5 KiB
Go

package blockhash
import (
"context"
"sync"
"time"
envv1 "github.com/certusone/radiance/proto/env/v1"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
"k8s.io/klog/v2"
)
type Tracker struct {
mu sync.Mutex
byNode map[string]struct {
Blockhash solana.Hash
HighestValidSlot uint64
}
nodes []*envv1.RPCNode
c map[string]*rpc.Client
}
func New(nodes []*envv1.RPCNode) *Tracker {
t := &Tracker{
byNode: make(map[string]struct {
Blockhash solana.Hash
HighestValidSlot uint64
}),
c: make(map[string]*rpc.Client),
nodes: nodes,
}
for _, node := range nodes {
t.c[node.Name] = rpc.New(node.Http)
}
return t
}
func (t *Tracker) MostPopular() solana.Hash {
t.mu.Lock()
defer t.mu.Unlock()
// Return the most frequently occurring value of t.byNode
var mostPopular solana.Hash
var mostPopularCount int
for _, h := range t.byNode {
count := 0
for _, h2 := range t.byNode {
if h.Blockhash.Equals(h2.Blockhash) {
count++
}
}
if count > mostPopularCount {
mostPopular = h.Blockhash
mostPopularCount = count
}
klog.V(2).Infof("%s: %d", h, count)
}
return mostPopular
}
func (t *Tracker) MostRecent() solana.Hash {
t.mu.Lock()
defer t.mu.Unlock()
// Return the blockhash which has the highest valid slot
var mostRecent solana.Hash
var highestValidSlot uint64
for _, h := range t.byNode {
if h.HighestValidSlot > highestValidSlot {
highestValidSlot = h.HighestValidSlot
mostRecent = h.Blockhash
}
}
return mostRecent
}
func (t *Tracker) Run(ctx context.Context, interval time.Duration) {
t.update(ctx)
for {
select {
case <-ctx.Done():
return
case <-time.After(interval):
t.update(ctx)
}
}
}
func (t *Tracker) update(ctx context.Context) {
now := time.Now()
for _, node := range t.nodes {
node := node
go func() {
ctx, cancel := context.WithTimeout(ctx, time.Second*5)
defer cancel()
klog.V(1).Infof("Fetching blockhash from %s", node.Http)
h, err := t.c[node.Name].GetLatestBlockhash(ctx, rpc.CommitmentConfirmed)
if err != nil {
klog.Errorf("%s: failed to request blockhash: %v", node.Name, err)
return
}
klog.V(1).Infof("%s: fetched blockhash %d -> %s in %v",
node.Name, h.Value.LastValidBlockHeight, h.Value.Blockhash, time.Since(now))
t.mu.Lock()
t.byNode[node.Name] = struct {
Blockhash solana.Hash
HighestValidSlot uint64
}{
Blockhash: h.Value.Blockhash,
HighestValidSlot: h.Value.LastValidBlockHeight,
}
t.mu.Unlock()
}()
}
}