Files
libsam/pkg/shreder/addresstables.go

102 lines
2.6 KiB
Go
Raw Normal View History

2026-01-05 12:45:32 +08:00
package shreder
import (
"context"
"fmt"
"sync"
"github.com/gagliardetto/solana-go"
"github.com/gagliardetto/solana-go/rpc"
2026-01-05 14:38:02 +08:00
lru "github.com/hashicorp/golang-lru/v2"
2026-01-05 12:45:32 +08:00
"github.com/panjf2000/ants/v2"
)
type AddressTables struct {
rpcClient *rpc.Client
mux sync.RWMutex
2026-01-05 14:38:02 +08:00
tables *lru.Cache[solana.PublicKey, []solana.PublicKey]
2026-01-05 12:45:32 +08:00
loading map[solana.PublicKey]struct{}
pool *ants.Pool
}
func NewAddressTables(rpcClient *rpc.Client) *AddressTables {
2026-01-06 16:42:07 +08:00
pool, _ := ants.NewPool(5, ants.WithPreAlloc(true), ants.WithNonblocking(true))
2026-01-05 14:38:02 +08:00
cache, _ := lru.New[solana.PublicKey, []solana.PublicKey](10000)
2026-01-05 12:45:32 +08:00
return &AddressTables{
rpcClient: rpcClient,
2026-01-05 14:38:02 +08:00
tables: cache,
2026-01-05 12:45:32 +08:00
loading: make(map[solana.PublicKey]struct{}),
pool: pool,
}
}
func (at *AddressTables) loadAddressTable(tablePubkey solana.PublicKey) ([]solana.PublicKey, error) {
// decode acc
acc, err := at.rpcClient.GetAccountInfoWithOpts(context.Background(), tablePubkey, &rpc.GetAccountInfoOpts{
Encoding: solana.EncodingBase64,
})
if err != nil {
return nil, err
}
data := acc.GetBinary()
if len(data) <= 56 {
return nil, fmt.Errorf("account data too short")
}
offset := 56
var addresses solana.PublicKeySlice = make([]solana.PublicKey, 0, (len(data)-offset)/32)
for offset+32 <= len(data) {
addresses = append(addresses, solana.PublicKeyFromBytes(data[offset:offset+32]))
offset += 32
}
// addresses = append(addresses, solana.PublicKeyFromBytes(data[start:start+32]))
return addresses, nil
}
func (at *AddressTables) GetAddressTable(tablePubkey solana.PublicKey, idx []uint8) []solana.PublicKey {
at.mux.RLock()
2026-01-05 14:38:02 +08:00
addresses, ok := at.tables.Get(tablePubkey)
2026-01-05 12:45:32 +08:00
if !ok {
at.mux.RUnlock()
_ = at.pool.Submit(func() {
at.mux.RLock()
_, loading := at.loading[tablePubkey]
if loading {
at.mux.RUnlock()
return
}
at.mux.RUnlock()
at.mux.Lock()
at.loading[tablePubkey] = struct{}{}
at.mux.Unlock()
table, err := at.loadAddressTable(tablePubkey)
if err != nil {
logger.Error("loadAddressTable failed", "err", err, "table", tablePubkey)
at.mux.Lock()
delete(at.loading, tablePubkey)
at.mux.Unlock()
return
}
at.mux.Lock()
2026-01-05 14:38:02 +08:00
at.tables.Add(tablePubkey, table)
total := at.tables.Len()
2026-01-05 12:45:32 +08:00
delete(at.loading, tablePubkey)
at.mux.Unlock()
logger.Info("loadAddressTable", "table", tablePubkey.String(), "table count:", total)
})
return nil
}
at.mux.RUnlock()
var result solana.PublicKeySlice = make([]solana.PublicKey, 0, len(idx))
2026-01-05 19:34:35 +08:00
for _, i := range idx {
if int(i) >= len(addresses) {
2026-01-05 12:45:32 +08:00
continue
}
result = append(result, addresses[i])
}
return result
}