mirror of
https://github.com/luxfi/crypto.git
synced 2026-07-27 01:54:50 +00:00
chore: remove local cache/utils, use external luxfi/cache package
- Remove cache/ and utils/ directories (originally copied from luxfi/node) - Update secp256k1 imports to use github.com/luxfi/cache/lru - Update sig_fuzz_test.go to use local crypto/hashing instead of node/utils - Clean up go.mod: remove unused node/geth dependencies - crypto package is now fully standalone
This commit is contained in:
Vendored
-27
@@ -1,27 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cache
|
||||
|
||||
// Cacher acts as a best effort key value store.
|
||||
type Cacher[K comparable, V any] interface {
|
||||
// Put inserts an element into the cache. If space is required, elements will
|
||||
// be evicted.
|
||||
Put(key K, value V)
|
||||
|
||||
// Get returns the entry in the cache with the key specified, if no value
|
||||
// exists, false is returned.
|
||||
Get(key K) (V, bool)
|
||||
|
||||
// Evict removes the specified entry from the cache
|
||||
Evict(key K)
|
||||
|
||||
// Flush removes all entries from the cache
|
||||
Flush()
|
||||
|
||||
// Returns the number of elements currently in the cache
|
||||
Len() int
|
||||
|
||||
// Returns fraction of cache currently filled (0 --> 1)
|
||||
PortionFilled() float64
|
||||
}
|
||||
Vendored
-147
@@ -1,147 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cachetest
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/cache"
|
||||
)
|
||||
|
||||
const IntSize = ids.IDLen + 8
|
||||
|
||||
func IntSizeFunc(ids.ID, int64) int {
|
||||
return IntSize
|
||||
}
|
||||
|
||||
// Tests is a list of all Cacher tests
|
||||
var Tests = []struct {
|
||||
Size int
|
||||
Func func(t *testing.T, c cache.Cacher[ids.ID, int64])
|
||||
}{
|
||||
{Size: 1, Func: Basic},
|
||||
{Size: 2, Func: Eviction},
|
||||
}
|
||||
|
||||
func Basic(t *testing.T, cache cache.Cacher[ids.ID, int64]) {
|
||||
require := require.New(t)
|
||||
|
||||
id1 := ids.ID{1}
|
||||
_, found := cache.Get(id1)
|
||||
require.False(found)
|
||||
|
||||
expectedValue1 := int64(1)
|
||||
cache.Put(id1, expectedValue1)
|
||||
value, found := cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, value)
|
||||
|
||||
cache.Put(id1, expectedValue1)
|
||||
value, found = cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, value)
|
||||
|
||||
cache.Put(id1, expectedValue1)
|
||||
value, found = cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, value)
|
||||
|
||||
id2 := ids.ID{2}
|
||||
|
||||
expectedValue2 := int64(2)
|
||||
cache.Put(id2, expectedValue2)
|
||||
_, found = cache.Get(id1)
|
||||
require.False(found)
|
||||
|
||||
value, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue2, value)
|
||||
}
|
||||
|
||||
func Eviction(t *testing.T, cache cache.Cacher[ids.ID, int64]) {
|
||||
require := require.New(t)
|
||||
|
||||
id1 := ids.ID{1}
|
||||
id2 := ids.ID{2}
|
||||
id3 := ids.ID{3}
|
||||
|
||||
expectedValue1 := int64(1)
|
||||
expectedValue2 := int64(2)
|
||||
expectedValue3 := int64(3)
|
||||
|
||||
require.Zero(cache.Len())
|
||||
|
||||
cache.Put(id1, expectedValue1)
|
||||
|
||||
require.Equal(1, cache.Len())
|
||||
|
||||
cache.Put(id2, expectedValue2)
|
||||
|
||||
require.Equal(2, cache.Len())
|
||||
|
||||
val, found := cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, val)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue2, val)
|
||||
|
||||
_, found = cache.Get(id3)
|
||||
require.False(found)
|
||||
|
||||
cache.Put(id3, expectedValue3)
|
||||
require.Equal(2, cache.Len())
|
||||
|
||||
_, found = cache.Get(id1)
|
||||
require.False(found)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue2, val)
|
||||
|
||||
val, found = cache.Get(id3)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue3, val)
|
||||
|
||||
cache.Get(id2)
|
||||
cache.Put(id1, expectedValue1)
|
||||
|
||||
val, found = cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, val)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue2, val)
|
||||
|
||||
_, found = cache.Get(id3)
|
||||
require.False(found)
|
||||
|
||||
cache.Evict(id2)
|
||||
cache.Put(id3, expectedValue3)
|
||||
|
||||
val, found = cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, val)
|
||||
|
||||
_, found = cache.Get(id2)
|
||||
require.False(found)
|
||||
|
||||
val, found = cache.Get(id3)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue3, val)
|
||||
|
||||
cache.Flush()
|
||||
|
||||
_, found = cache.Get(id1)
|
||||
require.False(found)
|
||||
_, found = cache.Get(id2)
|
||||
require.False(found)
|
||||
_, found = cache.Get(id3)
|
||||
require.False(found)
|
||||
}
|
||||
Vendored
-29
@@ -1,29 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cache
|
||||
|
||||
import "github.com/luxfi/node/utils"
|
||||
|
||||
var _ Cacher[struct{}, struct{}] = (*Empty[struct{}, struct{}])(nil)
|
||||
|
||||
// Empty is a cache that doesn't store anything.
|
||||
type Empty[K any, V any] struct{}
|
||||
|
||||
func (*Empty[K, V]) Put(K, V) {}
|
||||
|
||||
func (*Empty[K, V]) Get(K) (V, bool) {
|
||||
return utils.Zero[V](), false
|
||||
}
|
||||
|
||||
func (*Empty[K, _]) Evict(K) {}
|
||||
|
||||
func (*Empty[_, _]) Flush() {}
|
||||
|
||||
func (*Empty[_, _]) Len() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (*Empty[_, _]) PortionFilled() float64 {
|
||||
return 0
|
||||
}
|
||||
Vendored
-93
@@ -1,93 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package lru
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/node/cache"
|
||||
"github.com/luxfi/node/utils"
|
||||
"github.com/luxfi/node/utils/linked"
|
||||
)
|
||||
|
||||
var _ cache.Cacher[struct{}, struct{}] = (*Cache[struct{}, struct{}])(nil)
|
||||
|
||||
// Cache is a key value store with bounded size. If the size is attempted to be
|
||||
// exceeded, then an element is removed from the cache before the insertion is
|
||||
// done, based on evicting the least recently used value.
|
||||
type Cache[K comparable, V any] struct {
|
||||
lock sync.Mutex
|
||||
elements *linked.Hashmap[K, V]
|
||||
size int
|
||||
|
||||
// onEvict is called with the key and value of an entry before eviction.
|
||||
onEvict func(K, V)
|
||||
}
|
||||
|
||||
func NewCache[K comparable, V any](size int) *Cache[K, V] {
|
||||
return NewCacheWithOnEvict(size, func(K, V) {})
|
||||
}
|
||||
|
||||
// NewCacheWithOnEvict creates a new LRU cache with the given size and eviction callback.
|
||||
func NewCacheWithOnEvict[K comparable, V any](size int, onEvict func(K, V)) *Cache[K, V] {
|
||||
return &Cache[K, V]{
|
||||
elements: linked.NewHashmap[K, V](),
|
||||
size: max(size, 1),
|
||||
onEvict: onEvict,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache[K, V]) Put(key K, value V) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
if c.elements.Len() == c.size {
|
||||
oldestKey, oldestVal, _ := c.elements.Oldest()
|
||||
c.elements.Delete(oldestKey)
|
||||
if c.onEvict != nil {
|
||||
c.onEvict(oldestKey, oldestVal)
|
||||
}
|
||||
}
|
||||
c.elements.Put(key, value)
|
||||
}
|
||||
|
||||
func (c *Cache[K, V]) Get(key K) (V, bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
val, ok := c.elements.Get(key)
|
||||
if !ok {
|
||||
return utils.Zero[V](), false
|
||||
}
|
||||
c.elements.Put(key, val) // Mark [k] as MRU.
|
||||
return val, true
|
||||
}
|
||||
|
||||
func (c *Cache[K, _]) Evict(key K) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.elements.Delete(key)
|
||||
}
|
||||
|
||||
func (c *Cache[_, _]) Flush() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.elements.Clear()
|
||||
}
|
||||
|
||||
func (c *Cache[_, _]) Len() int {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.elements.Len()
|
||||
}
|
||||
|
||||
func (c *Cache[_, _]) PortionFilled() float64 {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return float64(c.elements.Len()) / float64(c.size)
|
||||
}
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package lru
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/cache/cachetest"
|
||||
)
|
||||
|
||||
func TestCache(t *testing.T) {
|
||||
c := NewCache[ids.ID, int64](1)
|
||||
cachetest.Basic(t, c)
|
||||
}
|
||||
|
||||
func TestCacheEviction(t *testing.T) {
|
||||
c := NewCache[ids.ID, int64](2)
|
||||
cachetest.Eviction(t, c)
|
||||
}
|
||||
Vendored
-82
@@ -1,82 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package lru
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/node/utils/linked"
|
||||
)
|
||||
|
||||
// Evictable allows the object to be notified when it is evicted
|
||||
//
|
||||
// Deprecated: Remove this once the vertex state no longer uses it.
|
||||
type Evictable[K comparable] interface {
|
||||
Key() K
|
||||
Evict()
|
||||
}
|
||||
|
||||
// Deduplicator is an LRU cache that notifies the objects when they are evicted.
|
||||
//
|
||||
// Deprecated: Remove this once the vertex state no longer uses it.
|
||||
type Deduplicator[K comparable, V Evictable[K]] struct {
|
||||
lock sync.Mutex
|
||||
entryMap map[K]*linked.ListElement[V]
|
||||
entryList *linked.List[V]
|
||||
size int
|
||||
}
|
||||
|
||||
// Deprecated: Remove this once the vertex state no longer uses it.
|
||||
func NewDeduplicator[K comparable, V Evictable[K]](size int) *Deduplicator[K, V] {
|
||||
return &Deduplicator[K, V]{
|
||||
entryMap: make(map[K]*linked.ListElement[V]),
|
||||
entryList: linked.NewList[V](),
|
||||
size: max(size, 1),
|
||||
}
|
||||
}
|
||||
|
||||
// Deduplicate returns either the provided value, or a previously provided value
|
||||
// with the same ID that hasn't yet been evicted
|
||||
func (d *Deduplicator[_, V]) Deduplicate(value V) V {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
key := value.Key()
|
||||
if e, ok := d.entryMap[key]; !ok {
|
||||
if d.entryList.Len() >= d.size {
|
||||
e = d.entryList.Front()
|
||||
d.entryList.MoveToBack(e)
|
||||
|
||||
delete(d.entryMap, e.Value.Key())
|
||||
e.Value.Evict()
|
||||
|
||||
e.Value = value
|
||||
} else {
|
||||
e = &linked.ListElement[V]{
|
||||
Value: value,
|
||||
}
|
||||
d.entryList.PushBack(e)
|
||||
}
|
||||
d.entryMap[key] = e
|
||||
} else {
|
||||
d.entryList.MoveToBack(e)
|
||||
|
||||
value = e.Value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// Flush removes all entries from the cache
|
||||
func (d *Deduplicator[_, _]) Flush() {
|
||||
d.lock.Lock()
|
||||
defer d.lock.Unlock()
|
||||
|
||||
for d.entryList.Len() > 0 {
|
||||
e := d.entryList.Front()
|
||||
d.entryList.Remove(e)
|
||||
|
||||
delete(d.entryMap, e.Value.Key())
|
||||
e.Value.Evict()
|
||||
}
|
||||
}
|
||||
Vendored
-43
@@ -1,43 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package lru
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
type evictable[K comparable] struct {
|
||||
id K
|
||||
evicted int
|
||||
}
|
||||
|
||||
func (e *evictable[K]) Key() K {
|
||||
return e.id
|
||||
}
|
||||
|
||||
func (e *evictable[_]) Evict() {
|
||||
e.evicted++
|
||||
}
|
||||
|
||||
func TestDeduplicator(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
cache := NewDeduplicator[ids.ID, *evictable[ids.ID]](1)
|
||||
|
||||
expectedValue1 := &evictable[ids.ID]{id: ids.ID{1}}
|
||||
require.Equal(expectedValue1, cache.Deduplicate(expectedValue1))
|
||||
require.Zero(expectedValue1.evicted)
|
||||
require.Equal(expectedValue1, cache.Deduplicate(expectedValue1))
|
||||
require.Zero(expectedValue1.evicted)
|
||||
|
||||
expectedValue2 := &evictable[ids.ID]{id: ids.ID{2}}
|
||||
returnedValue := cache.Deduplicate(expectedValue2)
|
||||
require.Equal(expectedValue2, returnedValue)
|
||||
require.Equal(1, expectedValue1.evicted)
|
||||
require.Zero(expectedValue2.evicted)
|
||||
}
|
||||
Vendored
-140
@@ -1,140 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package lru
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/node/cache"
|
||||
"github.com/luxfi/node/utils"
|
||||
"github.com/luxfi/node/utils/linked"
|
||||
)
|
||||
|
||||
var _ cache.Cacher[struct{}, any] = (*SizedCache[struct{}, any])(nil)
|
||||
|
||||
// sizedElement is used to store the element with its size, so we don't
|
||||
// calculate the size multiple times.
|
||||
//
|
||||
// This ensures that any inconsistencies returned by the size function can not
|
||||
// corrupt the cache.
|
||||
type sizedElement[V any] struct {
|
||||
value V
|
||||
size int
|
||||
}
|
||||
|
||||
// SizedCache is a key value store with bounded size. If the size is attempted
|
||||
// to be exceeded, then elements are removed from the cache until the bound is
|
||||
// honored, based on evicting the least recently used value.
|
||||
type SizedCache[K comparable, V any] struct {
|
||||
lock sync.Mutex
|
||||
elements *linked.Hashmap[K, *sizedElement[V]]
|
||||
maxSize int
|
||||
currentSize int
|
||||
size func(K, V) int
|
||||
}
|
||||
|
||||
func NewSizedCache[K comparable, V any](maxSize int, size func(K, V) int) *SizedCache[K, V] {
|
||||
return &SizedCache[K, V]{
|
||||
elements: linked.NewHashmap[K, *sizedElement[V]](),
|
||||
maxSize: maxSize,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SizedCache[K, V]) Put(key K, value V) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.put(key, value)
|
||||
}
|
||||
|
||||
func (c *SizedCache[K, V]) Get(key K) (V, bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.get(key)
|
||||
}
|
||||
|
||||
func (c *SizedCache[K, V]) Evict(key K) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.evict(key)
|
||||
}
|
||||
|
||||
func (c *SizedCache[K, V]) Flush() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.flush()
|
||||
}
|
||||
|
||||
func (c *SizedCache[_, _]) Len() int {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.len()
|
||||
}
|
||||
|
||||
func (c *SizedCache[_, _]) PortionFilled() float64 {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.portionFilled()
|
||||
}
|
||||
|
||||
func (c *SizedCache[K, V]) put(key K, value V) {
|
||||
newEntrySize := c.size(key, value)
|
||||
if newEntrySize > c.maxSize {
|
||||
c.flush()
|
||||
return
|
||||
}
|
||||
|
||||
if oldElement, ok := c.elements.Get(key); ok {
|
||||
c.currentSize -= oldElement.size
|
||||
}
|
||||
|
||||
// Remove elements until the size of elements in the cache <= [c.maxSize].
|
||||
for c.currentSize > c.maxSize-newEntrySize {
|
||||
oldestKey, oldestElement, _ := c.elements.Oldest()
|
||||
c.elements.Delete(oldestKey)
|
||||
c.currentSize -= oldestElement.size
|
||||
}
|
||||
|
||||
c.elements.Put(key, &sizedElement[V]{
|
||||
value: value,
|
||||
size: newEntrySize,
|
||||
})
|
||||
c.currentSize += newEntrySize
|
||||
}
|
||||
|
||||
func (c *SizedCache[K, V]) get(key K) (V, bool) {
|
||||
element, ok := c.elements.Get(key)
|
||||
if !ok {
|
||||
return utils.Zero[V](), false
|
||||
}
|
||||
|
||||
c.elements.Put(key, element) // Mark [k] as MRU.
|
||||
return element.value, true
|
||||
}
|
||||
|
||||
func (c *SizedCache[K, _]) evict(key K) {
|
||||
if element, ok := c.elements.Get(key); ok {
|
||||
c.elements.Delete(key)
|
||||
c.currentSize -= element.size
|
||||
}
|
||||
}
|
||||
|
||||
func (c *SizedCache[K, V]) flush() {
|
||||
c.elements.Clear()
|
||||
c.currentSize = 0
|
||||
}
|
||||
|
||||
func (c *SizedCache[_, _]) len() int {
|
||||
return c.elements.Len()
|
||||
}
|
||||
|
||||
func (c *SizedCache[_, _]) portionFilled() float64 {
|
||||
return float64(c.currentSize) / float64(c.maxSize)
|
||||
}
|
||||
Vendored
-90
@@ -1,90 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package lru
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/cache/cachetest"
|
||||
)
|
||||
|
||||
func TestSizedCache(t *testing.T) {
|
||||
c := NewSizedCache[ids.ID, int64](cachetest.IntSize, cachetest.IntSizeFunc)
|
||||
cachetest.Basic(t, c)
|
||||
}
|
||||
|
||||
func TestSizedCacheEviction(t *testing.T) {
|
||||
c := NewSizedCache[ids.ID, int64](2*cachetest.IntSize, cachetest.IntSizeFunc)
|
||||
cachetest.Eviction(t, c)
|
||||
}
|
||||
|
||||
func TestSizedCacheWrongKeyEvictionRegression(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
cache := NewSizedCache[string, struct{}](
|
||||
3,
|
||||
func(key string, _ struct{}) int {
|
||||
return len(key)
|
||||
},
|
||||
)
|
||||
|
||||
cache.Put("a", struct{}{})
|
||||
cache.Put("b", struct{}{})
|
||||
cache.Put("c", struct{}{})
|
||||
cache.Put("dd", struct{}{})
|
||||
|
||||
_, ok := cache.Get("a")
|
||||
require.False(ok)
|
||||
|
||||
_, ok = cache.Get("b")
|
||||
require.False(ok)
|
||||
|
||||
_, ok = cache.Get("c")
|
||||
require.True(ok)
|
||||
|
||||
_, ok = cache.Get("dd")
|
||||
require.True(ok)
|
||||
}
|
||||
|
||||
func TestSizedLRUSizeAlteringRegression(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
cache := NewSizedCache[string, *string](
|
||||
5,
|
||||
func(key string, val *string) int {
|
||||
if val != nil {
|
||||
return len(key) + len(*val)
|
||||
}
|
||||
|
||||
return len(key)
|
||||
},
|
||||
)
|
||||
|
||||
// put first value
|
||||
expectedPortionFilled := 0.6
|
||||
valueA := "ab"
|
||||
cache.Put("a", &valueA)
|
||||
|
||||
require.InDelta(expectedPortionFilled, cache.PortionFilled(), 0)
|
||||
|
||||
// mutate first value
|
||||
valueA = "abcd"
|
||||
require.InDelta(expectedPortionFilled, cache.PortionFilled(), 0, "after value A mutation, portion filled should be the same")
|
||||
|
||||
// put second value
|
||||
expectedPortionFilled = 0.8
|
||||
valueB := "bcd"
|
||||
cache.Put("b", &valueB)
|
||||
|
||||
require.InDelta(expectedPortionFilled, cache.PortionFilled(), 0)
|
||||
|
||||
_, ok := cache.Get("a")
|
||||
require.False(ok, "key a shouldn't exist after b is put")
|
||||
|
||||
_, ok = cache.Get("b")
|
||||
require.True(ok, "key b should exist")
|
||||
}
|
||||
Vendored
-133
@@ -1,133 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/crypto/utils"
|
||||
"github.com/luxfi/crypto/utils/linked"
|
||||
)
|
||||
|
||||
var _ Cacher[struct{}, struct{}] = (*LRU[struct{}, struct{}])(nil)
|
||||
|
||||
// NewLRU creates a new LRU cache with the specified size
|
||||
func NewLRU[K comparable, V any](size int) *LRU[K, V] {
|
||||
return &LRU[K, V]{
|
||||
Size: size,
|
||||
}
|
||||
}
|
||||
|
||||
// LRU is a key value store with bounded size. If the size is attempted to be
|
||||
// exceeded, then an element is removed from the cache before the insertion is
|
||||
// done, based on evicting the least recently used value.
|
||||
type LRU[K comparable, V any] struct {
|
||||
lock sync.Mutex
|
||||
elements *linked.Hashmap[K, V]
|
||||
// If set to <= 0, will be set internally to 1.
|
||||
Size int
|
||||
}
|
||||
|
||||
func (c *LRU[K, V]) Put(key K, value V) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.put(key, value)
|
||||
}
|
||||
|
||||
func (c *LRU[K, V]) Get(key K) (V, bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.get(key)
|
||||
}
|
||||
|
||||
func (c *LRU[K, _]) Evict(key K) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.evict(key)
|
||||
}
|
||||
|
||||
func (c *LRU[_, _]) Flush() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.flush()
|
||||
}
|
||||
|
||||
func (c *LRU[_, _]) Len() int {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.len()
|
||||
}
|
||||
|
||||
func (c *LRU[_, _]) PortionFilled() float64 {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.portionFilled()
|
||||
}
|
||||
|
||||
func (c *LRU[K, V]) put(key K, value V) {
|
||||
c.resize()
|
||||
|
||||
if c.elements.Len() == c.Size {
|
||||
oldestKey, _, _ := c.elements.Oldest()
|
||||
c.elements.Delete(oldestKey)
|
||||
}
|
||||
c.elements.Put(key, value)
|
||||
}
|
||||
|
||||
func (c *LRU[K, V]) get(key K) (V, bool) {
|
||||
c.resize()
|
||||
|
||||
val, ok := c.elements.Get(key)
|
||||
if !ok {
|
||||
return utils.Zero[V](), false
|
||||
}
|
||||
c.elements.Put(key, val) // Mark [k] as MRU.
|
||||
return val, true
|
||||
}
|
||||
|
||||
func (c *LRU[K, _]) evict(key K) {
|
||||
c.resize()
|
||||
|
||||
c.elements.Delete(key)
|
||||
}
|
||||
|
||||
func (c *LRU[K, V]) flush() {
|
||||
if c.elements != nil {
|
||||
c.elements.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *LRU[_, _]) len() int {
|
||||
if c.elements == nil {
|
||||
return 0
|
||||
}
|
||||
return c.elements.Len()
|
||||
}
|
||||
|
||||
func (c *LRU[_, _]) portionFilled() float64 {
|
||||
return float64(c.len()) / float64(c.Size)
|
||||
}
|
||||
|
||||
// Initializes [c.elements] if it's nil.
|
||||
// Sets [c.size] to 1 if it's <= 0.
|
||||
// Removes oldest elements to make number of elements
|
||||
// in the cache == [c.size] if necessary.
|
||||
func (c *LRU[K, V]) resize() {
|
||||
if c.elements == nil {
|
||||
c.elements = linked.NewHashmap[K, V]()
|
||||
}
|
||||
if c.Size <= 0 {
|
||||
c.Size = 1
|
||||
}
|
||||
for c.elements.Len() > c.Size {
|
||||
oldestKey, _, _ := c.elements.Oldest()
|
||||
c.elements.Delete(oldestKey)
|
||||
}
|
||||
}
|
||||
Vendored
-61
@@ -1,61 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
func BenchmarkLRUCachePutSmall(b *testing.B) {
|
||||
smallLen := 5
|
||||
cache := &LRU[ids.ID, int]{Size: smallLen}
|
||||
for n := 0; n < b.N; n++ {
|
||||
for i := 0; i < smallLen; i++ {
|
||||
var id ids.ID
|
||||
_, err := rand.Read(id[:])
|
||||
require.NoError(b, err)
|
||||
cache.Put(id, n)
|
||||
}
|
||||
b.StopTimer()
|
||||
cache.Flush()
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLRUCachePutMedium(b *testing.B) {
|
||||
mediumLen := 250
|
||||
cache := &LRU[ids.ID, int]{Size: mediumLen}
|
||||
for n := 0; n < b.N; n++ {
|
||||
for i := 0; i < mediumLen; i++ {
|
||||
var id ids.ID
|
||||
_, err := rand.Read(id[:])
|
||||
require.NoError(b, err)
|
||||
cache.Put(id, n)
|
||||
}
|
||||
b.StopTimer()
|
||||
cache.Flush()
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLRUCachePutLarge(b *testing.B) {
|
||||
largeLen := 10000
|
||||
cache := &LRU[ids.ID, int]{Size: largeLen}
|
||||
for n := 0; n < b.N; n++ {
|
||||
for i := 0; i < largeLen; i++ {
|
||||
var id ids.ID
|
||||
_, err := rand.Read(id[:])
|
||||
require.NoError(b, err)
|
||||
cache.Put(id, n)
|
||||
}
|
||||
b.StopTimer()
|
||||
cache.Flush()
|
||||
b.StartTimer()
|
||||
}
|
||||
}
|
||||
Vendored
-67
@@ -1,67 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cache_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/cache"
|
||||
"github.com/luxfi/node/cache/cachetest"
|
||||
)
|
||||
|
||||
func TestLRU(t *testing.T) {
|
||||
cache := &cache.LRU[ids.ID, int64]{Size: 1}
|
||||
|
||||
cachetest.Basic(t, cache)
|
||||
}
|
||||
|
||||
func TestLRUEviction(t *testing.T) {
|
||||
cache := &cache.LRU[ids.ID, int64]{Size: 2}
|
||||
|
||||
cachetest.Eviction(t, cache)
|
||||
}
|
||||
|
||||
func TestLRUResize(t *testing.T) {
|
||||
require := require.New(t)
|
||||
cache := cache.LRU[ids.ID, int64]{Size: 2}
|
||||
|
||||
id1 := ids.ID{1}
|
||||
id2 := ids.ID{2}
|
||||
|
||||
expectedVal1 := int64(1)
|
||||
expectedVal2 := int64(2)
|
||||
cache.Put(id1, expectedVal1)
|
||||
cache.Put(id2, expectedVal2)
|
||||
|
||||
val, found := cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedVal1, val)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedVal2, val)
|
||||
|
||||
cache.Size = 1
|
||||
// id1 evicted
|
||||
|
||||
_, found = cache.Get(id1)
|
||||
require.False(found)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedVal2, val)
|
||||
|
||||
cache.Size = 0
|
||||
// We reset the size to 1 in resize
|
||||
|
||||
_, found = cache.Get(id1)
|
||||
require.False(found)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedVal2, val)
|
||||
}
|
||||
Vendored
-126
@@ -1,126 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
"github.com/luxfi/node/utils/linked"
|
||||
)
|
||||
|
||||
var _ Cacher[struct{}, any] = (*sizedLRU[struct{}, any])(nil)
|
||||
|
||||
// sizedLRU is a key value store with bounded size. If the size is attempted to
|
||||
// be exceeded, then elements are removed from the cache until the bound is
|
||||
// honored, based on evicting the least recently used value.
|
||||
type sizedLRU[K comparable, V any] struct {
|
||||
lock sync.Mutex
|
||||
elements *linked.Hashmap[K, V]
|
||||
maxSize int
|
||||
currentSize int
|
||||
size func(K, V) int
|
||||
}
|
||||
|
||||
func NewSizedLRU[K comparable, V any](maxSize int, size func(K, V) int) Cacher[K, V] {
|
||||
return &sizedLRU[K, V]{
|
||||
elements: linked.NewHashmap[K, V](),
|
||||
maxSize: maxSize,
|
||||
size: size,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *sizedLRU[K, V]) Put(key K, value V) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.put(key, value)
|
||||
}
|
||||
|
||||
func (c *sizedLRU[K, V]) Get(key K) (V, bool) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.get(key)
|
||||
}
|
||||
|
||||
func (c *sizedLRU[K, V]) Evict(key K) {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.evict(key)
|
||||
}
|
||||
|
||||
func (c *sizedLRU[K, V]) Flush() {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
c.flush()
|
||||
}
|
||||
|
||||
func (c *sizedLRU[_, _]) Len() int {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.len()
|
||||
}
|
||||
|
||||
func (c *sizedLRU[_, _]) PortionFilled() float64 {
|
||||
c.lock.Lock()
|
||||
defer c.lock.Unlock()
|
||||
|
||||
return c.portionFilled()
|
||||
}
|
||||
|
||||
func (c *sizedLRU[K, V]) put(key K, value V) {
|
||||
newEntrySize := c.size(key, value)
|
||||
if newEntrySize > c.maxSize {
|
||||
c.flush()
|
||||
return
|
||||
}
|
||||
|
||||
if oldValue, ok := c.elements.Get(key); ok {
|
||||
c.currentSize -= c.size(key, oldValue)
|
||||
}
|
||||
|
||||
// Remove elements until the size of elements in the cache <= [c.maxSize].
|
||||
for c.currentSize > c.maxSize-newEntrySize {
|
||||
oldestKey, oldestValue, _ := c.elements.Oldest()
|
||||
c.elements.Delete(oldestKey)
|
||||
c.currentSize -= c.size(oldestKey, oldestValue)
|
||||
}
|
||||
|
||||
c.elements.Put(key, value)
|
||||
c.currentSize += newEntrySize
|
||||
}
|
||||
|
||||
func (c *sizedLRU[K, V]) get(key K) (V, bool) {
|
||||
value, ok := c.elements.Get(key)
|
||||
if !ok {
|
||||
return utils.Zero[V](), false
|
||||
}
|
||||
|
||||
c.elements.Put(key, value) // Mark [k] as MRU.
|
||||
return value, true
|
||||
}
|
||||
|
||||
func (c *sizedLRU[K, _]) evict(key K) {
|
||||
if value, ok := c.elements.Get(key); ok {
|
||||
c.elements.Delete(key)
|
||||
c.currentSize -= c.size(key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *sizedLRU[K, V]) flush() {
|
||||
c.elements.Clear()
|
||||
c.currentSize = 0
|
||||
}
|
||||
|
||||
func (c *sizedLRU[_, _]) len() int {
|
||||
return c.elements.Len()
|
||||
}
|
||||
|
||||
func (c *sizedLRU[_, _]) portionFilled() float64 {
|
||||
return float64(c.currentSize) / float64(c.maxSize)
|
||||
}
|
||||
Vendored
-54
@@ -1,54 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cache_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/cache"
|
||||
"github.com/luxfi/node/cache/cachetest"
|
||||
)
|
||||
|
||||
func TestSizedLRU(t *testing.T) {
|
||||
c := cache.NewSizedLRU[ids.ID, int64](cachetest.IntSize, cachetest.IntSizeFunc)
|
||||
|
||||
cachetest.Basic(t, c)
|
||||
}
|
||||
|
||||
func TestSizedLRUEviction(t *testing.T) {
|
||||
c := cache.NewSizedLRU[ids.ID, int64](2*cachetest.IntSize, cachetest.IntSizeFunc)
|
||||
|
||||
cachetest.Eviction(t, c)
|
||||
}
|
||||
|
||||
func TestSizedLRUWrongKeyEvictionRegression(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
c := cache.NewSizedLRU[string, struct{}](
|
||||
3,
|
||||
func(key string, _ struct{}) int {
|
||||
return len(key)
|
||||
},
|
||||
)
|
||||
|
||||
c.Put("a", struct{}{})
|
||||
c.Put("b", struct{}{})
|
||||
c.Put("c", struct{}{})
|
||||
c.Put("dd", struct{}{})
|
||||
|
||||
_, ok := c.Get("a")
|
||||
require.False(ok)
|
||||
|
||||
_, ok = c.Get("b")
|
||||
require.False(ok)
|
||||
|
||||
_, ok = c.Get("c")
|
||||
require.True(ok)
|
||||
|
||||
_, ok = c.Get("dd")
|
||||
require.True(ok)
|
||||
}
|
||||
Vendored
-73
@@ -1,73 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package metercacher
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/metric"
|
||||
|
||||
"github.com/luxfi/node/cache"
|
||||
)
|
||||
|
||||
var _ cache.Cacher[struct{}, struct{}] = (*Cache[struct{}, struct{}])(nil)
|
||||
|
||||
type Cache[K comparable, V any] struct {
|
||||
cache.Cacher[K, V]
|
||||
|
||||
metrics *cacheMetrics
|
||||
}
|
||||
|
||||
func New[K comparable, V any](
|
||||
namespace string,
|
||||
registry metric.Registry,
|
||||
cache cache.Cacher[K, V],
|
||||
) (*Cache[K, V], error) {
|
||||
metrics, err := newMetrics(namespace, registry)
|
||||
return &Cache[K, V]{
|
||||
Cacher: cache,
|
||||
metrics: metrics,
|
||||
}, err
|
||||
}
|
||||
|
||||
func (c *Cache[K, V]) Put(key K, value V) {
|
||||
start := time.Now()
|
||||
c.Cacher.Put(key, value)
|
||||
putDuration := time.Since(start)
|
||||
|
||||
c.metrics.putCount.Inc()
|
||||
c.metrics.putTime.Add(float64(putDuration))
|
||||
c.metrics.len.Set(float64(c.Cacher.Len()))
|
||||
c.metrics.portionFilled.Set(c.Cacher.PortionFilled())
|
||||
}
|
||||
|
||||
func (c *Cache[K, V]) Get(key K) (V, bool) {
|
||||
start := time.Now()
|
||||
value, has := c.Cacher.Get(key)
|
||||
getDuration := time.Since(start)
|
||||
|
||||
if has {
|
||||
c.metrics.getCount.With(hitLabels).Inc()
|
||||
c.metrics.getTime.With(hitLabels).Add(float64(getDuration))
|
||||
} else {
|
||||
c.metrics.getCount.With(missLabels).Inc()
|
||||
c.metrics.getTime.With(missLabels).Add(float64(getDuration))
|
||||
}
|
||||
|
||||
return value, has
|
||||
}
|
||||
|
||||
func (c *Cache[K, _]) Evict(key K) {
|
||||
c.Cacher.Evict(key)
|
||||
|
||||
c.metrics.len.Set(float64(c.Cacher.Len()))
|
||||
c.metrics.portionFilled.Set(c.Cacher.PortionFilled())
|
||||
}
|
||||
|
||||
func (c *Cache[_, _]) Flush() {
|
||||
c.Cacher.Flush()
|
||||
|
||||
c.metrics.len.Set(float64(c.Cacher.Len()))
|
||||
c.metrics.portionFilled.Set(c.Cacher.PortionFilled())
|
||||
}
|
||||
Vendored
-50
@@ -1,50 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build test
|
||||
|
||||
package metercacher
|
||||
|
||||
import "github.com/luxfi/metric"
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/metric"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/cache"
|
||||
"github.com/luxfi/node/cache/cachetest"
|
||||
"github.com/luxfi/node/cache/lru"
|
||||
)
|
||||
|
||||
func TestInterface(t *testing.T) {
|
||||
scenarios := []struct {
|
||||
name string
|
||||
setup func(size int) cache.Cacher[ids.ID, int64]
|
||||
}{
|
||||
{
|
||||
name: "cache LRU",
|
||||
setup: func(size int) cache.Cacher[ids.ID, int64] {
|
||||
return lru.NewCache[ids.ID, int64](size)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "sized cache LRU",
|
||||
setup: func(size int) cache.Cacher[ids.ID, int64] {
|
||||
return lru.NewSizedCache(size*cachetest.IntSize, cachetest.IntSizeFunc)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, scenario := range scenarios {
|
||||
t.Run(scenario.name, func(t *testing.T) {
|
||||
for _, test := range cachetest.Tests {
|
||||
baseCache := scenario.setup(test.Size)
|
||||
c, err := New("", metric.NewRegistry(), baseCache)
|
||||
require.NoError(t, err)
|
||||
test.Func(t, c)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Vendored
-72
@@ -1,72 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package metercacher
|
||||
|
||||
import (
|
||||
"github.com/luxfi/metric"
|
||||
)
|
||||
|
||||
const (
|
||||
resultLabel = "result"
|
||||
hitResult = "hit"
|
||||
missResult = "miss"
|
||||
)
|
||||
|
||||
var (
|
||||
resultLabels = []string{resultLabel}
|
||||
hitLabels = metric.Labels{
|
||||
resultLabel: hitResult,
|
||||
}
|
||||
missLabels = metric.Labels{
|
||||
resultLabel: missResult,
|
||||
}
|
||||
)
|
||||
|
||||
type cacheMetrics struct {
|
||||
getCount metric.CounterVec
|
||||
getTime metric.GaugeVec
|
||||
|
||||
putCount metric.Counter
|
||||
putTime metric.Gauge
|
||||
|
||||
len metric.Gauge
|
||||
portionFilled metric.Gauge
|
||||
}
|
||||
|
||||
func newMetrics(
|
||||
namespace string,
|
||||
registry metric.Registry,
|
||||
) (*cacheMetrics, error) {
|
||||
metricsInstance := metric.NewWithRegistry(namespace, registry)
|
||||
|
||||
m := &cacheMetrics{
|
||||
getCount: metricsInstance.NewCounterVec(
|
||||
"get_count",
|
||||
"number of get calls",
|
||||
resultLabels,
|
||||
),
|
||||
getTime: metricsInstance.NewGaugeVec(
|
||||
"get_time",
|
||||
"time spent (ns) in get calls",
|
||||
resultLabels,
|
||||
),
|
||||
putCount: metricsInstance.NewCounter(
|
||||
"put_count",
|
||||
"number of put calls",
|
||||
),
|
||||
putTime: metricsInstance.NewGauge(
|
||||
"put_time",
|
||||
"time spent (ns) in put calls",
|
||||
),
|
||||
len: metricsInstance.NewGauge(
|
||||
"len",
|
||||
"number of entries",
|
||||
),
|
||||
portionFilled: metricsInstance.NewGauge(
|
||||
"portion_filled",
|
||||
"fraction of cache filled",
|
||||
),
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
Vendored
-148
@@ -1,148 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build test
|
||||
|
||||
package cache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
const TestIntSize = ids.IDLen + 8
|
||||
|
||||
func TestIntSizeFunc(ids.ID, int64) int {
|
||||
return TestIntSize
|
||||
}
|
||||
|
||||
// CacherTests is a list of all Cacher tests
|
||||
var CacherTests = []struct {
|
||||
Size int
|
||||
Func func(t *testing.T, c Cacher[ids.ID, int64])
|
||||
}{
|
||||
{Size: 1, Func: TestBasic},
|
||||
{Size: 2, Func: TestEviction},
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T, cache Cacher[ids.ID, int64]) {
|
||||
require := require.New(t)
|
||||
|
||||
id1 := ids.ID{1}
|
||||
_, found := cache.Get(id1)
|
||||
require.False(found)
|
||||
|
||||
expectedValue1 := int64(1)
|
||||
cache.Put(id1, expectedValue1)
|
||||
value, found := cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, value)
|
||||
|
||||
cache.Put(id1, expectedValue1)
|
||||
value, found = cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, value)
|
||||
|
||||
cache.Put(id1, expectedValue1)
|
||||
value, found = cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, value)
|
||||
|
||||
id2 := ids.ID{2}
|
||||
|
||||
expectedValue2 := int64(2)
|
||||
cache.Put(id2, expectedValue2)
|
||||
_, found = cache.Get(id1)
|
||||
require.False(found)
|
||||
|
||||
value, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue2, value)
|
||||
}
|
||||
|
||||
func TestEviction(t *testing.T, cache Cacher[ids.ID, int64]) {
|
||||
require := require.New(t)
|
||||
|
||||
id1 := ids.ID{1}
|
||||
id2 := ids.ID{2}
|
||||
id3 := ids.ID{3}
|
||||
|
||||
expectedValue1 := int64(1)
|
||||
expectedValue2 := int64(2)
|
||||
expectedValue3 := int64(3)
|
||||
|
||||
require.Zero(cache.Len())
|
||||
|
||||
cache.Put(id1, expectedValue1)
|
||||
|
||||
require.Equal(1, cache.Len())
|
||||
|
||||
cache.Put(id2, expectedValue2)
|
||||
|
||||
require.Equal(2, cache.Len())
|
||||
|
||||
val, found := cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, val)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue2, val)
|
||||
|
||||
_, found = cache.Get(id3)
|
||||
require.False(found)
|
||||
|
||||
cache.Put(id3, expectedValue3)
|
||||
require.Equal(2, cache.Len())
|
||||
|
||||
_, found = cache.Get(id1)
|
||||
require.False(found)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue2, val)
|
||||
|
||||
val, found = cache.Get(id3)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue3, val)
|
||||
|
||||
cache.Get(id2)
|
||||
cache.Put(id1, expectedValue1)
|
||||
|
||||
val, found = cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, val)
|
||||
|
||||
val, found = cache.Get(id2)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue2, val)
|
||||
|
||||
_, found = cache.Get(id3)
|
||||
require.False(found)
|
||||
|
||||
cache.Evict(id2)
|
||||
cache.Put(id3, expectedValue3)
|
||||
|
||||
val, found = cache.Get(id1)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue1, val)
|
||||
|
||||
_, found = cache.Get(id2)
|
||||
require.False(found)
|
||||
|
||||
val, found = cache.Get(id3)
|
||||
require.True(found)
|
||||
require.Equal(expectedValue3, val)
|
||||
|
||||
cache.Flush()
|
||||
|
||||
_, found = cache.Get(id1)
|
||||
require.False(found)
|
||||
_, found = cache.Get(id2)
|
||||
require.False(found)
|
||||
_, found = cache.Get(id3)
|
||||
require.False(found)
|
||||
}
|
||||
@@ -5,128 +5,37 @@ go 1.25.5
|
||||
require (
|
||||
filippo.io/age v1.2.1
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6
|
||||
github.com/cloudflare/circl v1.6.2-0.20251204010831-23491bd573cf
|
||||
github.com/consensys/gnark-crypto v0.19.2
|
||||
github.com/crate-crypto/go-eth-kzg v1.4.0
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5
|
||||
github.com/google/btree v1.1.3
|
||||
github.com/google/gofuzz v1.2.0
|
||||
github.com/google/renameio/v2 v2.0.0
|
||||
github.com/gorilla/rpc v1.2.1
|
||||
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267
|
||||
github.com/klauspost/compress v1.18.0
|
||||
github.com/leanovate/gopter v0.2.11
|
||||
github.com/luxfi/consensus v1.22.26
|
||||
github.com/luxfi/cache v1.0.0
|
||||
github.com/luxfi/ids v1.2.4
|
||||
github.com/luxfi/ledger-lux-go v1.0.0
|
||||
github.com/luxfi/log v1.1.26
|
||||
github.com/luxfi/math v1.0.2
|
||||
github.com/luxfi/metric v1.4.8
|
||||
github.com/luxfi/mock v0.1.0
|
||||
github.com/luxfi/node v1.22.14
|
||||
github.com/mr-tron/base58 v1.2.0
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/spaolacci/murmur3 v1.1.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/supranational/blst v0.3.16
|
||||
github.com/thepudds/fzgen v0.4.3
|
||||
github.com/zeebo/blake3 v0.2.4
|
||||
go.uber.org/mock v0.6.0
|
||||
golang.org/x/crypto v0.46.0
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b
|
||||
golang.org/x/sync v0.19.0
|
||||
golang.org/x/sys v0.39.0
|
||||
golang.org/x/tools v0.39.0
|
||||
gonum.org/v1/gonum v0.16.0
|
||||
google.golang.org/grpc v1.75.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/DataDog/zstd v1.5.7 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.24.4 // indirect
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.5 // indirect
|
||||
github.com/btcsuite/btcutil v1.0.2 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cockroachdb/errors v1.12.0 // indirect
|
||||
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 // indirect
|
||||
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 // indirect
|
||||
github.com/cockroachdb/pebble v1.1.5 // indirect
|
||||
github.com/cockroachdb/redact v1.1.6 // indirect
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.8.0 // indirect
|
||||
github.com/dgraph-io/ristretto/v2 v2.2.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/getsentry/sentry-go v0.35.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/mock v1.7.0-rc.1 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/flatbuffers v25.2.10+incompatible // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
|
||||
github.com/holiman/uint256 v1.3.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/luxfi/cache v1.0.0 // indirect
|
||||
github.com/luxfi/constants v1.2.2 // indirect
|
||||
github.com/luxfi/database v1.2.12 // indirect
|
||||
github.com/luxfi/genesis v1.4.7 // indirect
|
||||
github.com/luxfi/geth v1.16.53 // indirect
|
||||
github.com/luxfi/p2p v1.4.6 // indirect
|
||||
github.com/luxfi/trace v0.1.4 // indirect
|
||||
github.com/luxfi/utils v1.0.0 // indirect
|
||||
github.com/luxfi/warp v1.16.36 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/onsi/gomega v1.38.0 // indirect
|
||||
github.com/pires/go-proxyproto v0.8.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.67.4 // indirect
|
||||
github.com/prometheus/procfs v0.19.2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
github.com/rs/cors v1.11.1 // indirect
|
||||
github.com/sanity-io/litter v1.5.5 // indirect
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.15 // indirect
|
||||
github.com/tklauser/numcpus v0.10.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zeebo/assert v1.3.0 // indirect
|
||||
github.com/zondax/golem v0.27.0 // indirect
|
||||
github.com/zondax/hid v0.9.2 // indirect
|
||||
github.com/zondax/ledger-go v1.0.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
golang.org/x/mod v0.30.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/text v0.32.0 // indirect
|
||||
golang.org/x/time v0.12.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
golang.org/x/exp v0.0.0-20250819193227-8b4c13bb791b // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
@@ -43,55 +43,17 @@ filippo.io/age v1.2.1 h1:X0TZjehAZylOIj4DubWYU1vWQxv9bJpo+Uu2/LGhi1o=
|
||||
filippo.io/age v1.2.1/go.mod h1:JL9ew2lTN+Pyft4RiNGguFfOpewKwSHm5ayKD/A4004=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
|
||||
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
|
||||
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
|
||||
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bits-and-blooms/bitset v1.24.4 h1:95H15Og1clikBrKr/DuzMXkQzECs1M6hhoGXLwLQOZE=
|
||||
github.com/bits-and-blooms/bitset v1.24.4/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M=
|
||||
github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A=
|
||||
github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY=
|
||||
github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.5 h1:dpAlnAwmT1yIBm3exhT1/8iUSD98RDJM5vqJVQDQLiU=
|
||||
github.com/btcsuite/btcd/btcec/v2 v2.3.5/go.mod h1:m22FrOAiuxl/tht9wIqAoGHcbnCCaPWyauO8y2LGGtQ=
|
||||
github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6 h1:zFL2+c3Lb9gEgqKNzowKUPQNb8jV7v5Oaodi/AYFd6c=
|
||||
github.com/btcsuite/btcd/btcutil v1.1.6/go.mod h1:9dFymx8HpuLqBnsPELrImQeTQfKBQqzqGbbV3jK55aE=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
|
||||
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
|
||||
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
|
||||
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
|
||||
github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
|
||||
github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
|
||||
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
|
||||
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
|
||||
github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
|
||||
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
|
||||
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
|
||||
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
@@ -101,20 +63,6 @@ github.com/cloudflare/circl v1.6.2-0.20251204010831-23491bd573cf/go.mod h1:uddAz
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4=
|
||||
github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
|
||||
github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo=
|
||||
github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g=
|
||||
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0 h1:pU88SPhIFid6/k0egdR5V6eALQYq2qbSmukrkgIh/0A=
|
||||
github.com/cockroachdb/fifo v0.0.0-20240816210425-c5d0cb0b6fc0/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
|
||||
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506 h1:ASDL+UJcILMqgNeV5jiqR4j+sTuvQNHdf2chuKj1M5k=
|
||||
github.com/cockroachdb/logtags v0.0.0-20241215232642-bb51bb14a506/go.mod h1:Mw7HqKr2kdtu6aYGn3tPmAftiP3QPX63LdK/zcariIo=
|
||||
github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
|
||||
github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
|
||||
github.com/cockroachdb/redact v1.1.6 h1:zXJBwDZ84xJNlHl1rMyCojqyIxv+7YUpQiJLQ7n4314=
|
||||
github.com/cockroachdb/redact v1.1.6/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb h1:3bCgBvB8PbJVMX1ouCcSIxvsqKPYM7gs72o0zC76n9g=
|
||||
github.com/cockroachdb/tokenbucket v0.0.0-20250429170803-42689b6311bb/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
|
||||
github.com/consensys/gnark-crypto v0.19.2 h1:qrEAIXq3T4egxqiliFFoNrepkIWVEeIYwt3UL0fvS80=
|
||||
github.com/consensys/gnark-crypto v0.19.2/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
@@ -122,28 +70,14 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
|
||||
github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v0.0.0-20161028175848-04cdfd42973b/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/dgraph-io/badger/v4 v4.8.0 h1:JYph1ChBijCw8SLeybvPINizbDKWZ5n/GYbz2yhN/bs=
|
||||
github.com/dgraph-io/badger/v4 v4.8.0/go.mod h1:U6on6e8k/RTbUWxqKR0MvugJuVmkxSNc79ap4917h4w=
|
||||
github.com/dgraph-io/ristretto/v2 v2.2.0 h1:bkY3XzJcXoMuELV8F+vS8kzNgicwQFAaGINAEJdWGOM=
|
||||
github.com/dgraph-io/ristretto/v2 v2.2.0/go.mod h1:RZrm63UmcBAaYWC1DotLYBmTvgkrs0+XhBd7Npn7/zI=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38=
|
||||
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
@@ -154,30 +88,12 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
|
||||
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/getsentry/sentry-go v0.35.1 h1:iopow6UVLE2aXu46xKVIs8Z9D/YZkJrHkgozrxa+tOQ=
|
||||
github.com/getsentry/sentry-go v0.35.1/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
@@ -191,8 +107,6 @@ github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
|
||||
github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U=
|
||||
github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -210,17 +124,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible h1:F3vclr7C3HpB1k9mxCGRMXq6FdUalZ6H/pNX4FP1v0Q=
|
||||
github.com/google/flatbuffers v25.2.10+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
@@ -233,8 +138,6 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
@@ -252,27 +155,13 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
|
||||
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/gorilla/rpc v1.2.1 h1:yC+LMV5esttgpVvNORL/xX4jvTTEUE30UZhZ5JF7K9k=
|
||||
github.com/gorilla/rpc v1.2.1/go.mod h1:uNpOihAlF5xRFLuTYhfR0yfCTm0WTQSQttkMSptRfGk=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -295,24 +184,17 @@ github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2p
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
|
||||
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267 h1:TMtDYDHKYY15rFihtRfck/bfFqNfvcabqvXAFQfAUpY=
|
||||
github.com/jedisct1/go-minisign v0.0.0-20230811132847-661be99b8267/go.mod h1:h1nSAbGFqGVzn6Jyl1R/iCcBUHN4g+gW1u9CoBTrb9E=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
@@ -323,44 +205,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
|
||||
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
|
||||
github.com/luxfi/cache v1.0.0 h1:OcqGi/qs9ky+7O8fR6WMZtFsunvA40LBTg3QRQqJurs=
|
||||
github.com/luxfi/cache v1.0.0/go.mod h1:BEvpffvV3SXs/KSa9nUC1TszxKwUdIHwIaoXpeiYU+Y=
|
||||
github.com/luxfi/consensus v1.22.26 h1:q80Re+qPdkzYLxp+HLJLutcqM64AWsBC1eaN+XaDbV4=
|
||||
github.com/luxfi/consensus v1.22.26/go.mod h1:kFwUKSUwzlVpkvL0PJc0NhYMoBn8BSu8qwjlYl0Iizg=
|
||||
github.com/luxfi/constants v1.2.2 h1:zzmm2CQSrCGqt4SOdHQpS7vZuHXeCgTy4EwK4xtOvbI=
|
||||
github.com/luxfi/constants v1.2.2/go.mod h1:lJSH4WAMaiVsCC5wi3kptAXJrWiNFwORptpaYTUs7P0=
|
||||
github.com/luxfi/database v1.2.12 h1:0TWcVlkotLcn9oFUXeHn5zBT24RxnPJLw9LIgapZIzc=
|
||||
github.com/luxfi/database v1.2.12/go.mod h1:BlczmDTU4z76wPGlhtbzI4iWbEIiWDyfDOCGoq8Cmc0=
|
||||
github.com/luxfi/genesis v1.4.7 h1:v+2rYwpZXY/2bfaIca8lj7oMueS14fPHHGnJMHr6woY=
|
||||
github.com/luxfi/genesis v1.4.7/go.mod h1:kns8O17CH6rjVwS+JI5nGzSL7VuqxDArVFVOmj8t07E=
|
||||
github.com/luxfi/geth v1.16.53 h1:tuOYAUdvVH1BHJWMwv3vzRN/GS+rlI90JrVIDp8DwWA=
|
||||
github.com/luxfi/geth v1.16.53/go.mod h1:/Rh2kzHGzK1HLd9wA6hnpqDwBev/vN+w6E756EY7Rac=
|
||||
github.com/luxfi/ids v1.2.4 h1:e0OaeSI6xWjS9JnxxxfvCCs9HHDUrwDc3M9ctIhjcDI=
|
||||
github.com/luxfi/ids v1.2.4/go.mod h1:HwvRJSNbuZS+u+0JqeHfPX2S13iFyLCnfGxjBCmt244=
|
||||
github.com/luxfi/ledger-lux-go v1.0.0 h1:u5L6hrJU10tOd3+N6tMkq0ba1uWh/rLXvQiKlJoMENE=
|
||||
github.com/luxfi/ledger-lux-go v1.0.0/go.mod h1:bJgOxgG1jE5uz8upWrwoDuCS/gfEQEQkL3TMYjwe5VA=
|
||||
github.com/luxfi/log v1.1.26 h1:ECnJ4wV7TF6WZ5gC6CD6ddZ4OhF+zhQij4bgVUhumDg=
|
||||
github.com/luxfi/log v1.1.26/go.mod h1:ACN1FtMlJ/NRuWROYH2TtiFmJuUbSG+OxINlRzJM/38=
|
||||
github.com/luxfi/math v1.0.2 h1:bgBGmVC7S/QxLCgAJZJdvjFHMNXygcApcKofIn7bWpU=
|
||||
github.com/luxfi/math v1.0.2/go.mod h1:2Jk1/s0f0yN2q0Sx/cBExbFJz7IDEbjxlP3VUiaujms=
|
||||
github.com/luxfi/metric v1.4.8 h1:mMbcJW+E1z6qQxn34+BE109HlvT6ciiDXuc10qEBJRs=
|
||||
github.com/luxfi/metric v1.4.8/go.mod h1:AUQ7NSNz9WndAcr/SKnOkP7XSFFnBXOa+ihtJYfDaQY=
|
||||
github.com/luxfi/mock v0.1.0 h1:IwElfNu+T9sXvzFX6tudPDx1vqPuACRSRdxpD5lxW+o=
|
||||
github.com/luxfi/mock v0.1.0/go.mod h1:izF+9K0gGzFC9zERn6Po37v46eLdPB+EIsDjL3GLk+U=
|
||||
github.com/luxfi/node v1.22.14 h1:hravEERCTm9/W6pRZyLJ+c97K/HHwHIK86X4+NOwLsk=
|
||||
github.com/luxfi/node v1.22.14/go.mod h1:uAAABB2ny8xOGueJGw3euXA9nadf73100qj+Ei2hiG0=
|
||||
github.com/luxfi/p2p v1.4.6 h1:Xxu69AfFOGwMvtwDCbaf4bpRS3bvj57xRwBgqxbICdM=
|
||||
github.com/luxfi/p2p v1.4.6/go.mod h1:i3nwxhknQ86tsM7UC+QlQXHL11gIucd45os0jMKKIEg=
|
||||
github.com/luxfi/trace v0.1.4 h1:ttCRyXGwWuz232se+lIUqhWHBoTuvPLhHH/hLWyqtaM=
|
||||
github.com/luxfi/trace v0.1.4/go.mod h1:Az7HWh+PCuPftXjQu+ssjv51nKauaFu+q2un7bmZYBA=
|
||||
github.com/luxfi/utils v1.0.0 h1:eibxyw/xkwnB9KRSmYvKLbdLTUElyfJpxLIBeTBKqlU=
|
||||
github.com/luxfi/utils v1.0.0/go.mod h1:ABqhBdGNig0CaDcnNYldv1byS0BTNi5IKPbJSPF1p98=
|
||||
github.com/luxfi/warp v1.16.36 h1:zznGGx91vYI06MBG441jE7wzd3VSMGXERiGVTtN2KJI=
|
||||
github.com/luxfi/warp v1.16.36/go.mod h1:zXX6/GErHux4xaMy7nV8c4sdFQLFX6iva08kBxGPylo=
|
||||
github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
@@ -378,70 +230,24 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||
github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 h1:4kuARK6Y6FxaNu/BnU2OAaLF86eTVhP2hjTB6iMvItA=
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354/go.mod h1:KSVJerMDfblTH7p5MZaTt+8zaT2iEk3AkVb9PQdZuE8=
|
||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
||||
github.com/neelance/sourcemap v0.0.0-20200213170602-2833bce08e4c/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
|
||||
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
|
||||
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
|
||||
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
|
||||
github.com/onsi/gomega v1.38.0 h1:c/WX+w8SLAinvuKKQFh77WEucCnPk4j2OTUr7lt7BeY=
|
||||
github.com/onsi/gomega v1.38.0/go.mod h1:OcXcwId0b9QsE7Y49u+BTrL4IdKOBOKnD6VQNTJEB6o=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pires/go-proxyproto v0.8.1 h1:9KEixbdJfhrbtjpz/ZwCdWDD2Xem0NZ38qMYaASJgp0=
|
||||
github.com/pires/go-proxyproto v0.8.1/go.mod h1:ZKAAyp3cgy5Y5Mo4n9AlScrkCZwUy0g3Jf+slqQVcuU=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.67.4 h1:yR3NqWO1/UyO1w2PhUvXlGQs/PtFmoveVO0KZ4+Lvsc=
|
||||
github.com/prometheus/common v0.67.4/go.mod h1:gP0fq6YjjNCLssJCQp0yk4M8W6ikLURwkdd/YKtTbyI=
|
||||
github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws=
|
||||
github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
|
||||
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
|
||||
github.com/sanity-io/litter v1.5.5/go.mod h1:9gzJgR2i4ZpjZHsKvUXIRQVk7P+yM3e+jAF7bU2UI5U=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/go v0.0.0-20200502201357-93f07166e636/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
@@ -451,8 +257,6 @@ github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
|
||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.2.1/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk=
|
||||
@@ -460,55 +264,29 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v0.0.0-20161117074351-18a02ba4a312/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.1.4/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/supranational/blst v0.3.16 h1:bTDadT+3fK497EvLdWRQEjiGnUtzJ7jjIUMF0jqwYhE=
|
||||
github.com/supranational/blst v0.3.16/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI=
|
||||
github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48=
|
||||
github.com/thepudds/fzgen v0.4.3 h1:srUP/34BulQaEwPP/uHZkdjUcUjIzL7Jkf4CBVryiP8=
|
||||
github.com/thepudds/fzgen v0.4.3/go.mod h1:BhhwtRhzgvLWAjjcHDJ9pEiLD2Z9hrVIFjBCHJ//zJ4=
|
||||
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
|
||||
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
|
||||
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
|
||||
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
|
||||
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
github.com/zondax/golem v0.27.0 h1:IbBjGIXF3SoGOZHsILJvIM/F/ylwJzMcHAcggiqniPw=
|
||||
github.com/zondax/golem v0.27.0/go.mod h1:AmorCgJPt00L8xN1VrMBe13PSifoZksnQ1Ge906bu4A=
|
||||
github.com/zondax/hid v0.9.2 h1:WCJFnEDMiqGF64nlZz28E9qLVZ0KSJ7xpc5DLEyma2U=
|
||||
github.com/zondax/hid v0.9.2/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
|
||||
github.com/zondax/ledger-go v1.0.1 h1:Ks/2tz/dOF+dbRynfZ0dEhcdL1lqw43Sa0zMXHpQ3aQ=
|
||||
github.com/zondax/ledger-go v1.0.1/go.mod h1:j7IgMY39f30apthJYMd1YsHZRqdyu4KbVmUp0nU78X0=
|
||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
|
||||
go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ=
|
||||
@@ -519,47 +297,21 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
|
||||
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
|
||||
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
|
||||
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
|
||||
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
|
||||
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
|
||||
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
@@ -602,16 +354,11 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
|
||||
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -634,11 +381,9 @@ golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
@@ -648,15 +393,9 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -686,7 +425,6 @@ golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -696,12 +434,9 @@ golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -715,16 +450,13 @@ golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -732,16 +464,11 @@ golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||
@@ -757,17 +484,12 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
|
||||
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
@@ -815,25 +537,18 @@ golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82u
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
|
||||
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
|
||||
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
@@ -904,10 +619,6 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
|
||||
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
|
||||
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a h1:DMCgtIAIQGZqJXMVzJF4MV8BlWoJh2ZuFiRdAleyr58=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250811230008-5f3141c8851a/go.mod h1:y2yVLIE/CSMCPXaHnSKXxu1spLPnglFLegmgdY23uuE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
@@ -928,8 +639,6 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -942,25 +651,17 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import (
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/luxfi/crypto/cache/lru"
|
||||
"github.com/luxfi/cache/lru"
|
||||
"github.com/luxfi/crypto/cb58"
|
||||
"github.com/luxfi/crypto/hashing"
|
||||
"github.com/luxfi/ids"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
package secp256k1
|
||||
|
||||
import (
|
||||
"github.com/luxfi/crypto/cache/lru"
|
||||
"github.com/luxfi/cache/lru"
|
||||
)
|
||||
|
||||
// RecoverCacheType provides a cache for public key recovery with methods
|
||||
|
||||
@@ -9,7 +9,7 @@ import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/luxfi/node/utils/hashing"
|
||||
"github.com/luxfi/crypto/hashing"
|
||||
)
|
||||
|
||||
// FuzzSignatureVerification tests signature verification with random inputs
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
_ json.Marshaler = (*Atomic[struct{}])(nil)
|
||||
_ json.Unmarshaler = (*Atomic[struct{}])(nil)
|
||||
)
|
||||
|
||||
type Atomic[T any] struct {
|
||||
lock sync.RWMutex
|
||||
value T
|
||||
}
|
||||
|
||||
func NewAtomic[T any](value T) *Atomic[T] {
|
||||
return &Atomic[T]{
|
||||
value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Atomic[T]) Get() T {
|
||||
a.lock.RLock()
|
||||
defer a.lock.RUnlock()
|
||||
|
||||
return a.value
|
||||
}
|
||||
|
||||
func (a *Atomic[T]) Set(value T) {
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
a.value = value
|
||||
}
|
||||
|
||||
func (a *Atomic[T]) Swap(value T) T {
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
old := a.value
|
||||
a.value = value
|
||||
|
||||
return old
|
||||
}
|
||||
|
||||
func (a *Atomic[T]) MarshalJSON() ([]byte, error) {
|
||||
a.lock.RLock()
|
||||
defer a.lock.RUnlock()
|
||||
|
||||
return json.Marshal(a.value)
|
||||
}
|
||||
|
||||
func (a *Atomic[T]) UnmarshalJSON(b []byte) error {
|
||||
a.lock.Lock()
|
||||
defer a.lock.Unlock()
|
||||
|
||||
return json.Unmarshal(b, &a.value)
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestAtomic(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
var a Atomic[bool]
|
||||
require.Zero(a.Get())
|
||||
|
||||
a.Set(false)
|
||||
require.False(a.Get())
|
||||
|
||||
a.Set(true)
|
||||
require.True(a.Get())
|
||||
|
||||
a.Set(false)
|
||||
require.False(a.Get())
|
||||
}
|
||||
|
||||
func TestAtomicJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value *Atomic[netip.AddrPort]
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "zero value",
|
||||
value: new(Atomic[netip.AddrPort]),
|
||||
expected: `""`,
|
||||
},
|
||||
{
|
||||
name: "ipv4 value",
|
||||
value: NewAtomic(netip.AddrPortFrom(
|
||||
netip.AddrFrom4([4]byte{1, 2, 3, 4}),
|
||||
12345,
|
||||
)),
|
||||
expected: `"1.2.3.4:12345"`,
|
||||
},
|
||||
{
|
||||
name: "ipv6 loopback",
|
||||
value: NewAtomic(netip.AddrPortFrom(
|
||||
netip.IPv6Loopback(),
|
||||
12345,
|
||||
)),
|
||||
expected: `"[::1]:12345"`,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
b, err := json.Marshal(test.value)
|
||||
require.NoError(err)
|
||||
require.Equal(test.expected, string(b))
|
||||
|
||||
var parsed Atomic[netip.AddrPort]
|
||||
require.NoError(json.Unmarshal([]byte(test.expected), &parsed))
|
||||
require.Equal(test.value.Get(), parsed.Get())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
var _ Beacon = (*beacon)(nil)
|
||||
|
||||
type Beacon interface {
|
||||
ID() ids.NodeID
|
||||
IP() netip.AddrPort
|
||||
}
|
||||
|
||||
type beacon struct {
|
||||
id ids.NodeID
|
||||
ip netip.AddrPort
|
||||
}
|
||||
|
||||
func New(id ids.NodeID, ip netip.AddrPort) Beacon {
|
||||
return &beacon{
|
||||
id: id,
|
||||
ip: ip,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *beacon) ID() ids.NodeID {
|
||||
return b.id
|
||||
}
|
||||
|
||||
func (b *beacon) IP() netip.AddrPort {
|
||||
return b.ip
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
var (
|
||||
_ Set = (*set)(nil)
|
||||
|
||||
errDuplicateID = errors.New("duplicated ID")
|
||||
errDuplicateIP = errors.New("duplicated IP")
|
||||
|
||||
errUnknownID = errors.New("unknown ID")
|
||||
errUnknownIP = errors.New("unknown IP")
|
||||
)
|
||||
|
||||
type Set interface {
|
||||
Add(Beacon) error
|
||||
|
||||
RemoveByID(ids.NodeID) error
|
||||
RemoveByIP(netip.AddrPort) error
|
||||
|
||||
Len() int
|
||||
|
||||
IDsArg() string
|
||||
IPsArg() string
|
||||
}
|
||||
|
||||
type set struct {
|
||||
ids map[ids.NodeID]int
|
||||
ips map[netip.AddrPort]int
|
||||
beacons []Beacon
|
||||
}
|
||||
|
||||
func NewSet() Set {
|
||||
return &set{
|
||||
ids: make(map[ids.NodeID]int),
|
||||
ips: make(map[netip.AddrPort]int),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *set) Add(b Beacon) error {
|
||||
id := b.ID()
|
||||
_, duplicateID := s.ids[id]
|
||||
if duplicateID {
|
||||
return errDuplicateID
|
||||
}
|
||||
|
||||
ip := b.IP()
|
||||
_, duplicateIP := s.ips[ip]
|
||||
if duplicateIP {
|
||||
return errDuplicateIP
|
||||
}
|
||||
|
||||
s.ids[id] = len(s.beacons)
|
||||
s.ips[ip] = len(s.beacons)
|
||||
s.beacons = append(s.beacons, b)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *set) RemoveByID(idToRemove ids.NodeID) error {
|
||||
indexToRemove, exists := s.ids[idToRemove]
|
||||
if !exists {
|
||||
return errUnknownID
|
||||
}
|
||||
toRemove := s.beacons[indexToRemove]
|
||||
ipToRemove := toRemove.IP()
|
||||
|
||||
indexToMove := len(s.beacons) - 1
|
||||
toMove := s.beacons[indexToMove]
|
||||
idToMove := toMove.ID()
|
||||
ipToMove := toMove.IP()
|
||||
|
||||
s.ids[idToMove] = indexToRemove
|
||||
s.ips[ipToMove] = indexToRemove
|
||||
s.beacons[indexToRemove] = toMove
|
||||
|
||||
delete(s.ids, idToRemove)
|
||||
delete(s.ips, ipToRemove)
|
||||
s.beacons[indexToMove] = nil
|
||||
s.beacons = s.beacons[:indexToMove]
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *set) RemoveByIP(ip netip.AddrPort) error {
|
||||
indexToRemove, exists := s.ips[ip]
|
||||
if !exists {
|
||||
return errUnknownIP
|
||||
}
|
||||
toRemove := s.beacons[indexToRemove]
|
||||
idToRemove := toRemove.ID()
|
||||
return s.RemoveByID(idToRemove)
|
||||
}
|
||||
|
||||
func (s *set) Len() int {
|
||||
return len(s.beacons)
|
||||
}
|
||||
|
||||
func (s *set) IDsArg() string {
|
||||
sb := strings.Builder{}
|
||||
if len(s.beacons) == 0 {
|
||||
return ""
|
||||
}
|
||||
b := s.beacons[0]
|
||||
_, _ = sb.WriteString(b.ID().String())
|
||||
for _, b := range s.beacons[1:] {
|
||||
_, _ = sb.WriteString(",")
|
||||
_, _ = sb.WriteString(b.ID().String())
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func (s *set) IPsArg() string {
|
||||
sb := strings.Builder{}
|
||||
if len(s.beacons) == 0 {
|
||||
return ""
|
||||
}
|
||||
b := s.beacons[0]
|
||||
_, _ = sb.WriteString(b.IP().String())
|
||||
for _, b := range s.beacons[1:] {
|
||||
_, _ = sb.WriteString(",")
|
||||
_, _ = sb.WriteString(b.IP().String())
|
||||
}
|
||||
return sb.String()
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package beacon
|
||||
|
||||
import (
|
||||
"net/netip"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
)
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
id0 := ids.BuildTestNodeID([]byte{0})
|
||||
id1 := ids.BuildTestNodeID([]byte{1})
|
||||
id2 := ids.BuildTestNodeID([]byte{2})
|
||||
|
||||
ip0 := netip.AddrPortFrom(
|
||||
netip.IPv4Unspecified(),
|
||||
0,
|
||||
)
|
||||
ip1 := netip.AddrPortFrom(
|
||||
netip.IPv4Unspecified(),
|
||||
1,
|
||||
)
|
||||
ip2 := netip.AddrPortFrom(
|
||||
netip.IPv4Unspecified(),
|
||||
2,
|
||||
)
|
||||
|
||||
b0 := New(id0, ip0)
|
||||
b1 := New(id1, ip1)
|
||||
b2 := New(id2, ip2)
|
||||
|
||||
s := NewSet()
|
||||
|
||||
require.Empty(s.IDsArg())
|
||||
require.Empty(s.IPsArg())
|
||||
require.Zero(s.Len())
|
||||
|
||||
require.NoError(s.Add(b0))
|
||||
|
||||
require.Equal("NodeID-111111111111111111116DBWJs", s.IDsArg())
|
||||
require.Equal("0.0.0.0:0", s.IPsArg())
|
||||
require.Equal(1, s.Len())
|
||||
|
||||
err := s.Add(b0)
|
||||
require.ErrorIs(err, errDuplicateID)
|
||||
|
||||
require.Equal("NodeID-111111111111111111116DBWJs", s.IDsArg())
|
||||
require.Equal("0.0.0.0:0", s.IPsArg())
|
||||
require.Equal(1, s.Len())
|
||||
|
||||
require.NoError(s.Add(b1))
|
||||
|
||||
require.Equal("NodeID-111111111111111111116DBWJs,NodeID-6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt", s.IDsArg())
|
||||
require.Equal("0.0.0.0:0,0.0.0.0:1", s.IPsArg())
|
||||
require.Equal(2, s.Len())
|
||||
|
||||
require.NoError(s.Add(b2))
|
||||
|
||||
require.Equal("NodeID-111111111111111111116DBWJs,NodeID-6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt,NodeID-BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp", s.IDsArg())
|
||||
require.Equal("0.0.0.0:0,0.0.0.0:1,0.0.0.0:2", s.IPsArg())
|
||||
require.Equal(3, s.Len())
|
||||
|
||||
require.NoError(s.RemoveByID(b0.ID()))
|
||||
|
||||
require.Equal("NodeID-BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp,NodeID-6HgC8KRBEhXYbF4riJyJFLSHt37UNuRt", s.IDsArg())
|
||||
require.Equal("0.0.0.0:2,0.0.0.0:1", s.IPsArg())
|
||||
require.Equal(2, s.Len())
|
||||
|
||||
require.NoError(s.RemoveByIP(b1.IP()))
|
||||
|
||||
require.Equal("NodeID-BaMPFdqMUQ46BV8iRcwbVfsam55kMqcp", s.IDsArg())
|
||||
require.Equal("0.0.0.0:2", s.IPsArg())
|
||||
require.Equal(1, s.Len())
|
||||
}
|
||||
@@ -1,154 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bimap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
_ json.Marshaler = (*BiMap[int, int])(nil)
|
||||
_ json.Unmarshaler = (*BiMap[int, int])(nil)
|
||||
|
||||
nullBytes = []byte("null")
|
||||
errNotBijective = errors.New("map not bijective")
|
||||
)
|
||||
|
||||
type Entry[K, V any] struct {
|
||||
Key K
|
||||
Value V
|
||||
}
|
||||
|
||||
// BiMap is a bi-directional map.
|
||||
type BiMap[K, V comparable] struct {
|
||||
keyToValue map[K]V
|
||||
valueToKey map[V]K
|
||||
}
|
||||
|
||||
// New creates a new empty bimap.
|
||||
func New[K, V comparable]() *BiMap[K, V] {
|
||||
return &BiMap[K, V]{
|
||||
keyToValue: make(map[K]V),
|
||||
valueToKey: make(map[V]K),
|
||||
}
|
||||
}
|
||||
|
||||
// Put the key value pair into the map. If either [key] or [val] was previously
|
||||
// in the map, the previous entries will be removed and returned.
|
||||
//
|
||||
// Note: Unlike normal maps, it's possible that Put removes 0, 1, or 2 existing
|
||||
// entries to ensure that mappings are one-to-one.
|
||||
func (m *BiMap[K, V]) Put(key K, val V) []Entry[K, V] {
|
||||
var removed []Entry[K, V]
|
||||
oldVal, oldValDeleted := m.DeleteKey(key)
|
||||
if oldValDeleted {
|
||||
removed = append(removed, Entry[K, V]{
|
||||
Key: key,
|
||||
Value: oldVal,
|
||||
})
|
||||
}
|
||||
oldKey, oldKeyDeleted := m.DeleteValue(val)
|
||||
if oldKeyDeleted {
|
||||
removed = append(removed, Entry[K, V]{
|
||||
Key: oldKey,
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
m.keyToValue[key] = val
|
||||
m.valueToKey[val] = key
|
||||
return removed
|
||||
}
|
||||
|
||||
// GetKey that maps to the provided value.
|
||||
func (m *BiMap[K, V]) GetKey(val V) (K, bool) {
|
||||
key, ok := m.valueToKey[val]
|
||||
return key, ok
|
||||
}
|
||||
|
||||
// GetValue that is mapped to the provided key.
|
||||
func (m *BiMap[K, V]) GetValue(key K) (V, bool) {
|
||||
val, ok := m.keyToValue[key]
|
||||
return val, ok
|
||||
}
|
||||
|
||||
// HasKey returns true if [key] is in the map.
|
||||
func (m *BiMap[K, _]) HasKey(key K) bool {
|
||||
_, ok := m.keyToValue[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
// HasValue returns true if [val] is in the map.
|
||||
func (m *BiMap[_, V]) HasValue(val V) bool {
|
||||
_, ok := m.valueToKey[val]
|
||||
return ok
|
||||
}
|
||||
|
||||
// DeleteKey removes [key] from the map and returns the value it mapped to.
|
||||
func (m *BiMap[K, V]) DeleteKey(key K) (V, bool) {
|
||||
val, ok := m.keyToValue[key]
|
||||
if !ok {
|
||||
return utils.Zero[V](), false
|
||||
}
|
||||
delete(m.keyToValue, key)
|
||||
delete(m.valueToKey, val)
|
||||
return val, true
|
||||
}
|
||||
|
||||
// DeleteValue removes [val] from the map and returns the key that mapped to it.
|
||||
func (m *BiMap[K, V]) DeleteValue(val V) (K, bool) {
|
||||
key, ok := m.valueToKey[val]
|
||||
if !ok {
|
||||
return utils.Zero[K](), false
|
||||
}
|
||||
delete(m.keyToValue, key)
|
||||
delete(m.valueToKey, val)
|
||||
return key, true
|
||||
}
|
||||
|
||||
// Keys returns the keys of the map. The keys will be in an indeterminate order.
|
||||
func (m *BiMap[K, _]) Keys() []K {
|
||||
return maps.Keys(m.keyToValue)
|
||||
}
|
||||
|
||||
// Values returns the values of the map. The values will be in an indeterminate
|
||||
// order.
|
||||
func (m *BiMap[_, V]) Values() []V {
|
||||
return maps.Values(m.keyToValue)
|
||||
}
|
||||
|
||||
// Len return the number of entries in this map.
|
||||
func (m *BiMap[K, V]) Len() int {
|
||||
return len(m.keyToValue)
|
||||
}
|
||||
|
||||
func (m *BiMap[K, V]) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(m.keyToValue)
|
||||
}
|
||||
|
||||
func (m *BiMap[K, V]) UnmarshalJSON(b []byte) error {
|
||||
if bytes.Equal(b, nullBytes) {
|
||||
return nil
|
||||
}
|
||||
var keyToValue map[K]V
|
||||
if err := json.Unmarshal(b, &keyToValue); err != nil {
|
||||
return err
|
||||
}
|
||||
valueToKey := make(map[V]K, len(keyToValue))
|
||||
for k, v := range keyToValue {
|
||||
valueToKey[v] = k
|
||||
}
|
||||
if len(keyToValue) != len(valueToKey) {
|
||||
return errNotBijective
|
||||
}
|
||||
|
||||
m.keyToValue = keyToValue
|
||||
m.valueToKey = valueToKey
|
||||
return nil
|
||||
}
|
||||
@@ -1,366 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bimap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBiMapPut(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
state *BiMap[int, int]
|
||||
key int
|
||||
value int
|
||||
expectedRemoved []Entry[int, int]
|
||||
expectedState *BiMap[int, int]
|
||||
}{
|
||||
{
|
||||
name: "none removed",
|
||||
state: New[int, int](),
|
||||
key: 1,
|
||||
value: 2,
|
||||
expectedRemoved: nil,
|
||||
expectedState: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
1: 2,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
2: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "key removed",
|
||||
state: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
1: 2,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
2: 1,
|
||||
},
|
||||
},
|
||||
key: 1,
|
||||
value: 3,
|
||||
expectedRemoved: []Entry[int, int]{
|
||||
{
|
||||
Key: 1,
|
||||
Value: 2,
|
||||
},
|
||||
},
|
||||
expectedState: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
1: 3,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
3: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "value removed",
|
||||
state: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
1: 2,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
2: 1,
|
||||
},
|
||||
},
|
||||
key: 3,
|
||||
value: 2,
|
||||
expectedRemoved: []Entry[int, int]{
|
||||
{
|
||||
Key: 1,
|
||||
Value: 2,
|
||||
},
|
||||
},
|
||||
expectedState: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
3: 2,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
2: 3,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "key and value removed",
|
||||
state: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
1: 2,
|
||||
3: 4,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
2: 1,
|
||||
4: 3,
|
||||
},
|
||||
},
|
||||
key: 1,
|
||||
value: 4,
|
||||
expectedRemoved: []Entry[int, int]{
|
||||
{
|
||||
Key: 1,
|
||||
Value: 2,
|
||||
},
|
||||
{
|
||||
Key: 3,
|
||||
Value: 4,
|
||||
},
|
||||
},
|
||||
expectedState: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
1: 4,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
4: 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
removed := test.state.Put(test.key, test.value)
|
||||
require.Equal(test.expectedRemoved, removed)
|
||||
require.Equal(test.expectedState, test.state)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBiMapHasValueAndGetKey(t *testing.T) {
|
||||
m := New[int, int]()
|
||||
require.Empty(t, m.Put(1, 2))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
value int
|
||||
expectedKey int
|
||||
expectedExists bool
|
||||
}{
|
||||
{
|
||||
name: "fetch unknown",
|
||||
value: 3,
|
||||
expectedKey: 0,
|
||||
expectedExists: false,
|
||||
},
|
||||
{
|
||||
name: "fetch known value",
|
||||
value: 2,
|
||||
expectedKey: 1,
|
||||
expectedExists: true,
|
||||
},
|
||||
{
|
||||
name: "fetch known key",
|
||||
value: 1,
|
||||
expectedKey: 0,
|
||||
expectedExists: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
exists := m.HasValue(test.value)
|
||||
require.Equal(test.expectedExists, exists)
|
||||
|
||||
key, exists := m.GetKey(test.value)
|
||||
require.Equal(test.expectedKey, key)
|
||||
require.Equal(test.expectedExists, exists)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBiMapHasKeyAndGetValue(t *testing.T) {
|
||||
m := New[int, int]()
|
||||
require.Empty(t, m.Put(1, 2))
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
key int
|
||||
expectedValue int
|
||||
expectedExists bool
|
||||
}{
|
||||
{
|
||||
name: "fetch unknown",
|
||||
key: 3,
|
||||
expectedValue: 0,
|
||||
expectedExists: false,
|
||||
},
|
||||
{
|
||||
name: "fetch known key",
|
||||
key: 1,
|
||||
expectedValue: 2,
|
||||
expectedExists: true,
|
||||
},
|
||||
{
|
||||
name: "fetch known value",
|
||||
key: 2,
|
||||
expectedValue: 0,
|
||||
expectedExists: false,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
exists := m.HasKey(test.key)
|
||||
require.Equal(test.expectedExists, exists)
|
||||
|
||||
value, exists := m.GetValue(test.key)
|
||||
require.Equal(test.expectedValue, value)
|
||||
require.Equal(test.expectedExists, exists)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBiMapDeleteKey(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
state *BiMap[int, int]
|
||||
key int
|
||||
expectedValue int
|
||||
expectedRemoved bool
|
||||
expectedState *BiMap[int, int]
|
||||
}{
|
||||
{
|
||||
name: "none removed",
|
||||
state: New[int, int](),
|
||||
key: 1,
|
||||
expectedValue: 0,
|
||||
expectedRemoved: false,
|
||||
expectedState: New[int, int](),
|
||||
},
|
||||
{
|
||||
name: "key removed",
|
||||
state: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
1: 2,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
2: 1,
|
||||
},
|
||||
},
|
||||
key: 1,
|
||||
expectedValue: 2,
|
||||
expectedRemoved: true,
|
||||
expectedState: New[int, int](),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
value, removed := test.state.DeleteKey(test.key)
|
||||
require.Equal(test.expectedValue, value)
|
||||
require.Equal(test.expectedRemoved, removed)
|
||||
require.Equal(test.expectedState, test.state)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBiMapDeleteValue(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
state *BiMap[int, int]
|
||||
value int
|
||||
expectedKey int
|
||||
expectedRemoved bool
|
||||
expectedState *BiMap[int, int]
|
||||
}{
|
||||
{
|
||||
name: "none removed",
|
||||
state: New[int, int](),
|
||||
value: 1,
|
||||
expectedKey: 0,
|
||||
expectedRemoved: false,
|
||||
expectedState: New[int, int](),
|
||||
},
|
||||
{
|
||||
name: "key removed",
|
||||
state: &BiMap[int, int]{
|
||||
keyToValue: map[int]int{
|
||||
1: 2,
|
||||
},
|
||||
valueToKey: map[int]int{
|
||||
2: 1,
|
||||
},
|
||||
},
|
||||
value: 2,
|
||||
expectedKey: 1,
|
||||
expectedRemoved: true,
|
||||
expectedState: New[int, int](),
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
key, removed := test.state.DeleteValue(test.value)
|
||||
require.Equal(test.expectedKey, key)
|
||||
require.Equal(test.expectedRemoved, removed)
|
||||
require.Equal(test.expectedState, test.state)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBiMapLenAndLists(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
m := New[int, int]()
|
||||
require.Zero(m.Len())
|
||||
require.Empty(m.Keys())
|
||||
require.Empty(m.Values())
|
||||
|
||||
m.Put(1, 2)
|
||||
require.Equal(1, m.Len())
|
||||
require.ElementsMatch([]int{1}, m.Keys())
|
||||
require.ElementsMatch([]int{2}, m.Values())
|
||||
|
||||
m.Put(2, 3)
|
||||
require.Equal(2, m.Len())
|
||||
require.ElementsMatch([]int{1, 2}, m.Keys())
|
||||
require.ElementsMatch([]int{2, 3}, m.Values())
|
||||
|
||||
m.Put(1, 3)
|
||||
require.Equal(1, m.Len())
|
||||
require.ElementsMatch([]int{1}, m.Keys())
|
||||
require.ElementsMatch([]int{3}, m.Values())
|
||||
|
||||
m.DeleteKey(1)
|
||||
require.Zero(m.Len())
|
||||
require.Empty(m.Keys())
|
||||
require.Empty(m.Values())
|
||||
}
|
||||
|
||||
func TestBiMapJSON(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
expectedMap := New[int, int]()
|
||||
expectedMap.Put(1, 2)
|
||||
expectedMap.Put(2, 3)
|
||||
|
||||
jsonBytes, err := json.Marshal(expectedMap)
|
||||
require.NoError(err)
|
||||
|
||||
expectedJSONBytes := []byte(`{"1":2,"2":3}`)
|
||||
require.JSONEq(string(expectedJSONBytes), string(jsonBytes))
|
||||
|
||||
var unmarshalledMap BiMap[int, int]
|
||||
require.NoError(json.Unmarshal(jsonBytes, &unmarshalledMap))
|
||||
require.Equal(expectedMap, &unmarshalledMap)
|
||||
}
|
||||
|
||||
func TestBiMapInvalidJSON(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
invalidJSONBytes := []byte(`{"1":2,"2":2}`)
|
||||
var unmarshalledMap BiMap[int, int]
|
||||
err := json.Unmarshal(invalidJSONBytes, &unmarshalledMap)
|
||||
require.ErrorIs(err, errNotBijective)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
var (
|
||||
require = require.New(t)
|
||||
count = 10000
|
||||
p = 0.1
|
||||
)
|
||||
|
||||
numHashes, numEntries := OptimalParameters(count, p)
|
||||
f, err := New(numHashes, numEntries)
|
||||
require.NoError(err)
|
||||
require.NotNil(f)
|
||||
|
||||
salt := []byte("test salt")
|
||||
Add(f, []byte("hello"), salt)
|
||||
|
||||
contains := Contains(f, []byte("hello"), salt)
|
||||
require.True(contains, "should have contained the key")
|
||||
|
||||
contains = Contains(f, []byte("bye"), salt)
|
||||
require.False(contains, "shouldn't have contained the key")
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
// BloomFilter is the interface for bloom filter implementations that support
|
||||
// adding and checking raw byte slices
|
||||
type BloomFilter interface {
|
||||
// Add adds to filter, assumed thread safe
|
||||
Add(...[]byte)
|
||||
|
||||
// Check checks filter, assumed thread safe
|
||||
Check([]byte) bool
|
||||
}
|
||||
@@ -1,147 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/bits"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
minHashes = 1
|
||||
maxHashes = 16 // Supports a false positive probability of 2^-16 when using optimal size values
|
||||
minEntries = 1
|
||||
|
||||
bitsPerByte = 8
|
||||
bytesPerUint64 = 8
|
||||
hashRotation = 17
|
||||
)
|
||||
|
||||
var (
|
||||
errInvalidNumHashes = errors.New("invalid num hashes")
|
||||
errTooFewHashes = errors.New("too few hashes")
|
||||
errTooManyHashes = errors.New("too many hashes")
|
||||
errTooFewEntries = errors.New("too few entries")
|
||||
)
|
||||
|
||||
type Filter struct {
|
||||
// numBits is always equal to [bitsPerByte * len(entries)]
|
||||
numBits uint64
|
||||
|
||||
lock sync.RWMutex
|
||||
hashSeeds []uint64
|
||||
entries []byte
|
||||
count int
|
||||
}
|
||||
|
||||
// New creates a new Filter with the specified number of hashes and bytes for
|
||||
// entries. The returned bloom filter is safe for concurrent usage.
|
||||
func New(numHashes, numEntries int) (*Filter, error) {
|
||||
if numEntries < minEntries {
|
||||
return nil, errTooFewEntries
|
||||
}
|
||||
|
||||
hashSeeds, err := newHashSeeds(numHashes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Filter{
|
||||
numBits: uint64(numEntries * bitsPerByte),
|
||||
hashSeeds: hashSeeds,
|
||||
entries: make([]byte, numEntries),
|
||||
count: 0,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (f *Filter) Add(hash uint64) {
|
||||
f.lock.Lock()
|
||||
defer f.lock.Unlock()
|
||||
|
||||
_ = 1 % f.numBits // hint to the compiler that numBits is not 0
|
||||
for _, seed := range f.hashSeeds {
|
||||
hash = bits.RotateLeft64(hash, hashRotation) ^ seed
|
||||
index := hash % f.numBits
|
||||
byteIndex := index / bitsPerByte
|
||||
bitIndex := index % bitsPerByte
|
||||
f.entries[byteIndex] |= 1 << bitIndex
|
||||
}
|
||||
f.count++
|
||||
}
|
||||
|
||||
// Count returns the number of elements that have been added to the bloom
|
||||
// filter.
|
||||
func (f *Filter) Count() int {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return f.count
|
||||
}
|
||||
|
||||
func (f *Filter) Contains(hash uint64) bool {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return contains(f.hashSeeds, f.entries, hash)
|
||||
}
|
||||
|
||||
func (f *Filter) Marshal() []byte {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return marshal(f.hashSeeds, f.entries)
|
||||
}
|
||||
|
||||
func newHashSeeds(count int) ([]uint64, error) {
|
||||
switch {
|
||||
case count < minHashes:
|
||||
return nil, fmt.Errorf("%w: %d < %d", errTooFewHashes, count, minHashes)
|
||||
case count > maxHashes:
|
||||
return nil, fmt.Errorf("%w: %d > %d", errTooManyHashes, count, maxHashes)
|
||||
}
|
||||
|
||||
bytes := make([]byte, count*bytesPerUint64)
|
||||
if _, err := rand.Reader.Read(bytes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
seeds := make([]uint64, count)
|
||||
for i := range seeds {
|
||||
seeds[i] = binary.BigEndian.Uint64(bytes[i*bytesPerUint64:])
|
||||
}
|
||||
return seeds, nil
|
||||
}
|
||||
|
||||
func contains(hashSeeds []uint64, entries []byte, hash uint64) bool {
|
||||
var (
|
||||
numBits = bitsPerByte * uint64(len(entries))
|
||||
_ = 1 % numBits // hint to the compiler that numBits is not 0
|
||||
accumulator byte = 1
|
||||
)
|
||||
for seedIndex := 0; seedIndex < len(hashSeeds) && accumulator != 0; seedIndex++ {
|
||||
hash = bits.RotateLeft64(hash, hashRotation) ^ hashSeeds[seedIndex]
|
||||
index := hash % numBits
|
||||
byteIndex := index / bitsPerByte
|
||||
bitIndex := index % bitsPerByte
|
||||
accumulator &= entries[byteIndex] >> bitIndex
|
||||
}
|
||||
return accumulator != 0
|
||||
}
|
||||
|
||||
func marshal(hashSeeds []uint64, entries []byte) []byte {
|
||||
numHashes := len(hashSeeds)
|
||||
entriesOffset := 1 + numHashes*bytesPerUint64
|
||||
|
||||
bytes := make([]byte, entriesOffset+len(entries))
|
||||
bytes[0] = byte(numHashes)
|
||||
for i, seed := range hashSeeds {
|
||||
binary.BigEndian.PutUint64(bytes[1+i*bytesPerUint64:], seed)
|
||||
}
|
||||
copy(bytes[entriesOffset:], entries)
|
||||
return bytes
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/spaolacci/murmur3"
|
||||
)
|
||||
|
||||
// marshalCommon serializes [hashSeeds] and [entries] into a byte slice.
|
||||
func marshalCommon(hashSeeds []uint64, entries []byte) []byte {
|
||||
bytes := make([]byte, 1+len(hashSeeds)*bytesPerUint64+len(entries))
|
||||
bytes[0] = byte(len(hashSeeds))
|
||||
offset := 1
|
||||
for _, seed := range hashSeeds {
|
||||
binary.BigEndian.PutUint64(bytes[offset:], seed)
|
||||
offset += bytesPerUint64
|
||||
}
|
||||
copy(bytes[offset:], entries)
|
||||
return bytes
|
||||
}
|
||||
|
||||
// containsCommon returns true if [hash] is in the filter defined by [hashSeeds] and [entries].
|
||||
func containsCommon(hashSeeds []uint64, entries []byte, hash uint64) bool {
|
||||
for _, seed := range hashSeeds {
|
||||
if !containsWithSeed(entries, hash, seed) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func containsWithSeed(entries []byte, hash, seed uint64) bool {
|
||||
index := getIndex(entries, hash, seed)
|
||||
byteIndex := index / bitsPerByte
|
||||
bitIndex := index % bitsPerByte
|
||||
return entries[byteIndex]&(1<<bitIndex) != 0
|
||||
}
|
||||
|
||||
func getIndex(entries []byte, hash, seed uint64) uint64 {
|
||||
// If the filter has L entries, we only want to use hashes in the range
|
||||
// [0, L). We achieve this by incrementally shifting the hash to use
|
||||
// bits that we haven't used before. If we run out of bits, we
|
||||
// perform a "hash extension" by rehashing the original hash and the
|
||||
// seed.
|
||||
//
|
||||
// If the size of the bloom filter approaches MaxUint64, the hash may
|
||||
// overflow back to the beginning of the range. This means that the
|
||||
// same index can be used more than once for a single value.
|
||||
// This is OK, it just means the filter isn't as good as it could be
|
||||
// if we had more bits in the hash.
|
||||
numEntries := uint64(len(entries))
|
||||
entriesMask := numEntries*bitsPerByte - 1
|
||||
hash += seed
|
||||
// Note: It may be significantly more performant to use:
|
||||
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/
|
||||
// over the modulo operation.
|
||||
index := hash & entriesMask
|
||||
for index >= numEntries*bitsPerByte {
|
||||
hash = hash >> hashRotation
|
||||
// If the hash is zero, we have run out of bits and need to
|
||||
// rehash.
|
||||
if hash == 0 {
|
||||
hash = uint64(murmur3.Sum64(binary.BigEndian.AppendUint64(binary.BigEndian.AppendUint64(nil, hash), seed)))
|
||||
}
|
||||
index = hash & entriesMask
|
||||
}
|
||||
return index
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/node/utils/units"
|
||||
)
|
||||
|
||||
func TestNewErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
numHashes int
|
||||
numEntries int
|
||||
err error
|
||||
}{
|
||||
{
|
||||
numHashes: 0,
|
||||
numEntries: 1,
|
||||
err: errTooFewHashes,
|
||||
},
|
||||
{
|
||||
numHashes: 17,
|
||||
numEntries: 1,
|
||||
err: errTooManyHashes,
|
||||
},
|
||||
{
|
||||
numHashes: 8,
|
||||
numEntries: 0,
|
||||
err: errTooFewEntries,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.err.Error(), func(t *testing.T) {
|
||||
_, err := New(test.numHashes, test.numEntries)
|
||||
require.ErrorIs(t, err, test.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalUsage(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
toAdd := make([]uint64, 1024)
|
||||
for i := range toAdd {
|
||||
toAdd[i] = rand.Uint64() //#nosec G404
|
||||
}
|
||||
|
||||
initialNumHashes, initialNumBytes := OptimalParameters(1024, 0.01)
|
||||
filter, err := New(initialNumHashes, initialNumBytes)
|
||||
require.NoError(err)
|
||||
|
||||
for i, elem := range toAdd {
|
||||
filter.Add(elem)
|
||||
for _, elem := range toAdd[:i] {
|
||||
require.True(filter.Contains(elem))
|
||||
}
|
||||
}
|
||||
|
||||
require.Equal(len(toAdd), filter.Count())
|
||||
|
||||
filterBytes := filter.Marshal()
|
||||
parsedFilter, err := Parse(filterBytes)
|
||||
require.NoError(err)
|
||||
|
||||
for _, elem := range toAdd {
|
||||
require.True(parsedFilter.Contains(elem))
|
||||
}
|
||||
|
||||
parsedFilterBytes := parsedFilter.Marshal()
|
||||
require.Equal(filterBytes, parsedFilterBytes)
|
||||
}
|
||||
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
f, err := New(8, 16*units.KiB)
|
||||
require.NoError(b, err)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f.Add(1)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMarshal(b *testing.B) {
|
||||
f, err := New(OptimalParameters(10_000, .01))
|
||||
require.NoError(b, err)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f.Marshal()
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
)
|
||||
|
||||
func Add(f *Filter, key, salt []byte) {
|
||||
f.Add(Hash(key, salt))
|
||||
}
|
||||
|
||||
func Contains(c Checker, key, salt []byte) bool {
|
||||
return c.Contains(Hash(key, salt))
|
||||
}
|
||||
|
||||
type Checker interface {
|
||||
Contains(hash uint64) bool
|
||||
}
|
||||
|
||||
func Hash(key, salt []byte) uint64 {
|
||||
hash := sha256.New()
|
||||
// sha256.Write never returns errors
|
||||
_, _ = hash.Write(key)
|
||||
_, _ = hash.Write(salt)
|
||||
|
||||
output := make([]byte, 0, sha256.Size)
|
||||
return binary.BigEndian.Uint64(hash.Sum(output))
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/utils/units"
|
||||
)
|
||||
|
||||
func TestCollisionResistance(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
f, err := New(8, 16*units.KiB)
|
||||
require.NoError(err)
|
||||
|
||||
Add(f, []byte("hello world?"), []byte("so salty"))
|
||||
collision := Contains(f, []byte("hello world!"), []byte("so salty"))
|
||||
require.False(collision)
|
||||
}
|
||||
|
||||
func BenchmarkHash(b *testing.B) {
|
||||
key := ids.GenerateTestID()
|
||||
salt := ids.GenerateTestID()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
Hash(key[:], salt[:])
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/math/set"
|
||||
)
|
||||
|
||||
type mapFilter struct {
|
||||
lock sync.RWMutex
|
||||
values set.Set[string]
|
||||
}
|
||||
|
||||
func NewMap() BloomFilter {
|
||||
return &mapFilter{}
|
||||
}
|
||||
|
||||
func (m *mapFilter) Add(bl ...[]byte) {
|
||||
m.lock.Lock()
|
||||
defer m.lock.Unlock()
|
||||
|
||||
for _, b := range bl {
|
||||
m.values.Add(string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func (m *mapFilter) Check(b []byte) bool {
|
||||
m.lock.RLock()
|
||||
defer m.lock.RUnlock()
|
||||
|
||||
return m.values.Contains(string(b))
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"github.com/luxfi/metric"
|
||||
)
|
||||
|
||||
// Metrics is a collection of commonly useful metrics when using a long-lived
|
||||
// bloom filter.
|
||||
type Metrics struct {
|
||||
Count metric.Gauge
|
||||
NumHashes metric.Gauge
|
||||
NumEntries metric.Gauge
|
||||
MaxCount metric.Gauge
|
||||
ResetCount metric.Counter
|
||||
}
|
||||
|
||||
func NewMetrics(
|
||||
namespace string,
|
||||
registry metric.Registry,
|
||||
) (*Metrics, error) {
|
||||
metricsInstance := metric.NewWithRegistry(namespace, registry)
|
||||
|
||||
m := &Metrics{
|
||||
Count: metricsInstance.NewGauge(
|
||||
"count",
|
||||
"Number of additions that have been performed to the bloom",
|
||||
),
|
||||
NumHashes: metricsInstance.NewGauge(
|
||||
"hashes",
|
||||
"Number of hashes in the bloom",
|
||||
),
|
||||
NumEntries: metricsInstance.NewGauge(
|
||||
"entries",
|
||||
"Number of bytes allocated to slots in the bloom",
|
||||
),
|
||||
MaxCount: metricsInstance.NewGauge(
|
||||
"max_count",
|
||||
"Maximum number of additions that should be performed to the bloom before resetting",
|
||||
),
|
||||
ResetCount: metricsInstance.NewCounter(
|
||||
"reset_count",
|
||||
"Number times the bloom has been reset",
|
||||
),
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// Reset the metrics to align with the provided bloom filter and max count.
|
||||
func (m *Metrics) Reset(newFilter *Filter, maxCount int) {
|
||||
m.Count.Set(float64(newFilter.Count()))
|
||||
m.NumHashes.Set(float64(len(newFilter.hashSeeds)))
|
||||
m.NumEntries.Set(float64(len(newFilter.entries)))
|
||||
m.MaxCount.Set(float64(maxCount))
|
||||
m.ResetCount.Inc()
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import "math"
|
||||
|
||||
const ln2Squared = math.Ln2 * math.Ln2
|
||||
|
||||
// OptimalParameters calculates the optimal [numHashes] and [numEntries] that
|
||||
// should be allocated for a bloom filter which will contain [count] and target
|
||||
// [falsePositiveProbability].
|
||||
func OptimalParameters(count int, falsePositiveProbability float64) (int, int) {
|
||||
numEntries := OptimalEntries(count, falsePositiveProbability)
|
||||
numHashes := OptimalHashes(numEntries, count)
|
||||
return numHashes, numEntries
|
||||
}
|
||||
|
||||
// OptimalHashes calculates the number of hashes which will minimize the false
|
||||
// positive probability of a bloom filter with [numEntries] after [count]
|
||||
// additions.
|
||||
//
|
||||
// It is guaranteed to return a value in the range [minHashes, maxHashes].
|
||||
//
|
||||
// ref: https://en.wikipedia.org/wiki/Bloom_filter
|
||||
func OptimalHashes(numEntries, count int) int {
|
||||
switch {
|
||||
case numEntries < minEntries:
|
||||
return minHashes
|
||||
case count <= 0:
|
||||
return maxHashes
|
||||
}
|
||||
|
||||
numHashes := math.Ceil(float64(numEntries) * bitsPerByte * math.Ln2 / float64(count))
|
||||
// Converting a floating-point value to an int produces an undefined value
|
||||
// if the floating-point value cannot be represented as an int. To avoid
|
||||
// this undefined behavior, we explicitly check against MaxInt here.
|
||||
//
|
||||
// ref: https://go.dev/ref/spec#Conversions
|
||||
if numHashes >= maxHashes {
|
||||
return maxHashes
|
||||
}
|
||||
return max(int(numHashes), minHashes)
|
||||
}
|
||||
|
||||
// OptimalEntries calculates the optimal number of entries to use when creating
|
||||
// a new Bloom filter when targenting a size of [count] with
|
||||
// [falsePositiveProbability] assuming that the optimal number of hashes is
|
||||
// used.
|
||||
//
|
||||
// It is guaranteed to return a value in the range [minEntries, MaxInt].
|
||||
//
|
||||
// ref: https://en.wikipedia.org/wiki/Bloom_filter
|
||||
func OptimalEntries(count int, falsePositiveProbability float64) int {
|
||||
switch {
|
||||
case count <= 0:
|
||||
return minEntries
|
||||
case falsePositiveProbability >= 1:
|
||||
return minEntries
|
||||
case falsePositiveProbability <= 0:
|
||||
return math.MaxInt
|
||||
}
|
||||
|
||||
entriesInBits := -float64(count) * math.Log(falsePositiveProbability) / ln2Squared
|
||||
entries := (entriesInBits + bitsPerByte - 1) / bitsPerByte
|
||||
// Converting a floating-point value to an int produces an undefined value
|
||||
// if the floating-point value cannot be represented as an int. To avoid
|
||||
// this undefined behavior, we explicitly check against MaxInt here.
|
||||
//
|
||||
// ref: https://go.dev/ref/spec#Conversions
|
||||
if entries >= math.MaxInt {
|
||||
return math.MaxInt
|
||||
}
|
||||
return max(int(entries), minEntries)
|
||||
}
|
||||
|
||||
// EstimateCount estimates the number of additions a bloom filter with
|
||||
// [numHashes] and [numEntries] must have to reach [falsePositiveProbability].
|
||||
// This is derived by inversing a lower-bound on the probability of false
|
||||
// positives. For values where numBits >> numHashes, the predicted probability
|
||||
// is fairly accurate.
|
||||
//
|
||||
// It is guaranteed to return a value in the range [0, MaxInt].
|
||||
//
|
||||
// ref: https://tsapps.nist.gov/publication/get_pdf.cfm?pub_id=903775
|
||||
func EstimateCount(numHashes, numEntries int, falsePositiveProbability float64) int {
|
||||
switch {
|
||||
case numHashes < minHashes:
|
||||
return 0
|
||||
case numEntries < minEntries:
|
||||
return 0
|
||||
case falsePositiveProbability <= 0:
|
||||
return 0
|
||||
case falsePositiveProbability >= 1:
|
||||
return math.MaxInt
|
||||
}
|
||||
|
||||
invNumHashes := 1 / float64(numHashes)
|
||||
numBits := float64(numEntries * 8)
|
||||
exp := 1 - math.Pow(falsePositiveProbability, invNumHashes)
|
||||
count := math.Ceil(-math.Log(exp) * numBits * invNumHashes)
|
||||
// Converting a floating-point value to an int produces an undefined value
|
||||
// if the floating-point value cannot be represented as an int. To avoid
|
||||
// this undefined behavior, we explicitly check against MaxInt here.
|
||||
//
|
||||
// ref: https://go.dev/ref/spec#Conversions
|
||||
if count >= math.MaxInt {
|
||||
return math.MaxInt
|
||||
}
|
||||
return int(count)
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const largestFloat64LessThan1 float64 = 1 - 1e-16
|
||||
|
||||
func TestOptimalHashes(t *testing.T) {
|
||||
tests := []struct {
|
||||
numEntries int
|
||||
count int
|
||||
expectedHashes int
|
||||
}{
|
||||
{ // invalid params
|
||||
numEntries: 0,
|
||||
count: 1024,
|
||||
expectedHashes: minHashes,
|
||||
},
|
||||
{ // invalid params
|
||||
numEntries: 1024,
|
||||
count: 0,
|
||||
expectedHashes: maxHashes,
|
||||
},
|
||||
{
|
||||
numEntries: math.MaxInt,
|
||||
count: 1,
|
||||
expectedHashes: maxHashes,
|
||||
},
|
||||
{
|
||||
numEntries: 1,
|
||||
count: math.MaxInt,
|
||||
expectedHashes: minHashes,
|
||||
},
|
||||
{
|
||||
numEntries: 1024,
|
||||
count: 1024,
|
||||
expectedHashes: 6,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%d_%d", test.numEntries, test.count), func(t *testing.T) {
|
||||
hashes := OptimalHashes(test.numEntries, test.count)
|
||||
require.Equal(t, test.expectedHashes, hashes)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestOptimalEntries(t *testing.T) {
|
||||
tests := []struct {
|
||||
count int
|
||||
falsePositiveProbability float64
|
||||
expectedEntries int
|
||||
}{
|
||||
{ // invalid params
|
||||
count: 0,
|
||||
falsePositiveProbability: .5,
|
||||
expectedEntries: minEntries,
|
||||
},
|
||||
{ // invalid params
|
||||
count: 1,
|
||||
falsePositiveProbability: 0,
|
||||
expectedEntries: math.MaxInt,
|
||||
},
|
||||
{ // invalid params
|
||||
count: 1,
|
||||
falsePositiveProbability: 1,
|
||||
expectedEntries: minEntries,
|
||||
},
|
||||
{
|
||||
count: math.MaxInt,
|
||||
falsePositiveProbability: math.SmallestNonzeroFloat64,
|
||||
expectedEntries: math.MaxInt,
|
||||
},
|
||||
{
|
||||
count: 1024,
|
||||
falsePositiveProbability: largestFloat64LessThan1,
|
||||
expectedEntries: minEntries,
|
||||
},
|
||||
{
|
||||
count: 1024,
|
||||
falsePositiveProbability: .01,
|
||||
expectedEntries: 1227,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%d_%f", test.count, test.falsePositiveProbability), func(t *testing.T) {
|
||||
entries := OptimalEntries(test.count, test.falsePositiveProbability)
|
||||
require.Equal(t, test.expectedEntries, entries)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEstimateEntries(t *testing.T) {
|
||||
tests := []struct {
|
||||
numHashes int
|
||||
numEntries int
|
||||
falsePositiveProbability float64
|
||||
expectedEntries int
|
||||
}{
|
||||
{ // invalid params
|
||||
numHashes: 0,
|
||||
numEntries: 2_048,
|
||||
falsePositiveProbability: .5,
|
||||
expectedEntries: 0,
|
||||
},
|
||||
{ // invalid params
|
||||
numHashes: 1,
|
||||
numEntries: 0,
|
||||
falsePositiveProbability: .5,
|
||||
expectedEntries: 0,
|
||||
},
|
||||
{ // invalid params
|
||||
numHashes: 1,
|
||||
numEntries: 1,
|
||||
falsePositiveProbability: 2,
|
||||
expectedEntries: math.MaxInt,
|
||||
},
|
||||
{ // invalid params
|
||||
numHashes: 1,
|
||||
numEntries: 1,
|
||||
falsePositiveProbability: -1,
|
||||
expectedEntries: 0,
|
||||
},
|
||||
{
|
||||
numHashes: 8,
|
||||
numEntries: 2_048,
|
||||
falsePositiveProbability: 0,
|
||||
expectedEntries: 0,
|
||||
},
|
||||
{ // params from OptimalParameters(10_000, .01)
|
||||
numHashes: 7,
|
||||
numEntries: 11_982,
|
||||
falsePositiveProbability: .01,
|
||||
expectedEntries: 9_993,
|
||||
},
|
||||
{ // params from OptimalParameters(100_000, .001)
|
||||
numHashes: 10,
|
||||
numEntries: 179_720,
|
||||
falsePositiveProbability: .001,
|
||||
expectedEntries: 100_000,
|
||||
},
|
||||
{ // params from OptimalParameters(10_000, .01)
|
||||
numHashes: 7,
|
||||
numEntries: 11_982,
|
||||
falsePositiveProbability: .05,
|
||||
expectedEntries: 14_449,
|
||||
},
|
||||
{ // params from OptimalParameters(10_000, .01)
|
||||
numHashes: 7,
|
||||
numEntries: 11_982,
|
||||
falsePositiveProbability: 1,
|
||||
expectedEntries: math.MaxInt,
|
||||
},
|
||||
{ // params from OptimalParameters(10_000, .01)
|
||||
numHashes: 7,
|
||||
numEntries: 11_982,
|
||||
falsePositiveProbability: math.SmallestNonzeroFloat64,
|
||||
expectedEntries: 0,
|
||||
},
|
||||
{ // params from OptimalParameters(10_000, .01)
|
||||
numHashes: 7,
|
||||
numEntries: 11_982,
|
||||
falsePositiveProbability: largestFloat64LessThan1,
|
||||
expectedEntries: math.MaxInt,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%d_%d_%f", test.numHashes, test.numEntries, test.falsePositiveProbability), func(t *testing.T) {
|
||||
entries := EstimateCount(test.numHashes, test.numEntries, test.falsePositiveProbability)
|
||||
require.Equal(t, test.expectedEntries, entries)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzOptimalHashes(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, numEntries, count int) {
|
||||
hashes := OptimalHashes(numEntries, count)
|
||||
require.GreaterOrEqual(t, hashes, minHashes)
|
||||
require.LessOrEqual(t, hashes, maxHashes)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzOptimalEntries(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, count int, falsePositiveProbability float64) {
|
||||
entries := OptimalEntries(count, falsePositiveProbability)
|
||||
require.GreaterOrEqual(t, entries, minEntries)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzEstimateEntries(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, numHashes, numEntries int, falsePositiveProbability float64) {
|
||||
entries := EstimateCount(numHashes, numEntries, falsePositiveProbability)
|
||||
require.GreaterOrEqual(t, entries, 0)
|
||||
})
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
EmptyFilter = &ReadFilter{
|
||||
hashSeeds: make([]uint64, minHashes),
|
||||
entries: make([]byte, minEntries),
|
||||
}
|
||||
FullFilter = &ReadFilter{
|
||||
hashSeeds: make([]uint64, minHashes),
|
||||
entries: make([]byte, minEntries),
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
for i := range FullFilter.entries {
|
||||
FullFilter.entries[i] = 0xFF
|
||||
}
|
||||
}
|
||||
|
||||
type ReadFilter struct {
|
||||
hashSeeds []uint64
|
||||
entries []byte
|
||||
}
|
||||
|
||||
// Parse [bytes] into a read-only bloom filter.
|
||||
func Parse(bytes []byte) (*ReadFilter, error) {
|
||||
if len(bytes) == 0 {
|
||||
return nil, errInvalidNumHashes
|
||||
}
|
||||
numHashes := bytes[0]
|
||||
entriesOffset := 1 + int(numHashes)*bytesPerUint64
|
||||
switch {
|
||||
case numHashes < minHashes:
|
||||
return nil, fmt.Errorf("%w: %d < %d", errTooFewHashes, numHashes, minHashes)
|
||||
case numHashes > maxHashes:
|
||||
return nil, fmt.Errorf("%w: %d > %d", errTooManyHashes, numHashes, maxHashes)
|
||||
case len(bytes) < entriesOffset+minEntries: // numEntries = len(bytes) - entriesOffset
|
||||
return nil, errTooFewEntries
|
||||
}
|
||||
|
||||
f := &ReadFilter{
|
||||
hashSeeds: make([]uint64, numHashes),
|
||||
entries: bytes[entriesOffset:],
|
||||
}
|
||||
for i := range f.hashSeeds {
|
||||
f.hashSeeds[i] = binary.BigEndian.Uint64(bytes[1+i*bytesPerUint64:])
|
||||
}
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func (f *ReadFilter) Contains(hash uint64) bool {
|
||||
return contains(f.hashSeeds, f.entries, hash)
|
||||
}
|
||||
|
||||
func (f *ReadFilter) Marshal() []byte {
|
||||
return marshal(f.hashSeeds, f.entries)
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bloom
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func NewMaliciousFilter(numHashes, numEntries int) *Filter {
|
||||
f := &Filter{
|
||||
numBits: uint64(numEntries * bitsPerByte),
|
||||
hashSeeds: make([]uint64, numHashes),
|
||||
entries: make([]byte, numEntries),
|
||||
count: 0,
|
||||
}
|
||||
for i := range f.entries {
|
||||
f.entries[i] = math.MaxUint8
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func TestParseErrors(t *testing.T) {
|
||||
tests := []struct {
|
||||
bytes []byte
|
||||
err error
|
||||
}{
|
||||
{
|
||||
bytes: nil,
|
||||
err: errInvalidNumHashes,
|
||||
},
|
||||
{
|
||||
bytes: NewMaliciousFilter(0, 1).Marshal(),
|
||||
err: errTooFewHashes,
|
||||
},
|
||||
{
|
||||
bytes: NewMaliciousFilter(17, 1).Marshal(),
|
||||
err: errTooManyHashes,
|
||||
},
|
||||
{
|
||||
bytes: NewMaliciousFilter(1, 0).Marshal(),
|
||||
err: errTooFewEntries,
|
||||
},
|
||||
{
|
||||
bytes: []byte{
|
||||
0x01, // num hashes = 1
|
||||
},
|
||||
err: errTooFewEntries,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.err.Error(), func(t *testing.T) {
|
||||
_, err := Parse(test.bytes)
|
||||
require.ErrorIs(t, err, test.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParse(b *testing.B) {
|
||||
f, err := New(OptimalParameters(10_000, .01))
|
||||
require.NoError(b, err)
|
||||
bytes := f.Marshal()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = Parse(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkContains(b *testing.B) {
|
||||
f := NewMaliciousFilter(maxHashes, 1)
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
f.Contains(1)
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzParseThenMarshal(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, bytes []byte) {
|
||||
f, err := Parse(bytes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
marshalledBytes := marshal(f.hashSeeds, f.entries)
|
||||
require.Equal(t, bytes, marshalledBytes)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzMarshalThenParse(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, numHashes int, entries []byte) {
|
||||
require := require.New(t)
|
||||
|
||||
hashSeeds, err := newHashSeeds(numHashes)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if len(entries) < minEntries {
|
||||
return
|
||||
}
|
||||
|
||||
marshalledBytes := marshal(hashSeeds, entries)
|
||||
rf, err := Parse(marshalledBytes)
|
||||
require.NoError(err)
|
||||
require.Equal(hashSeeds, rf.hashSeeds)
|
||||
require.Equal(entries, rf.entries)
|
||||
})
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package buffer
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
_ Queue[struct{}] = (*boundedQueue[struct{}])(nil)
|
||||
|
||||
errInvalidMaxSize = errors.New("maxSize must be greater than 0")
|
||||
)
|
||||
|
||||
// A FIFO queue.
|
||||
type Queue[T any] interface {
|
||||
// Pushes [elt] onto the queue.
|
||||
// If the queue is full, the oldest element is evicted to make space.
|
||||
Push(T)
|
||||
// Pops the oldest element from the queue.
|
||||
// Returns false if the queue is empty.
|
||||
Pop() (T, bool)
|
||||
// Returns the oldest element without removing it.
|
||||
// Returns false if the queue is empty.
|
||||
Peek() (T, bool)
|
||||
// Returns the element at the given index without removing it.
|
||||
// Index(0) returns the oldest element.
|
||||
// Index(Len() - 1) returns the newest element.
|
||||
// Returns false if there is no element at that index.
|
||||
Index(int) (T, bool)
|
||||
// Returns the number of elements in the queue.
|
||||
Len() int
|
||||
// Returns the queue elements from oldest to newest.
|
||||
// This is an O(n) operation and should be used sparingly.
|
||||
List() []T
|
||||
}
|
||||
|
||||
// Keeps up to [maxSize] entries in an ordered buffer
|
||||
// and calls [onEvict] on any item that is evicted.
|
||||
// Not safe for concurrent use.
|
||||
type boundedQueue[T any] struct {
|
||||
deque Deque[T]
|
||||
maxSize int
|
||||
onEvict func(T)
|
||||
}
|
||||
|
||||
// Returns a new bounded, non-blocking queue that holds up to [maxSize] elements.
|
||||
// When an element is evicted, [onEvict] is called with the evicted element.
|
||||
// If [onEvict] is nil, this is a no-op.
|
||||
// [maxSize] must be >= 1.
|
||||
// Not safe for concurrent use.
|
||||
func NewBoundedQueue[T any](maxSize int, onEvict func(T)) (Queue[T], error) {
|
||||
if maxSize < 1 {
|
||||
return nil, errInvalidMaxSize
|
||||
}
|
||||
return &boundedQueue[T]{
|
||||
deque: NewUnboundedDeque[T](maxSize + 1), // +1 so we never resize
|
||||
maxSize: maxSize,
|
||||
onEvict: onEvict,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (b *boundedQueue[T]) Push(elt T) {
|
||||
if b.deque.Len() == b.maxSize {
|
||||
evicted, _ := b.deque.PopLeft()
|
||||
if b.onEvict != nil {
|
||||
b.onEvict(evicted)
|
||||
}
|
||||
}
|
||||
_ = b.deque.PushRight(elt)
|
||||
}
|
||||
|
||||
func (b *boundedQueue[T]) Pop() (T, bool) {
|
||||
return b.deque.PopLeft()
|
||||
}
|
||||
|
||||
func (b *boundedQueue[T]) Peek() (T, bool) {
|
||||
return b.deque.PeekLeft()
|
||||
}
|
||||
|
||||
func (b *boundedQueue[T]) Index(i int) (T, bool) {
|
||||
return b.deque.Index(i)
|
||||
}
|
||||
|
||||
func (b *boundedQueue[T]) Len() int {
|
||||
return b.deque.Len()
|
||||
}
|
||||
|
||||
func (b *boundedQueue[T]) List() []T {
|
||||
return b.deque.List()
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNewBoundedQueue(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
// Case: maxSize < 1
|
||||
_, err := NewBoundedQueue[bool](0, nil)
|
||||
require.ErrorIs(err, errInvalidMaxSize)
|
||||
|
||||
// Case: maxSize == 1 and nil onEvict
|
||||
b, err := NewBoundedQueue[bool](1, nil)
|
||||
require.NoError(err)
|
||||
|
||||
// Put 2 elements to make sure we don't panic on evict
|
||||
b.Push(true)
|
||||
b.Push(true)
|
||||
}
|
||||
|
||||
func TestBoundedQueue(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
maxSize := 3
|
||||
evicted := []int{}
|
||||
onEvict := func(elt int) {
|
||||
evicted = append(evicted, elt)
|
||||
}
|
||||
b, err := NewBoundedQueue(maxSize, onEvict)
|
||||
require.NoError(err)
|
||||
|
||||
require.Zero(b.Len())
|
||||
|
||||
// Fill the queue
|
||||
for i := 0; i < maxSize; i++ {
|
||||
b.Push(i)
|
||||
require.Equal(i+1, b.Len())
|
||||
got, ok := b.Peek()
|
||||
require.True(ok)
|
||||
require.Zero(got)
|
||||
got, ok = b.Index(i)
|
||||
require.True(ok)
|
||||
require.Equal(i, got)
|
||||
require.Len(b.List(), i+1)
|
||||
}
|
||||
require.Equal([]int{}, evicted)
|
||||
require.Len(b.List(), maxSize)
|
||||
// Queue is [0, 1, 2]
|
||||
|
||||
// Empty the queue
|
||||
for i := 0; i < maxSize; i++ {
|
||||
got, ok := b.Pop()
|
||||
require.True(ok)
|
||||
require.Equal(i, got)
|
||||
require.Equal(maxSize-i-1, b.Len())
|
||||
require.Len(b.List(), maxSize-i-1)
|
||||
}
|
||||
|
||||
// Queue is empty
|
||||
|
||||
_, ok := b.Pop()
|
||||
require.False(ok)
|
||||
_, ok = b.Peek()
|
||||
require.False(ok)
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
require.Zero(b.Len())
|
||||
require.Empty(b.List())
|
||||
|
||||
// Fill the queue again
|
||||
for i := 0; i < maxSize; i++ {
|
||||
b.Push(i)
|
||||
require.Equal(i+1, b.Len())
|
||||
}
|
||||
|
||||
// Queue is [0, 1, 2]
|
||||
|
||||
// Putting another element should evict the oldest.
|
||||
b.Push(maxSize)
|
||||
|
||||
// Queue is [1, 2, 3]
|
||||
|
||||
require.Equal(maxSize, b.Len())
|
||||
require.Len(b.List(), maxSize)
|
||||
got, ok := b.Peek()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
got, ok = b.Index(maxSize - 1)
|
||||
require.True(ok)
|
||||
require.Equal(maxSize, got)
|
||||
require.Equal([]int{0}, evicted)
|
||||
|
||||
// Put 2 more elements
|
||||
b.Push(maxSize + 1)
|
||||
b.Push(maxSize + 2)
|
||||
|
||||
// Queue is [3, 4, 5]
|
||||
|
||||
require.Equal(maxSize, b.Len())
|
||||
require.Equal([]int{0, 1, 2}, evicted)
|
||||
got, ok = b.Peek()
|
||||
require.True(ok)
|
||||
require.Equal(3, got)
|
||||
require.Equal([]int{3, 4, 5}, b.List())
|
||||
|
||||
for i := maxSize; i < 2*maxSize; i++ {
|
||||
got, ok := b.Index(i - maxSize)
|
||||
require.True(ok)
|
||||
require.Equal(i, got)
|
||||
}
|
||||
|
||||
// Empty the queue
|
||||
for i := 0; i < maxSize; i++ {
|
||||
got, ok := b.Pop()
|
||||
require.True(ok)
|
||||
require.Equal(i+3, got)
|
||||
require.Equal(maxSize-i-1, b.Len())
|
||||
require.Len(b.List(), maxSize-i-1)
|
||||
}
|
||||
|
||||
// Queue is empty
|
||||
|
||||
require.Empty(b.List())
|
||||
require.Zero(b.Len())
|
||||
require.Equal([]int{0, 1, 2}, evicted)
|
||||
_, ok = b.Pop()
|
||||
require.False(ok)
|
||||
_, ok = b.Peek()
|
||||
require.False(ok)
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
)
|
||||
|
||||
var _ BlockingDeque[int] = (*unboundedBlockingDeque[int])(nil)
|
||||
|
||||
type BlockingDeque[T any] interface {
|
||||
Deque[T]
|
||||
|
||||
// Close and empty the deque.
|
||||
Close()
|
||||
}
|
||||
|
||||
// Returns a new unbounded deque with the given initial size.
|
||||
// Note that the returned deque is always empty -- [initSize] is just
|
||||
// a hint to prevent unnecessary resizing.
|
||||
func NewUnboundedBlockingDeque[T any](initSize int) BlockingDeque[T] {
|
||||
q := &unboundedBlockingDeque[T]{
|
||||
Deque: NewUnboundedDeque[T](initSize),
|
||||
}
|
||||
q.cond = sync.NewCond(&q.lock)
|
||||
return q
|
||||
}
|
||||
|
||||
type unboundedBlockingDeque[T any] struct {
|
||||
lock sync.RWMutex
|
||||
cond *sync.Cond
|
||||
closed bool
|
||||
|
||||
Deque[T]
|
||||
}
|
||||
|
||||
// If the deque is closed returns false.
|
||||
func (q *unboundedBlockingDeque[T]) PushRight(elt T) bool {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
|
||||
if q.closed {
|
||||
return false
|
||||
}
|
||||
|
||||
// Add the item to the queue
|
||||
q.Deque.PushRight(elt)
|
||||
|
||||
// Signal a waiting thread
|
||||
q.cond.Signal()
|
||||
return true
|
||||
}
|
||||
|
||||
// If the deque is closed returns false.
|
||||
func (q *unboundedBlockingDeque[T]) PopRight() (T, bool) {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
|
||||
for {
|
||||
if q.closed {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
if q.Deque.Len() != 0 {
|
||||
return q.Deque.PopRight()
|
||||
}
|
||||
q.cond.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (q *unboundedBlockingDeque[T]) PeekRight() (T, bool) {
|
||||
q.lock.RLock()
|
||||
defer q.lock.RUnlock()
|
||||
|
||||
if q.closed {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
return q.Deque.PeekRight()
|
||||
}
|
||||
|
||||
// If the deque is closed returns false.
|
||||
func (q *unboundedBlockingDeque[T]) PushLeft(elt T) bool {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
|
||||
if q.closed {
|
||||
return false
|
||||
}
|
||||
|
||||
// Add the item to the queue
|
||||
q.Deque.PushLeft(elt)
|
||||
|
||||
// Signal a waiting thread
|
||||
q.cond.Signal()
|
||||
return true
|
||||
}
|
||||
|
||||
// If the deque is closed returns false.
|
||||
func (q *unboundedBlockingDeque[T]) PopLeft() (T, bool) {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
|
||||
for {
|
||||
if q.closed {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
if q.Deque.Len() != 0 {
|
||||
return q.Deque.PopLeft()
|
||||
}
|
||||
q.cond.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (q *unboundedBlockingDeque[T]) PeekLeft() (T, bool) {
|
||||
q.lock.RLock()
|
||||
defer q.lock.RUnlock()
|
||||
|
||||
if q.closed {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
return q.Deque.PeekLeft()
|
||||
}
|
||||
|
||||
func (q *unboundedBlockingDeque[T]) Index(i int) (T, bool) {
|
||||
q.lock.RLock()
|
||||
defer q.lock.RUnlock()
|
||||
|
||||
if q.closed {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
return q.Deque.Index(i)
|
||||
}
|
||||
|
||||
func (q *unboundedBlockingDeque[T]) Len() int {
|
||||
q.lock.RLock()
|
||||
defer q.lock.RUnlock()
|
||||
|
||||
if q.closed {
|
||||
return 0
|
||||
}
|
||||
return q.Deque.Len()
|
||||
}
|
||||
|
||||
func (q *unboundedBlockingDeque[T]) List() []T {
|
||||
q.lock.RLock()
|
||||
defer q.lock.RUnlock()
|
||||
|
||||
if q.closed {
|
||||
return nil
|
||||
}
|
||||
return q.Deque.List()
|
||||
}
|
||||
|
||||
func (q *unboundedBlockingDeque[T]) Close() {
|
||||
q.cond.L.Lock()
|
||||
defer q.cond.L.Unlock()
|
||||
|
||||
if q.closed {
|
||||
return
|
||||
}
|
||||
|
||||
q.Deque = nil
|
||||
|
||||
// Mark the queue as closed
|
||||
q.closed = true
|
||||
q.cond.Broadcast()
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUnboundedBlockingDequePush(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
deque := NewUnboundedBlockingDeque[int](2)
|
||||
require.Empty(deque.List())
|
||||
_, ok := deque.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
ok = deque.PushRight(1)
|
||||
require.True(ok)
|
||||
require.Equal([]int{1}, deque.List())
|
||||
got, ok := deque.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
ok = deque.PushRight(2)
|
||||
require.True(ok)
|
||||
require.Equal([]int{1, 2}, deque.List())
|
||||
got, ok = deque.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
got, ok = deque.Index(1)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
_, ok = deque.Index(2)
|
||||
require.False(ok)
|
||||
|
||||
ch, ok := deque.PopLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, ch)
|
||||
require.Equal([]int{2}, deque.List())
|
||||
got, ok = deque.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
}
|
||||
|
||||
func TestUnboundedBlockingDequePop(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
deque := NewUnboundedBlockingDeque[int](2)
|
||||
require.Empty(deque.List())
|
||||
|
||||
ok := deque.PushRight(1)
|
||||
require.True(ok)
|
||||
require.Equal([]int{1}, deque.List())
|
||||
got, ok := deque.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
ch, ok := deque.PopLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, ch)
|
||||
require.Empty(deque.List())
|
||||
|
||||
var (
|
||||
gotOk bool
|
||||
gotCh int
|
||||
)
|
||||
|
||||
wg := &sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
gotCh, gotOk = deque.PopLeft()
|
||||
wg.Done()
|
||||
}()
|
||||
|
||||
ok = deque.PushRight(2)
|
||||
require.True(ok)
|
||||
wg.Wait()
|
||||
|
||||
require.True(gotOk)
|
||||
require.Equal(2, gotCh)
|
||||
|
||||
require.Empty(deque.List())
|
||||
_, ok = deque.Index(0)
|
||||
require.False(ok)
|
||||
}
|
||||
|
||||
func TestUnboundedBlockingDequeClose(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
deque := NewUnboundedBlockingDeque[int](2)
|
||||
|
||||
ok := deque.PushLeft(1)
|
||||
require.True(ok)
|
||||
|
||||
deque.Close()
|
||||
|
||||
_, ok = deque.PopRight()
|
||||
require.False(ok)
|
||||
|
||||
ok = deque.PushLeft(1)
|
||||
require.False(ok)
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package buffer
|
||||
|
||||
import "github.com/luxfi/node/utils"
|
||||
|
||||
const defaultInitSize = 32
|
||||
|
||||
// An unbounded deque (double-ended queue).
|
||||
// See https://en.wikipedia.org/wiki/Double-ended_queue
|
||||
// Not safe for concurrent access.
|
||||
type Deque[T any] interface {
|
||||
// Place an element at the leftmost end of the deque.
|
||||
// Returns true if the element was placed in the deque.
|
||||
PushLeft(T) bool
|
||||
// Place an element at the rightmost end of the deque.
|
||||
// Returns true if the element was placed in the deque.
|
||||
PushRight(T) bool
|
||||
// Remove and return the leftmost element of the deque.
|
||||
// Returns false if the deque is empty.
|
||||
PopLeft() (T, bool)
|
||||
// Remove and return the rightmost element of the deque.
|
||||
// Returns false if the deque is empty.
|
||||
PopRight() (T, bool)
|
||||
// Return the leftmost element of the deque without removing it.
|
||||
// Returns false if the deque is empty.
|
||||
PeekLeft() (T, bool)
|
||||
// Return the rightmost element of the deque without removing it.
|
||||
// Returns false if the deque is empty.
|
||||
PeekRight() (T, bool)
|
||||
// Returns the element at the given index.
|
||||
// Returns false if the index is out of bounds.
|
||||
// The leftmost element is at index 0.
|
||||
Index(int) (T, bool)
|
||||
// Returns the number of elements in the deque.
|
||||
Len() int
|
||||
// Returns the elements in the deque from left to right.
|
||||
List() []T
|
||||
}
|
||||
|
||||
// Returns a new unbounded deque with the given initial slice size.
|
||||
// Note that the returned deque is always empty -- [initSize] is just
|
||||
// a hint to prevent unnecessary resizing.
|
||||
func NewUnboundedDeque[T any](initSize int) Deque[T] {
|
||||
if initSize < 2 {
|
||||
initSize = defaultInitSize
|
||||
}
|
||||
return &unboundedSliceDeque[T]{
|
||||
// Note that [initSize] must be >= 2 to satisfy invariants (1) and (2).
|
||||
data: make([]T, initSize),
|
||||
right: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// Invariants after each function call and before the first call:
|
||||
// (1) The next element pushed left will be placed at data[left]
|
||||
// (2) The next element pushed right will be placed at data[right]
|
||||
// (3) There are [size] elements in the deque.
|
||||
type unboundedSliceDeque[T any] struct {
|
||||
size, left, right int
|
||||
data []T
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) PushRight(elt T) bool {
|
||||
// Invariant (2) says it's safe to place the element without resizing.
|
||||
b.data[b.right] = elt
|
||||
b.size++
|
||||
b.right++
|
||||
b.right %= len(b.data)
|
||||
|
||||
b.resize()
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) PushLeft(elt T) bool {
|
||||
// Invariant (1) says it's safe to place the element without resizing.
|
||||
b.data[b.left] = elt
|
||||
b.size++
|
||||
b.left--
|
||||
if b.left < 0 {
|
||||
b.left = len(b.data) - 1 // Wrap around
|
||||
}
|
||||
|
||||
b.resize()
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) PopLeft() (T, bool) {
|
||||
if b.size == 0 {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
idx := b.leftmostEltIdx()
|
||||
elt := b.data[idx]
|
||||
// Zero out to prevent memory leak.
|
||||
b.data[idx] = utils.Zero[T]()
|
||||
b.size--
|
||||
b.left++
|
||||
b.left %= len(b.data)
|
||||
return elt, true
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) PeekLeft() (T, bool) {
|
||||
if b.size == 0 {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
idx := b.leftmostEltIdx()
|
||||
return b.data[idx], true
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) PopRight() (T, bool) {
|
||||
if b.size == 0 {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
idx := b.rightmostEltIdx()
|
||||
elt := b.data[idx]
|
||||
// Zero out to prevent memory leak.
|
||||
b.data[idx] = utils.Zero[T]()
|
||||
b.size--
|
||||
b.right--
|
||||
if b.right < 0 {
|
||||
b.right = len(b.data) - 1 // Wrap around
|
||||
}
|
||||
return elt, true
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) PeekRight() (T, bool) {
|
||||
if b.size == 0 {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
idx := b.rightmostEltIdx()
|
||||
return b.data[idx], true
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) Index(idx int) (T, bool) {
|
||||
if idx < 0 || idx >= b.size {
|
||||
return utils.Zero[T](), false
|
||||
}
|
||||
leftmostIdx := b.leftmostEltIdx()
|
||||
idx = (leftmostIdx + idx) % len(b.data)
|
||||
return b.data[idx], true
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) Len() int {
|
||||
return b.size
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) List() []T {
|
||||
if b.size == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
list := make([]T, b.size)
|
||||
leftmostIdx := b.leftmostEltIdx()
|
||||
if numCopied := copy(list, b.data[leftmostIdx:]); numCopied < b.size {
|
||||
// We copied all of the elements from the leftmost element index
|
||||
// to the end of the underlying slice, but we still haven't copied
|
||||
// all of the elements, so wrap around and copy the rest.
|
||||
copy(list[numCopied:], b.data[:b.right])
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) leftmostEltIdx() int {
|
||||
if b.left == len(b.data)-1 { // Wrap around case
|
||||
return 0
|
||||
}
|
||||
return b.left + 1 // Normal case
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) rightmostEltIdx() int {
|
||||
if b.right == 0 {
|
||||
return len(b.data) - 1 // Wrap around case
|
||||
}
|
||||
return b.right - 1 // Normal case
|
||||
}
|
||||
|
||||
func (b *unboundedSliceDeque[T]) resize() {
|
||||
if b.size != len(b.data) {
|
||||
return
|
||||
}
|
||||
newData := make([]T, b.size*2)
|
||||
leftmostIdx := b.leftmostEltIdx()
|
||||
copy(newData, b.data[leftmostIdx:])
|
||||
numCopied := len(b.data) - leftmostIdx
|
||||
copy(newData[numCopied:], b.data[:b.right])
|
||||
b.data = newData
|
||||
b.left = len(b.data) - 1
|
||||
b.right = b.size
|
||||
}
|
||||
@@ -1,672 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package buffer
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestUnboundedDeque_InitialCapGreaterThanMin(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
bIntf := NewUnboundedDeque[int](10)
|
||||
require.IsType(&unboundedSliceDeque[int]{}, bIntf)
|
||||
b := bIntf.(*unboundedSliceDeque[int])
|
||||
require.Empty(b.List())
|
||||
require.Zero(b.Len())
|
||||
_, ok := b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushLeft(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok := b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
_, ok = b.Index(1)
|
||||
require.False(ok)
|
||||
|
||||
got, ok = b.PopLeft()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushLeft(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PopRight()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushRight(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PopRight()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushRight(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PopLeft()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushLeft(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
b.PushLeft(2)
|
||||
require.Equal(2, b.Len())
|
||||
require.Equal([]int{2, 1}, b.List())
|
||||
|
||||
got, ok = b.PopLeft()
|
||||
require.Equal(1, b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PopLeft()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushRight(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
b.PushRight(2)
|
||||
require.Equal(2, b.Len())
|
||||
require.Equal([]int{1, 2}, b.List())
|
||||
|
||||
got, ok = b.PopRight()
|
||||
require.Equal(1, b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PopRight()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushLeft(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
b.PushLeft(2)
|
||||
require.Equal(2, b.Len())
|
||||
require.Equal([]int{2, 1}, b.List())
|
||||
|
||||
got, ok = b.PopRight()
|
||||
require.Equal(1, b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Equal([]int{2}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PopLeft()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushRight(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
b.PushLeft(2)
|
||||
require.Equal(2, b.Len())
|
||||
require.Equal([]int{2, 1}, b.List())
|
||||
|
||||
got, ok = b.PopRight()
|
||||
require.Equal(1, b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Equal([]int{2}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PopLeft()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
b.PushLeft(1)
|
||||
require.Equal(1, b.Len())
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
b.PushRight(2)
|
||||
require.Equal(2, b.Len())
|
||||
require.Equal([]int{1, 2}, b.List())
|
||||
|
||||
got, ok = b.PopLeft()
|
||||
require.Equal(1, b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Equal([]int{2}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PopRight()
|
||||
require.Zero(b.Len())
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
}
|
||||
|
||||
// Cases we test:
|
||||
// 1. [left] moves to the left (no wrap around).
|
||||
// 2. [left] moves to the right (no wrap around).
|
||||
// 3. [left] wrapping around to the left side.
|
||||
// 4. [left] wrapping around to the right side.
|
||||
// 5. Resize.
|
||||
func TestUnboundedSliceDequePushLeftPopLeft(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
// Starts empty.
|
||||
bIntf := NewUnboundedDeque[int](2)
|
||||
require.IsType(&unboundedSliceDeque[int]{}, bIntf)
|
||||
b := bIntf.(*unboundedSliceDeque[int])
|
||||
require.Zero(bIntf.Len())
|
||||
require.Len(b.data, 2)
|
||||
require.Zero(b.left)
|
||||
require.Equal(1, b.right)
|
||||
require.Empty(b.List())
|
||||
// slice is [EMPTY]
|
||||
|
||||
_, ok := b.PopLeft()
|
||||
require.False(ok)
|
||||
_, ok = b.PeekLeft()
|
||||
require.False(ok)
|
||||
_, ok = b.PeekRight()
|
||||
require.False(ok)
|
||||
|
||||
b.PushLeft(1) // slice is [1,EMPTY]
|
||||
require.Equal(1, b.Len())
|
||||
require.Len(b.data, 2)
|
||||
require.Equal(1, b.left)
|
||||
require.Equal(1, b.right)
|
||||
require.Equal([]int{1}, b.List())
|
||||
|
||||
got, ok := b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
// This causes a resize
|
||||
b.PushLeft(2) // slice is [2,1,EMPTY,EMPTY]
|
||||
require.Equal(2, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(3, b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Equal([]int{2, 1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
got, ok = b.Index(1)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
// Tests left moving left with no wrap around.
|
||||
b.PushLeft(3) // slice is [2,1,EMPTY,3]
|
||||
require.Equal(3, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(2, b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Equal([]int{3, 2, 1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(3, got)
|
||||
got, ok = b.Index(1)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
got, ok = b.Index(2)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
_, ok = b.Index(3)
|
||||
require.False(ok)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(3, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
// Tests left moving right with no wrap around.
|
||||
got, ok = b.PopLeft() // slice is [2,1,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(3, got)
|
||||
require.Equal(2, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(3, b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Equal([]int{2, 1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
got, ok = b.Index(1)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
// Tests left wrapping around to the left side.
|
||||
got, ok = b.PopLeft() // slice is [EMPTY,1,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Equal(1, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Zero(b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
// Test left wrapping around to the right side.
|
||||
b.PushLeft(2) // slice is [2,1,EMPTY,EMPTY]
|
||||
require.Equal(2, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(3, b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Equal([]int{2, 1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
got, ok = b.Index(1)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PopLeft() // slice is [EMPTY,1,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Equal(1, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Zero(b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PopLeft() // slice is [EMPTY,EMPTY,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Zero(b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(1, b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
_, ok = b.PopLeft()
|
||||
require.False(ok)
|
||||
_, ok = b.PeekLeft()
|
||||
require.False(ok)
|
||||
_, ok = b.PeekRight()
|
||||
require.False(ok)
|
||||
}
|
||||
|
||||
func TestUnboundedSliceDequePushRightPopRight(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
// Starts empty.
|
||||
bIntf := NewUnboundedDeque[int](2)
|
||||
require.IsType(&unboundedSliceDeque[int]{}, bIntf)
|
||||
b := bIntf.(*unboundedSliceDeque[int])
|
||||
require.Zero(bIntf.Len())
|
||||
require.Len(b.data, 2)
|
||||
require.Zero(b.left)
|
||||
require.Equal(1, b.right)
|
||||
require.Empty(b.List())
|
||||
// slice is [EMPTY]
|
||||
|
||||
_, ok := b.PopRight()
|
||||
require.False(ok)
|
||||
_, ok = b.PeekLeft()
|
||||
require.False(ok)
|
||||
_, ok = b.PeekRight()
|
||||
require.False(ok)
|
||||
|
||||
b.PushRight(1) // slice is [1,EMPTY]
|
||||
require.Equal(1, b.Len())
|
||||
require.Len(b.data, 2)
|
||||
require.Zero(b.left)
|
||||
require.Zero(b.right)
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok := b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
// This causes a resize
|
||||
b.PushRight(2) // slice is [1,2,EMPTY,EMPTY]
|
||||
require.Equal(2, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(3, b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Equal([]int{1, 2}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
got, ok = b.Index(1)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
// Tests right moving right with no wrap around
|
||||
b.PushRight(3) // slice is [1,2,3,EMPTY]
|
||||
require.Equal(3, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(3, b.left)
|
||||
require.Equal(3, b.right)
|
||||
require.Equal([]int{1, 2, 3}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
got, ok = b.Index(1)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
got, ok = b.Index(2)
|
||||
require.True(ok)
|
||||
require.Equal(3, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(3, got)
|
||||
|
||||
// Tests right moving left with no wrap around
|
||||
got, ok = b.PopRight() // slice is [1,2,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(3, got)
|
||||
require.Equal(2, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(3, b.left)
|
||||
require.Equal(2, b.right)
|
||||
require.Equal([]int{1, 2}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
got, ok = b.Index(1)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
_, ok = b.Index(2)
|
||||
require.False(ok)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PopRight() // slice is [1,EMPTY,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Equal(1, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(3, b.left)
|
||||
require.Equal(1, b.right)
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
_, ok = b.Index(1)
|
||||
require.False(ok)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PopRight() // slice is [EMPTY,EMPTY,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Zero(b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(3, b.left)
|
||||
require.Zero(b.right)
|
||||
require.Empty(b.List())
|
||||
require.Zero(b.Len())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
_, ok = b.PeekLeft()
|
||||
require.False(ok)
|
||||
_, ok = b.PeekRight()
|
||||
require.False(ok)
|
||||
_, ok = b.PopRight()
|
||||
require.False(ok)
|
||||
|
||||
b.PushLeft(1) // slice is [EMPTY,EMPTY,EMPTY,1]
|
||||
require.Equal(1, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(2, b.left)
|
||||
require.Zero(b.right)
|
||||
require.Equal([]int{1}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
|
||||
// Test right wrapping around to the right
|
||||
got, ok = b.PopRight() // slice is [EMPTY,EMPTY,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(1, got)
|
||||
require.Zero(b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(2, b.left)
|
||||
require.Equal(3, b.right)
|
||||
require.Empty(b.List())
|
||||
require.Zero(b.Len())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
_, ok = b.PeekLeft()
|
||||
require.False(ok)
|
||||
|
||||
_, ok = b.PeekRight()
|
||||
require.False(ok)
|
||||
|
||||
// Tests right wrapping around to the left
|
||||
b.PushRight(2) // slice is [EMPTY,EMPTY,EMPTY,2]
|
||||
require.Equal(1, b.Len())
|
||||
require.Len(b.data, 4)
|
||||
require.Equal(2, b.left)
|
||||
require.Zero(b.right)
|
||||
require.Equal([]int{2}, b.List())
|
||||
got, ok = b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PeekLeft()
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PeekRight()
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
|
||||
got, ok = b.PopRight() // slice is [EMPTY,EMPTY,EMPTY,EMPTY]
|
||||
require.True(ok)
|
||||
require.Equal(2, got)
|
||||
require.Empty(b.List())
|
||||
_, ok = b.Index(0)
|
||||
require.False(ok)
|
||||
|
||||
_, ok = b.PeekLeft()
|
||||
require.False(ok)
|
||||
_, ok = b.PeekRight()
|
||||
require.False(ok)
|
||||
_, ok = b.PopRight()
|
||||
require.False(ok)
|
||||
}
|
||||
|
||||
func FuzzUnboundedSliceDeque(f *testing.F) {
|
||||
f.Fuzz(
|
||||
func(t *testing.T, initSize uint, input []byte) {
|
||||
require := require.New(t)
|
||||
b := NewUnboundedDeque[byte](int(initSize))
|
||||
for i, n := range input {
|
||||
b.PushRight(n)
|
||||
gotIndex, ok := b.Index(i)
|
||||
require.True(ok)
|
||||
require.Equal(n, gotIndex)
|
||||
}
|
||||
|
||||
list := b.List()
|
||||
require.Len(list, len(input))
|
||||
for i, n := range input {
|
||||
require.Equal(n, list[i])
|
||||
}
|
||||
|
||||
for i := 0; i < len(input); i++ {
|
||||
_, _ = b.PopLeft()
|
||||
list = b.List()
|
||||
if i == len(input)-1 {
|
||||
require.Empty(list)
|
||||
_, ok := b.Index(0)
|
||||
require.False(ok)
|
||||
} else {
|
||||
require.Equal(input[i+1:], list)
|
||||
got, ok := b.Index(0)
|
||||
require.True(ok)
|
||||
require.Equal(input[i+1], got)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/bits"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// RandomBytes returns a slice of n random bytes
|
||||
// Intended for use in testing
|
||||
func RandomBytes(n int) []byte {
|
||||
b := make([]byte, n)
|
||||
_, _ = rand.Read(b)
|
||||
return b
|
||||
}
|
||||
|
||||
// Constant taken from the "math" package
|
||||
const intSize = 32 << (^uint(0) >> 63) // 32 or 64
|
||||
|
||||
// BytesPool tracks buckets of available buffers to be allocated. Each bucket
|
||||
// allocates buffers of the following length:
|
||||
//
|
||||
// 0
|
||||
// 1
|
||||
// 3
|
||||
// 7
|
||||
// 15
|
||||
// 31
|
||||
// 63
|
||||
// 127
|
||||
// ...
|
||||
// MaxInt
|
||||
//
|
||||
// In order to allocate a buffer of length 19 (for example), we calculate the
|
||||
// number of bits required to represent 19 (5). And therefore allocate a slice
|
||||
// from bucket 5, which has length 31. This is the bucket which produces the
|
||||
// smallest slices that are at least length 19.
|
||||
//
|
||||
// When replacing a buffer of length 19, we calculate the number of bits
|
||||
// required to represent 20 (5). And therefore place the slice into bucket 4,
|
||||
// which has length 15. This is the bucket which produces the largest slices
|
||||
// that a length 19 slice can be used for.
|
||||
type BytesPool [intSize]sync.Pool
|
||||
|
||||
func NewBytesPool() *BytesPool {
|
||||
var p BytesPool
|
||||
for i := range p {
|
||||
// uint is used here to avoid overflowing int during the shift
|
||||
size := uint(1)<<i - 1
|
||||
p[i] = sync.Pool{
|
||||
New: func() interface{} {
|
||||
// Sync pool needs to return pointer-like values to avoid memory
|
||||
// allocations.
|
||||
b := make([]byte, size)
|
||||
return &b
|
||||
},
|
||||
}
|
||||
}
|
||||
return &p
|
||||
}
|
||||
|
||||
// Get returns a non-nil pointer to a slice with the requested length.
|
||||
//
|
||||
// It is not guaranteed for the returned bytes to have been zeroed.
|
||||
func (p *BytesPool) Get(length int) *[]byte {
|
||||
index := bits.Len(uint(length)) // Round up
|
||||
bytes := p[index].Get().(*[]byte)
|
||||
*bytes = (*bytes)[:length] // Set the length to be the expected value
|
||||
return bytes
|
||||
}
|
||||
|
||||
// Put takes ownership of a non-nil pointer to a slice of bytes.
|
||||
//
|
||||
// Note: this function takes ownership of the underlying array. So, the length
|
||||
// of the provided slice is ignored and only its capacity is used.
|
||||
func (p *BytesPool) Put(bytes *[]byte) {
|
||||
size := cap(*bytes)
|
||||
index := bits.Len(uint(size)+1) - 1 // Round down
|
||||
p[index].Put(bytes)
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Verify that [intSize] is correctly set based on the word size. This test uses
|
||||
// [math.MaxInt] to detect the word size.
|
||||
func TestIntSize(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
require.Contains([]int{32, 64}, intSize)
|
||||
if intSize == 32 {
|
||||
require.Equal(math.MaxInt32, math.MaxInt)
|
||||
} else {
|
||||
require.Equal(math.MaxInt64, math.MaxInt)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBytesPool(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
p := NewBytesPool()
|
||||
for i := 0; i < 128; i++ {
|
||||
bytes := p.Get(i)
|
||||
require.NotNil(bytes)
|
||||
require.Len(*bytes, i)
|
||||
p.Put(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBytesPool_Constant(b *testing.B) {
|
||||
sizes := []int{
|
||||
0,
|
||||
8,
|
||||
16,
|
||||
32,
|
||||
64,
|
||||
256,
|
||||
2048,
|
||||
}
|
||||
for _, size := range sizes {
|
||||
b.Run(strconv.Itoa(size), func(b *testing.B) {
|
||||
p := NewBytesPool()
|
||||
for i := 0; i < b.N; i++ {
|
||||
p.Put(p.Get(size))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBytesPool_Descending(b *testing.B) {
|
||||
p := NewBytesPool()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for size := 100_000; size > 0; size-- {
|
||||
p.Put(p.Get(size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBytesPool_Ascending(b *testing.B) {
|
||||
p := NewBytesPool()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for size := 0; size < 100_000; size++ {
|
||||
p.Put(p.Get(size))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkBytesPool_Random(b *testing.B) {
|
||||
p := NewBytesPool()
|
||||
sizes := make([]int, 1_000)
|
||||
for i := range sizes {
|
||||
sizes[i] = rand.Intn(100_000) //#nosec G404
|
||||
}
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
for _, size := range sizes {
|
||||
p.Put(p.Get(size))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cb58
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/mr-tron/base58/base58"
|
||||
|
||||
"github.com/luxfi/node/utils/hashing"
|
||||
)
|
||||
|
||||
const checksumLen = 4
|
||||
|
||||
var (
|
||||
ErrBase58Decoding = errors.New("base58 decoding error")
|
||||
ErrMissingChecksum = errors.New("input string is smaller than the checksum size")
|
||||
ErrBadChecksum = errors.New("invalid input checksum")
|
||||
errEncodingOverFlow = errors.New("encoding overflow")
|
||||
)
|
||||
|
||||
// Encode [bytes] to a string using cb58 format.
|
||||
// [bytes] may be nil, in which case it will be treated the same as an empty
|
||||
// slice.
|
||||
func Encode(bytes []byte) (string, error) {
|
||||
bytesLen := len(bytes)
|
||||
if bytesLen > math.MaxInt32-checksumLen {
|
||||
return "", errEncodingOverFlow
|
||||
}
|
||||
checked := make([]byte, bytesLen+checksumLen)
|
||||
copy(checked, bytes)
|
||||
copy(checked[len(bytes):], hashing.Checksum(bytes, checksumLen))
|
||||
return base58.Encode(checked), nil
|
||||
}
|
||||
|
||||
// Decode [str] to bytes from cb58.
|
||||
func Decode(str string) ([]byte, error) {
|
||||
decodedBytes, err := base58.Decode(str)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%w: %w", ErrBase58Decoding, err)
|
||||
}
|
||||
if len(decodedBytes) < checksumLen {
|
||||
return nil, ErrMissingChecksum
|
||||
}
|
||||
// Verify the checksum
|
||||
rawBytes := decodedBytes[:len(decodedBytes)-checksumLen]
|
||||
checksum := decodedBytes[len(decodedBytes)-checksumLen:]
|
||||
if !bytes.Equal(checksum, hashing.Checksum(rawBytes, checksumLen)) {
|
||||
return nil, ErrBadChecksum
|
||||
}
|
||||
return rawBytes, nil
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package cb58
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Test encoding bytes to a string and decoding back to bytes
|
||||
func TestEncodeDecode(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
type test struct {
|
||||
bytes []byte
|
||||
str string
|
||||
}
|
||||
|
||||
id := [32]byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32}
|
||||
tests := []test{
|
||||
{
|
||||
nil,
|
||||
"45PJLL",
|
||||
},
|
||||
{
|
||||
[]byte{},
|
||||
"45PJLL",
|
||||
},
|
||||
{
|
||||
[]byte{0},
|
||||
"1c7hwa",
|
||||
},
|
||||
{
|
||||
[]byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 255},
|
||||
"1NVSVezva3bAtJesnUj",
|
||||
},
|
||||
{
|
||||
id[:],
|
||||
"SkB92YpWm4Q2ijQHH34cqbKkCZWszsiQgHVjtNeFF2HdvDQU",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
// Encode the bytes
|
||||
strResult, err := Encode(test.bytes)
|
||||
require.NoError(err)
|
||||
// Make sure the string repr. is what we expected
|
||||
require.Equal(test.str, strResult)
|
||||
// Decode the string
|
||||
bytesResult, err := Decode(strResult)
|
||||
require.NoError(err)
|
||||
// Make sure we got the same bytes back
|
||||
require.True(bytes.Equal(test.bytes, bytesResult))
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzEncodeDecode(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
require := require.New(t)
|
||||
|
||||
// Encode bytes to string
|
||||
dataStr, err := Encode(data)
|
||||
require.NoError(err)
|
||||
|
||||
// Decode string to bytes
|
||||
gotData, err := Decode(dataStr)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(data, gotData)
|
||||
})
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
// Compressor compress and decompresses messages.
|
||||
// Decompress is the inverse of Compress.
|
||||
// Decompress(Compress(msg)) == msg.
|
||||
type Compressor interface {
|
||||
Compress([]byte) ([]byte, error)
|
||||
Decompress([]byte) ([]byte, error)
|
||||
}
|
||||
@@ -1,245 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
_ "embed"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
"github.com/luxfi/node/utils/units"
|
||||
)
|
||||
|
||||
const maxMessageSize = 2 * units.MiB // Max message size. Can't import due to cycle.
|
||||
|
||||
var (
|
||||
newCompressorFuncs = map[Type]func(maxSize int64) (Compressor, error){
|
||||
TypeNone: func(int64) (Compressor, error) { //nolint:unparam // an error is needed to be returned to compile
|
||||
return NewNoCompressor(), nil
|
||||
},
|
||||
TypeZstd: NewZstdCompressor,
|
||||
}
|
||||
|
||||
//go:embed zstd_zip_bomb.bin
|
||||
zstdZipBomb []byte
|
||||
|
||||
zipBombs = map[Type][]byte{
|
||||
TypeZstd: zstdZipBomb,
|
||||
}
|
||||
)
|
||||
|
||||
func TestDecompressZipBombs(t *testing.T) {
|
||||
for compressionType, zipBomb := range zipBombs {
|
||||
// Make sure that the hardcoded zip bomb would be a valid message.
|
||||
require.Less(t, len(zipBomb), maxMessageSize)
|
||||
|
||||
newCompressorFunc := newCompressorFuncs[compressionType]
|
||||
|
||||
t.Run(compressionType.String(), func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
compressor, err := newCompressorFunc(maxMessageSize)
|
||||
require.NoError(err)
|
||||
|
||||
var (
|
||||
beforeDecompressionStats runtime.MemStats
|
||||
afterDecompressionStats runtime.MemStats
|
||||
)
|
||||
runtime.ReadMemStats(&beforeDecompressionStats)
|
||||
_, err = compressor.Decompress(zipBomb)
|
||||
runtime.ReadMemStats(&afterDecompressionStats)
|
||||
|
||||
require.ErrorIs(err, ErrDecompressedMsgTooLarge)
|
||||
|
||||
// Make sure that we didn't allocate significantly more memory than
|
||||
// the max message size.
|
||||
bytesAllocatedDuringDecompression := afterDecompressionStats.TotalAlloc - beforeDecompressionStats.TotalAlloc
|
||||
require.Less(bytesAllocatedDuringDecompression, uint64(10*maxMessageSize))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCompressDecompress(t *testing.T) {
|
||||
for compressionType, newCompressorFunc := range newCompressorFuncs {
|
||||
t.Run(compressionType.String(), func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
data := utils.RandomBytes(4096)
|
||||
data2 := utils.RandomBytes(4096)
|
||||
|
||||
compressor, err := newCompressorFunc(maxMessageSize)
|
||||
require.NoError(err)
|
||||
|
||||
dataCompressed, err := compressor.Compress(data)
|
||||
require.NoError(err)
|
||||
|
||||
data2Compressed, err := compressor.Compress(data2)
|
||||
require.NoError(err)
|
||||
|
||||
dataDecompressed, err := compressor.Decompress(dataCompressed)
|
||||
require.NoError(err)
|
||||
require.Equal(data, dataDecompressed)
|
||||
|
||||
data2Decompressed, err := compressor.Decompress(data2Compressed)
|
||||
require.NoError(err)
|
||||
require.Equal(data2, data2Decompressed)
|
||||
|
||||
dataDecompressed, err = compressor.Decompress(dataCompressed)
|
||||
require.NoError(err)
|
||||
require.Equal(data, dataDecompressed)
|
||||
|
||||
maxMessage := utils.RandomBytes(maxMessageSize)
|
||||
maxMessageCompressed, err := compressor.Compress(maxMessage)
|
||||
require.NoError(err)
|
||||
|
||||
maxMessageDecompressed, err := compressor.Decompress(maxMessageCompressed)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(maxMessage, maxMessageDecompressed)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSizeLimiting(t *testing.T) {
|
||||
for compressionType, compressorFunc := range newCompressorFuncs {
|
||||
if compressionType == TypeNone {
|
||||
continue
|
||||
}
|
||||
t.Run(compressionType.String(), func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
compressor, err := compressorFunc(maxMessageSize)
|
||||
require.NoError(err)
|
||||
|
||||
data := make([]byte, maxMessageSize+1)
|
||||
_, err = compressor.Compress(data) // should be too large
|
||||
require.ErrorIs(err, ErrMsgTooLarge)
|
||||
|
||||
compressor2, err := compressorFunc(2 * maxMessageSize)
|
||||
require.NoError(err)
|
||||
|
||||
dataCompressed, err := compressor2.Compress(data)
|
||||
require.NoError(err)
|
||||
|
||||
_, err = compressor.Decompress(dataCompressed) // should be too large
|
||||
require.ErrorIs(err, ErrDecompressedMsgTooLarge)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Attempts to create a compressor with math.MaxInt64
|
||||
// which leads to undefined decompress behavior due to integer overflow
|
||||
// in limit reader creation.
|
||||
func TestNewCompressorWithInvalidLimit(t *testing.T) {
|
||||
for compressionType, compressorFunc := range newCompressorFuncs {
|
||||
if compressionType == TypeNone {
|
||||
continue
|
||||
}
|
||||
t.Run(compressionType.String(), func(t *testing.T) {
|
||||
_, err := compressorFunc(math.MaxInt64)
|
||||
require.ErrorIs(t, err, ErrInvalidMaxSizeCompressor)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func FuzzZstdCompressor(f *testing.F) {
|
||||
fuzzHelper(f, TypeZstd)
|
||||
}
|
||||
|
||||
func fuzzHelper(f *testing.F, compressionType Type) {
|
||||
var (
|
||||
compressor Compressor
|
||||
err error
|
||||
)
|
||||
switch compressionType {
|
||||
case TypeZstd:
|
||||
compressor, err = NewZstdCompressor(maxMessageSize)
|
||||
require.NoError(f, err)
|
||||
default:
|
||||
require.FailNow(f, "Unknown compression type")
|
||||
}
|
||||
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
require := require.New(t)
|
||||
|
||||
if len(data) > maxMessageSize {
|
||||
_, err := compressor.Compress(data)
|
||||
require.ErrorIs(err, ErrMsgTooLarge)
|
||||
}
|
||||
|
||||
compressed, err := compressor.Compress(data)
|
||||
require.NoError(err)
|
||||
|
||||
decompressed, err := compressor.Decompress(compressed)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(data, decompressed)
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkCompress(b *testing.B) {
|
||||
sizes := []int{
|
||||
0,
|
||||
256,
|
||||
units.KiB,
|
||||
units.MiB,
|
||||
maxMessageSize,
|
||||
}
|
||||
for compressionType, newCompressorFunc := range newCompressorFuncs {
|
||||
if compressionType == TypeNone {
|
||||
continue
|
||||
}
|
||||
for _, size := range sizes {
|
||||
b.Run(fmt.Sprintf("%s_%d", compressionType, size), func(b *testing.B) {
|
||||
require := require.New(b)
|
||||
|
||||
bytes := utils.RandomBytes(size)
|
||||
compressor, err := newCompressorFunc(maxMessageSize)
|
||||
require.NoError(err)
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := compressor.Compress(bytes)
|
||||
require.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkDecompress(b *testing.B) {
|
||||
sizes := []int{
|
||||
0,
|
||||
256,
|
||||
units.KiB,
|
||||
units.MiB,
|
||||
maxMessageSize,
|
||||
}
|
||||
for compressionType, newCompressorFunc := range newCompressorFuncs {
|
||||
if compressionType == TypeNone {
|
||||
continue
|
||||
}
|
||||
for _, size := range sizes {
|
||||
b.Run(fmt.Sprintf("%s_%d", compressionType, size), func(b *testing.B) {
|
||||
require := require.New(b)
|
||||
|
||||
bytes := utils.RandomBytes(size)
|
||||
compressor, err := newCompressorFunc(maxMessageSize)
|
||||
require.NoError(err)
|
||||
|
||||
compressedBytes, err := compressor.Compress(bytes)
|
||||
require.NoError(err)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := compressor.Decompress(compressedBytes)
|
||||
require.NoError(err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrInvalidMaxSizeCompressor = errors.New("invalid compressor max size")
|
||||
ErrDecompressedMsgTooLarge = errors.New("decompressed msg too large")
|
||||
ErrMsgTooLarge = errors.New("msg too large to be compressed")
|
||||
)
|
||||
@@ -1,83 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var _ Compressor = (*gzipCompressor)(nil)
|
||||
|
||||
type gzipCompressor struct {
|
||||
maxSize int64
|
||||
gzipWriterPool sync.Pool
|
||||
}
|
||||
|
||||
// Compress [msg] and returns the compressed bytes.
|
||||
func (g *gzipCompressor) Compress(msg []byte) ([]byte, error) {
|
||||
if int64(len(msg)) > g.maxSize {
|
||||
return nil, fmt.Errorf("%w: (%d) > (%d)", ErrMsgTooLarge, len(msg), g.maxSize)
|
||||
}
|
||||
|
||||
var writeBuffer bytes.Buffer
|
||||
gzipWriter := g.gzipWriterPool.Get().(*gzip.Writer)
|
||||
gzipWriter.Reset(&writeBuffer)
|
||||
defer g.gzipWriterPool.Put(gzipWriter)
|
||||
|
||||
if _, err := gzipWriter.Write(msg); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := gzipWriter.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeBuffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// Decompress decompresses [msg].
|
||||
func (g *gzipCompressor) Decompress(msg []byte) ([]byte, error) {
|
||||
bytesReader := bytes.NewReader(msg)
|
||||
gzipReader, err := gzip.NewReader(bytesReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// We allow [io.LimitReader] to read up to [g.maxSize + 1] bytes, so that if
|
||||
// the decompressed payload is greater than the maximum size, this function
|
||||
// will return the appropriate error instead of an incomplete byte slice.
|
||||
limitedReader := io.LimitReader(gzipReader, g.maxSize+1)
|
||||
|
||||
decompressed, err := io.ReadAll(limitedReader)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if int64(len(decompressed)) > g.maxSize {
|
||||
return nil, fmt.Errorf("%w: (%d) > (%d)", ErrDecompressedMsgTooLarge, len(decompressed), g.maxSize)
|
||||
}
|
||||
return decompressed, gzipReader.Close()
|
||||
}
|
||||
|
||||
// NewGzipCompressor returns a new gzip Compressor that compresses
|
||||
func NewGzipCompressor(maxSize int64) (Compressor, error) {
|
||||
if maxSize == math.MaxInt64 {
|
||||
// "Decompress" creates "io.LimitReader" with max size + 1:
|
||||
// if the max size + 1 overflows, "io.LimitReader" reads nothing
|
||||
// returning 0 byte for the decompress call
|
||||
// require max size <math.MaxInt64 to prevent int64 overflows
|
||||
return nil, ErrInvalidMaxSizeCompressor
|
||||
}
|
||||
|
||||
return &gzipCompressor{
|
||||
maxSize: maxSize,
|
||||
gzipWriterPool: sync.Pool{
|
||||
New: func() interface{} {
|
||||
return gzip.NewWriter(nil)
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
Binary file not shown.
@@ -1,20 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
var _ Compressor = (*noCompressor)(nil)
|
||||
|
||||
type noCompressor struct{}
|
||||
|
||||
func (*noCompressor) Compress(msg []byte) ([]byte, error) {
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func (*noCompressor) Decompress(msg []byte) ([]byte, error) {
|
||||
return msg, nil
|
||||
}
|
||||
|
||||
func NewNoCompressor() Compressor {
|
||||
return &noCompressor{}
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNoCompressor(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
data := []byte{1, 2, 3}
|
||||
compressor := NewNoCompressor()
|
||||
compressedBytes, err := compressor.Compress(data)
|
||||
require.NoError(err)
|
||||
require.Equal(data, compressedBytes)
|
||||
|
||||
decompressedBytes, err := compressor.Decompress(compressedBytes)
|
||||
require.NoError(err)
|
||||
require.Equal(data, decompressedBytes)
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var errUnknownCompressionType = errors.New("unknown compression type")
|
||||
|
||||
type Type byte
|
||||
|
||||
const (
|
||||
TypeNone Type = iota + 1
|
||||
TypeZstd
|
||||
)
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case TypeNone:
|
||||
return "none"
|
||||
case TypeZstd:
|
||||
return "zstd"
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
func TypeFromString(s string) (Type, error) {
|
||||
switch s {
|
||||
case TypeNone.String():
|
||||
return TypeNone, nil
|
||||
case TypeZstd.String():
|
||||
return TypeZstd, nil
|
||||
default:
|
||||
return TypeNone, errUnknownCompressionType
|
||||
}
|
||||
}
|
||||
|
||||
func (t Type) MarshalJSON() ([]byte, error) {
|
||||
var b strings.Builder
|
||||
if _, err := b.WriteString(`"`); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := b.WriteString(t.String()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := b.WriteString(`"`); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []byte(b.String()), nil
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestTypeString(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
for _, compressionType := range []Type{TypeNone, TypeZstd} {
|
||||
s := compressionType.String()
|
||||
parsedType, err := TypeFromString(s)
|
||||
require.NoError(err)
|
||||
require.Equal(compressionType, parsedType)
|
||||
}
|
||||
|
||||
_, err := TypeFromString("unknown")
|
||||
require.ErrorIs(err, errUnknownCompressionType)
|
||||
}
|
||||
|
||||
func TestTypeMarshalJSON(t *testing.T) {
|
||||
type test struct {
|
||||
Type Type
|
||||
expected string
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{
|
||||
Type: TypeNone,
|
||||
expected: `"none"`,
|
||||
},
|
||||
{
|
||||
Type: TypeZstd,
|
||||
expected: `"zstd"`,
|
||||
},
|
||||
{
|
||||
Type: Type(0),
|
||||
expected: `"unknown"`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.Type.String(), func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
b, err := tt.Type.MarshalJSON()
|
||||
require.NoError(err)
|
||||
require.Equal(tt.expected, string(b))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package compression
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
"github.com/klauspost/compress/zstd"
|
||||
)
|
||||
|
||||
var _ Compressor = (*zstdCompressor)(nil)
|
||||
|
||||
func NewZstdCompressor(maxSize int64) (Compressor, error) {
|
||||
return NewZstdCompressorWithLevel(maxSize, zstd.SpeedDefault)
|
||||
}
|
||||
|
||||
func NewZstdCompressorWithLevel(maxSize int64, level zstd.EncoderLevel) (Compressor, error) {
|
||||
if maxSize == math.MaxInt64 {
|
||||
// "Decompress" creates "io.LimitReader" with max size + 1:
|
||||
// if the max size + 1 overflows, "io.LimitReader" reads nothing
|
||||
// returning 0 byte for the decompress call
|
||||
// require max size < math.MaxInt64 to prevent int64 overflows
|
||||
return nil, ErrInvalidMaxSizeCompressor
|
||||
}
|
||||
decoder, err := zstd.NewReader(nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &zstdCompressor{
|
||||
maxSize: maxSize,
|
||||
level: level,
|
||||
decoder: decoder,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type zstdCompressor struct {
|
||||
maxSize int64
|
||||
level zstd.EncoderLevel
|
||||
decoder *zstd.Decoder
|
||||
}
|
||||
|
||||
func (z *zstdCompressor) Compress(msg []byte) ([]byte, error) {
|
||||
if int64(len(msg)) > z.maxSize {
|
||||
return nil, fmt.Errorf("%w: (%d) > (%d)", ErrMsgTooLarge, len(msg), z.maxSize)
|
||||
}
|
||||
encoder, err := zstd.NewWriter(nil, zstd.WithEncoderLevel(z.level))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return encoder.EncodeAll(msg, nil), nil
|
||||
}
|
||||
|
||||
func (z *zstdCompressor) Decompress(msg []byte) ([]byte, error) {
|
||||
decompressed, err := z.decoder.DecodeAll(msg, nil)
|
||||
if err != nil {
|
||||
// If the decoder returns an error about size limit, wrap it with our error
|
||||
if err.Error() == "decompressed size exceeds configured limit" {
|
||||
return nil, fmt.Errorf("%w: decompression stopped due to size limit", ErrDecompressedMsgTooLarge)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if int64(len(decompressed)) > z.maxSize {
|
||||
return nil, fmt.Errorf("%w: (%d) > (%d)", ErrDecompressedMsgTooLarge, len(decompressed), z.maxSize)
|
||||
}
|
||||
return decompressed, nil
|
||||
}
|
||||
Binary file not shown.
@@ -1,10 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package constants
|
||||
|
||||
// ChainAliasPrefix denotes a prefix for an alias that belongs to a blockchain ID.
|
||||
const ChainAliasPrefix string = "bc"
|
||||
|
||||
// VMAliasPrefix denotes a prefix for an alias that belongs to a VM ID.
|
||||
const VMAliasPrefix string = "vm"
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package constants
|
||||
|
||||
// Const variables to be exported
|
||||
const (
|
||||
// PlatformName exports the name of the platform
|
||||
PlatformName = "lux"
|
||||
|
||||
// AppName exports the name of the lux application
|
||||
AppName = "node"
|
||||
)
|
||||
@@ -1,52 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package constants
|
||||
|
||||
import "github.com/luxfi/math/set"
|
||||
|
||||
var (
|
||||
// ActivatedACPs is the set of ACPs that are activated.
|
||||
//
|
||||
// See: https://github.com/orgs/luxfi/projects/1
|
||||
ActivatedACPs = set.Of[uint32](
|
||||
// Durango:
|
||||
23, // https://github.com/luxfi/ACPs/blob/main/ACPs/23-p-chain-native-transfers/README.md
|
||||
24, // https://github.com/luxfi/ACPs/blob/main/ACPs/24-shanghai-eips/README.md
|
||||
25, // https://github.com/luxfi/ACPs/blob/main/ACPs/25-vm-application-errors/README.md
|
||||
30, // https://github.com/luxfi/ACPs/blob/main/ACPs/30-lux-warp-x-evm/README.md
|
||||
31, // https://github.com/luxfi/ACPs/blob/main/ACPs/31-enable-subnet-ownership-transfer/README.md
|
||||
41, // https://github.com/luxfi/ACPs/blob/main/ACPs/41-remove-pending-stakers/README.md
|
||||
62, // https://github.com/luxfi/ACPs/blob/main/ACPs/62-disable-addvalidatortx-and-adddelegatortx/README.md
|
||||
|
||||
// Etna:
|
||||
77, // https://github.com/luxfi/ACPs/blob/main/ACPs/77-reinventing-subnets/README.md
|
||||
103, // https://github.com/luxfi/ACPs/blob/main/ACPs/103-dynamic-fees/README.md
|
||||
118, // https://github.com/luxfi/ACPs/blob/main/ACPs/118-warp-signature-request/README.md
|
||||
125, // https://github.com/luxfi/ACPs/blob/main/ACPs/125-basefee-reduction/README.md
|
||||
131, // https://github.com/luxfi/ACPs/blob/main/ACPs/131-cancun-eips/README.md
|
||||
151, // https://github.com/luxfi/ACPs/blob/main/ACPs/151-use-current-block-pchain-height-as-context/README.md
|
||||
)
|
||||
|
||||
// CurrentACPs is the set of ACPs that are currently, at the time of
|
||||
// release, marked as implementable and not activated.
|
||||
//
|
||||
// See: https://github.com/orgs/luxfi/projects/1
|
||||
CurrentACPs = set.Of[uint32](
|
||||
176, // https://github.com/luxfi/ACPs/blob/main/ACPs/176-dynamic-evm-gas-limit-and-price-discovery-updates/README.md
|
||||
)
|
||||
|
||||
// ScheduledACPs are the ACPs included into the next upgrade.
|
||||
ScheduledACPs = set.Of[uint32](
|
||||
176, // https://github.com/luxfi/ACPs/blob/main/ACPs/176-dynamic-evm-gas-limit-and-price-discovery-updates/README.md
|
||||
)
|
||||
|
||||
// CurrentLPs is the set of supported Lux Protocols
|
||||
CurrentLPs = set.Of[uint32](176)
|
||||
|
||||
// ScheduledLPs are the LPs included in the next upgrade
|
||||
ScheduledLPs = set.Of[uint32](176)
|
||||
|
||||
// ActivatedLPs is the set of activated LPs
|
||||
ActivatedLPs = set.Of[uint32](176)
|
||||
)
|
||||
@@ -1,8 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package constants
|
||||
|
||||
// PointerOverhead is used to approximate the memory footprint from allocating a
|
||||
// pointer.
|
||||
const PointerOverhead = 8
|
||||
@@ -1,122 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package constants
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/math/set"
|
||||
)
|
||||
|
||||
// Const variables to be exported
|
||||
const (
|
||||
LocalID uint32 = 31337
|
||||
MainnetID uint32 = 1 // Use 1 for Lux compatibility
|
||||
TestnetID uint32 = 5 // Use 5 for Lux compatibility
|
||||
UnitTestID uint32 = 369
|
||||
|
||||
// Lux-specific network IDs
|
||||
LuxMainnetID uint32 = 96369 // Lux mainnet
|
||||
LuxTestnetID uint32 = 96370 // Lux testnet
|
||||
QChainMainnetID uint32 = 96380 // Q-Chain mainnet
|
||||
QChainTestnetID uint32 = 96381 // Q-Chain testnet
|
||||
|
||||
LocalName = "local"
|
||||
MainnetName = "mainnet"
|
||||
TestnetName = "testnet"
|
||||
UnitTestName = "testing"
|
||||
|
||||
FallbackHRP = "custom"
|
||||
LocalHRP = "local"
|
||||
MainnetHRP = "lux"
|
||||
TestnetHRP = "test"
|
||||
UnitTestHRP = "testing"
|
||||
)
|
||||
|
||||
// Variables to be exported
|
||||
var (
|
||||
PrimaryNetworkID = ids.Empty
|
||||
PlatformChainID = ids.Empty
|
||||
|
||||
// Q-Chain specific IDs
|
||||
QChainID = ids.ID{'q', 'c', 'h', 'a', 'i', 'n'}
|
||||
|
||||
NetworkIDToNetworkName = map[uint32]string{
|
||||
LocalID: LocalName,
|
||||
MainnetID: MainnetName,
|
||||
TestnetID: TestnetName,
|
||||
UnitTestID: UnitTestName,
|
||||
LuxMainnetID: MainnetName,
|
||||
LuxTestnetID: TestnetName,
|
||||
QChainMainnetID: "qchain-mainnet",
|
||||
QChainTestnetID: "qchain-testnet",
|
||||
}
|
||||
NetworkNameToNetworkID = map[string]uint32{
|
||||
LocalName: LocalID,
|
||||
MainnetName: MainnetID,
|
||||
TestnetName: TestnetID,
|
||||
UnitTestName: UnitTestID,
|
||||
}
|
||||
|
||||
NetworkIDToHRP = map[uint32]string{
|
||||
LocalID: LocalHRP,
|
||||
MainnetID: MainnetHRP,
|
||||
TestnetID: TestnetHRP,
|
||||
UnitTestID: UnitTestHRP,
|
||||
LuxMainnetID: MainnetHRP,
|
||||
LuxTestnetID: TestnetHRP,
|
||||
QChainMainnetID: "qchain",
|
||||
QChainTestnetID: "qtest",
|
||||
}
|
||||
NetworkHRPToNetworkID = map[string]uint32{
|
||||
LocalHRP: LocalID,
|
||||
MainnetHRP: MainnetID,
|
||||
TestnetHRP: TestnetID,
|
||||
UnitTestHRP: UnitTestID,
|
||||
}
|
||||
ProductionNetworkIDs = set.Of(MainnetID, TestnetID)
|
||||
|
||||
ValidNetworkPrefix = "network-"
|
||||
|
||||
ErrParseNetworkName = errors.New("failed to parse network name")
|
||||
)
|
||||
|
||||
// GetHRP returns the Human-Readable-Part of bech32 addresses for a networkID
|
||||
func GetHRP(networkID uint32) string {
|
||||
if hrp, ok := NetworkIDToHRP[networkID]; ok {
|
||||
return hrp
|
||||
}
|
||||
return FallbackHRP
|
||||
}
|
||||
|
||||
// NetworkName returns a human readable name for the network with
|
||||
// ID [networkID]
|
||||
func NetworkName(networkID uint32) string {
|
||||
if name, exists := NetworkIDToNetworkName[networkID]; exists {
|
||||
return name
|
||||
}
|
||||
return fmt.Sprintf("network-%d", networkID)
|
||||
}
|
||||
|
||||
// NetworkID returns the ID of the network with name [networkName]
|
||||
func NetworkID(networkName string) (uint32, error) {
|
||||
networkName = strings.ToLower(networkName)
|
||||
if id, exists := NetworkNameToNetworkID[networkName]; exists {
|
||||
return id, nil
|
||||
}
|
||||
|
||||
idStr := networkName
|
||||
if strings.HasPrefix(networkName, ValidNetworkPrefix) {
|
||||
idStr = networkName[len(ValidNetworkPrefix):]
|
||||
}
|
||||
id, err := strconv.ParseUint(idStr, 10, 32)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("%w: %q", ErrParseNetworkName, networkName)
|
||||
}
|
||||
return uint32(id), nil
|
||||
}
|
||||
@@ -1,134 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package constants
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetHRP(t *testing.T) {
|
||||
tests := []struct {
|
||||
id uint32
|
||||
hrp string
|
||||
}{
|
||||
{
|
||||
id: MainnetID,
|
||||
hrp: MainnetHRP,
|
||||
},
|
||||
{
|
||||
id: TestnetID,
|
||||
hrp: TestnetHRP,
|
||||
},
|
||||
{
|
||||
id: TestnetID,
|
||||
hrp: TestnetHRP,
|
||||
},
|
||||
{
|
||||
id: LocalID,
|
||||
hrp: LocalHRP,
|
||||
},
|
||||
{
|
||||
id: 4294967295,
|
||||
hrp: FallbackHRP,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.hrp, func(t *testing.T) {
|
||||
require.Equal(t, test.hrp, GetHRP(test.id))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkName(t *testing.T) {
|
||||
tests := []struct {
|
||||
id uint32
|
||||
name string
|
||||
}{
|
||||
{
|
||||
id: MainnetID,
|
||||
name: MainnetName,
|
||||
},
|
||||
{
|
||||
id: TestnetID,
|
||||
name: TestnetName,
|
||||
},
|
||||
{
|
||||
id: TestnetID,
|
||||
name: TestnetName,
|
||||
},
|
||||
{
|
||||
id: LocalID,
|
||||
name: LocalName,
|
||||
},
|
||||
{
|
||||
id: 4294967295,
|
||||
name: "network-4294967295",
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require.Equal(t, test.name, NetworkName(test.id))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNetworkID(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
id uint32
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: MainnetName,
|
||||
id: MainnetID,
|
||||
},
|
||||
{
|
||||
name: "MaInNeT",
|
||||
id: MainnetID,
|
||||
},
|
||||
{
|
||||
name: TestnetName,
|
||||
id: TestnetID,
|
||||
},
|
||||
{
|
||||
name: TestnetName,
|
||||
id: TestnetID,
|
||||
},
|
||||
{
|
||||
name: LocalName,
|
||||
id: LocalID,
|
||||
},
|
||||
{
|
||||
name: "network-4294967295",
|
||||
id: 4294967295,
|
||||
},
|
||||
{
|
||||
name: "4294967295",
|
||||
id: 4294967295,
|
||||
},
|
||||
{
|
||||
name: "networ-4294967295",
|
||||
expectedErr: ErrParseNetworkName,
|
||||
},
|
||||
{
|
||||
name: "network-4294967295123123",
|
||||
expectedErr: ErrParseNetworkName,
|
||||
},
|
||||
{
|
||||
name: "4294967295123123",
|
||||
expectedErr: ErrParseNetworkName,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
id, err := NetworkID(test.name)
|
||||
require.ErrorIs(err, test.expectedErr)
|
||||
require.Equal(test.id, id)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package constants
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/luxfi/node/utils/compression"
|
||||
"github.com/luxfi/node/utils/units"
|
||||
)
|
||||
|
||||
const (
|
||||
// The network must be "tcp", "tcp4", "tcp6", "unix" or "unixpacket".
|
||||
NetworkType = "tcp"
|
||||
|
||||
DefaultMaxMessageSize = 2 * units.MiB
|
||||
DefaultPingPongTimeout = 30 * time.Second
|
||||
DefaultPingFrequency = 3 * DefaultPingPongTimeout / 4
|
||||
DefaultByteSliceCap = 128
|
||||
|
||||
MaxContainersLen = int(4 * DefaultMaxMessageSize / 5)
|
||||
|
||||
DefaultNetworkPeerListNumValidatorIPs = 15
|
||||
DefaultNetworkPeerListValidatorGossipSize = 20
|
||||
DefaultNetworkPeerListNonValidatorGossipSize = 0
|
||||
DefaultNetworkPeerListPeersGossipSize = 10
|
||||
DefaultNetworkPeerListGossipFreq = time.Minute
|
||||
DefaultNetworkPeerListPullGossipFreq = 2 * time.Second
|
||||
DefaultNetworkPeerListBloomResetFreq = time.Minute
|
||||
|
||||
// Inbound Connection Throttling
|
||||
DefaultInboundConnUpgradeThrottlerCooldown = 10 * time.Second
|
||||
DefaultInboundThrottlerMaxConnsPerSec = 256
|
||||
|
||||
// Outbound Connection Throttling
|
||||
DefaultOutboundConnectionThrottlingRps = 50
|
||||
DefaultOutboundConnectionTimeout = 30 * time.Second
|
||||
|
||||
// Timeouts
|
||||
DefaultNetworkInitialTimeout = 5 * time.Second
|
||||
DefaultNetworkMinimumTimeout = 2 * time.Second
|
||||
DefaultNetworkMaximumTimeout = 10 * time.Second
|
||||
DefaultNetworkMaximumInboundTimeout = 10 * time.Second
|
||||
DefaultNetworkTimeoutHalflife = 5 * time.Minute
|
||||
DefaultNetworkTimeoutCoefficient = 2
|
||||
DefaultNetworkReadHandshakeTimeout = 15 * time.Second
|
||||
DefaultNoIngressValidatorConnectionGracePeriod = 10 * time.Minute
|
||||
|
||||
DefaultNetworkCompressionType = compression.TypeZstd
|
||||
DefaultNetworkMaxClockDifference = time.Minute
|
||||
DefaultNetworkRequireValidatorToConnect = false
|
||||
DefaultNetworkPeerReadBufferSize = 8 * units.KiB
|
||||
DefaultNetworkPeerWriteBufferSize = 8 * units.KiB
|
||||
|
||||
DefaultNetworkTCPProxyEnabled = false
|
||||
|
||||
// The PROXY protocol specification recommends setting this value to be at
|
||||
// least 3 seconds to cover a TCP retransmit.
|
||||
// Ref: https://www.haproxy.org/download/2.3/doc/proxy-protocol.txt
|
||||
// Specifying a timeout of 0 will actually result in a timeout of 200ms, but
|
||||
// a timeout of 0 should generally not be provided.
|
||||
DefaultNetworkTCPProxyReadTimeout = 3 * time.Second
|
||||
|
||||
// Benchlist
|
||||
DefaultBenchlistFailThreshold = 10
|
||||
DefaultBenchlistDuration = 15 * time.Minute
|
||||
DefaultBenchlistMinFailingDuration = 2*time.Minute + 30*time.Second
|
||||
|
||||
// Router
|
||||
DefaultConsensusAppConcurrency = 2
|
||||
DefaultConsensusShutdownTimeout = time.Minute
|
||||
DefaultFrontierPollFrequency = 100 * time.Millisecond
|
||||
|
||||
// Inbound Throttling
|
||||
DefaultInboundThrottlerAtLargeAllocSize = 6 * units.MiB
|
||||
DefaultInboundThrottlerVdrAllocSize = 32 * units.MiB
|
||||
DefaultInboundThrottlerNodeMaxAtLargeBytes = DefaultMaxMessageSize
|
||||
DefaultInboundThrottlerMaxProcessingMsgsPerNode = 1024
|
||||
DefaultInboundThrottlerBandwidthRefillRate = 512 * units.KiB
|
||||
DefaultInboundThrottlerBandwidthMaxBurstSize = DefaultMaxMessageSize
|
||||
DefaultInboundThrottlerCPUMaxRecheckDelay = 5 * time.Second
|
||||
DefaultInboundThrottlerDiskMaxRecheckDelay = 5 * time.Second
|
||||
MinInboundThrottlerMaxRecheckDelay = time.Millisecond
|
||||
|
||||
// Outbound Throttling
|
||||
DefaultOutboundThrottlerAtLargeAllocSize = 32 * units.MiB
|
||||
DefaultOutboundThrottlerVdrAllocSize = 32 * units.MiB
|
||||
DefaultOutboundThrottlerNodeMaxAtLargeBytes = DefaultMaxMessageSize
|
||||
|
||||
// Network Health
|
||||
DefaultHealthCheckAveragerHalflife = 10 * time.Second
|
||||
|
||||
DefaultNetworkHealthMaxTimeSinceMsgSent = time.Minute
|
||||
DefaultNetworkHealthMaxTimeSinceMsgReceived = time.Minute
|
||||
DefaultNetworkHealthMaxPortionSendQueueFill = 0.9
|
||||
DefaultNetworkHealthMinPeers = 1
|
||||
DefaultNetworkHealthMaxSendFailRate = .9
|
||||
|
||||
// Metrics
|
||||
DefaultUptimeMetricFreq = 30 * time.Second
|
||||
|
||||
// Delays
|
||||
DefaultNetworkInitialReconnectDelay = time.Second
|
||||
DefaultNetworkMaxReconnectDelay = time.Minute
|
||||
)
|
||||
@@ -1,41 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package constants
|
||||
|
||||
import "github.com/luxfi/ids"
|
||||
|
||||
const (
|
||||
PlatformVMName = "platformvm"
|
||||
XVMName = "xvm"
|
||||
EVMName = "evm"
|
||||
XSVMName = "xsvm"
|
||||
QVMName = "qvm"
|
||||
)
|
||||
|
||||
var (
|
||||
PlatformVMID = ids.ID{'p', 'l', 'a', 't', 'f', 'o', 'r', 'm', 'v', 'm'}
|
||||
XVMID = ids.ID{'a', 'v', 'm'}
|
||||
EVMID = ids.ID{'e', 'v', 'm'}
|
||||
XSVMID = ids.ID{'x', 's', 'v', 'm'}
|
||||
QVMID = ids.ID{'q', 'v', 'm'}
|
||||
)
|
||||
|
||||
// VMName returns the name of the VM with the provided ID. If a human readable
|
||||
// name isn't known, then the formatted ID is returned.
|
||||
func VMName(vmID ids.ID) string {
|
||||
switch vmID {
|
||||
case PlatformVMID:
|
||||
return PlatformVMName
|
||||
case XVMID:
|
||||
return XVMName
|
||||
case EVMID:
|
||||
return EVMName
|
||||
case XSVMID:
|
||||
return XSVMName
|
||||
case QVMID:
|
||||
return QVMName
|
||||
default:
|
||||
return vmID.String()
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries Inc All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
type detachedContext struct {
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
func Detach(ctx context.Context) context.Context {
|
||||
return &detachedContext{
|
||||
ctx: ctx,
|
||||
}
|
||||
}
|
||||
|
||||
func (*detachedContext) Deadline() (time.Time, bool) {
|
||||
return time.Time{}, false
|
||||
}
|
||||
|
||||
func (*detachedContext) Done() <-chan struct{} {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*detachedContext) Err() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *detachedContext) Value(key any) any {
|
||||
return c.ctx.Value(key)
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bls
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
"github.com/luxfi/node/utils/crypto/bls/blstest"
|
||||
)
|
||||
|
||||
func BenchmarkVerify(b *testing.B) {
|
||||
privateKey := newKey(require.New(b))
|
||||
publicKey := publicKey(privateKey)
|
||||
|
||||
for _, messageSize := range blstest.BenchmarkSizes {
|
||||
b.Run(strconv.Itoa(messageSize), func(b *testing.B) {
|
||||
message := utils.RandomBytes(messageSize)
|
||||
signature := sign(privateKey, message)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
require.True(b, Verify(publicKey, signature, message))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkAggregatePublicKeys(b *testing.B) {
|
||||
keys := make([]*PublicKey, blstest.BiggestBenchmarkSize)
|
||||
|
||||
for i := range keys {
|
||||
privateKey := newKey(require.New(b))
|
||||
|
||||
keys[i] = publicKey(privateKey)
|
||||
}
|
||||
|
||||
for _, size := range blstest.BenchmarkSizes {
|
||||
b.Run(strconv.Itoa(size), func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := AggregatePublicKeys(keys[:size])
|
||||
require.NoError(b, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPublicKeyToCompressedBytes(b *testing.B) {
|
||||
sk := newKey(require.New(b))
|
||||
|
||||
pk := publicKey(sk)
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
PublicKeyToCompressedBytes(pk)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPublicKeyFromCompressedBytes(b *testing.B) {
|
||||
sk := newKey(require.New(b))
|
||||
|
||||
pk := publicKey(sk)
|
||||
pkBytes := PublicKeyToCompressedBytes(pk)
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
_, _ = PublicKeyFromCompressedBytes(pkBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPublicKeyToUncompressedBytes(b *testing.B) {
|
||||
sk := newKey(require.New(b))
|
||||
|
||||
pk := publicKey(sk)
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
PublicKeyToUncompressedBytes(pk)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkPublicKeyFromValidUncompressedBytes(b *testing.B) {
|
||||
sk := newKey(require.New(b))
|
||||
|
||||
pk := publicKey(sk)
|
||||
pkBytes := PublicKeyToUncompressedBytes(pk)
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
_ = PublicKeyFromValidUncompressedBytes(pkBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSignatureFromBytes(b *testing.B) {
|
||||
sk := newKey(require.New(b))
|
||||
|
||||
message := utils.RandomBytes(32)
|
||||
signature := sign(sk, message)
|
||||
signatureBytes := SignatureToBytes(signature)
|
||||
|
||||
b.ResetTimer()
|
||||
for range b.N {
|
||||
_, _ = SignatureFromBytes(signatureBytes)
|
||||
}
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build cgo
|
||||
// +build cgo
|
||||
|
||||
package bls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
luxbls "github.com/luxfi/crypto/bls"
|
||||
)
|
||||
|
||||
const PublicKeyLen = luxbls.PublicKeyLen
|
||||
|
||||
var (
|
||||
ErrNoPublicKeys = luxbls.ErrNoPublicKeys
|
||||
ErrFailedPublicKeyDecompress = luxbls.ErrFailedPublicKeyDecompress
|
||||
errInvalidPublicKey = errors.New("invalid public key")
|
||||
errFailedPublicKeyAggregation = errors.New("couldn't aggregate public keys")
|
||||
)
|
||||
|
||||
type PublicKey = luxbls.PublicKey
|
||||
|
||||
// PublicKeyToCompressedBytes returns the compressed big-endian format of the
|
||||
// public key.
|
||||
func PublicKeyToCompressedBytes(pk *PublicKey) []byte {
|
||||
return luxbls.PublicKeyToCompressedBytes(pk)
|
||||
}
|
||||
|
||||
// PublicKeyFromCompressedBytes parses the compressed big-endian format of the
|
||||
// public key into a public key.
|
||||
func PublicKeyFromCompressedBytes(pkBytes []byte) (*PublicKey, error) {
|
||||
return luxbls.PublicKeyFromCompressedBytes(pkBytes)
|
||||
}
|
||||
|
||||
// PublicKeyToUncompressedBytes returns the uncompressed big-endian format of
|
||||
// the public key.
|
||||
func PublicKeyToUncompressedBytes(key *PublicKey) []byte {
|
||||
return luxbls.PublicKeyToUncompressedBytes(key)
|
||||
}
|
||||
|
||||
// PublicKeyFromUncompressedBytes parses the uncompressed big-endian format of
|
||||
// the public key into a public key.
|
||||
func PublicKeyFromUncompressedBytes(pkBytes []byte) (*PublicKey, error) {
|
||||
// luxfi/crypto handles uncompressed format
|
||||
pk := luxbls.PublicKeyFromValidUncompressedBytes(pkBytes)
|
||||
if pk == nil {
|
||||
return nil, errInvalidPublicKey
|
||||
}
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromValidUncompressedBytes parses the uncompressed big-endian format of
|
||||
// the public key into a public key without performing validation.
|
||||
// This should only be used when the public key is known to be valid.
|
||||
func PublicKeyFromValidUncompressedBytes(pkBytes []byte) *PublicKey {
|
||||
return luxbls.PublicKeyFromValidUncompressedBytes(pkBytes)
|
||||
}
|
||||
|
||||
// AggregatePublicKeys aggregates a non-zero number of public keys into a
|
||||
// single aggregated public key.
|
||||
func AggregatePublicKeys(pks []*PublicKey) (*PublicKey, error) {
|
||||
if len(pks) == 0 {
|
||||
return nil, ErrNoPublicKeys
|
||||
}
|
||||
// luxfi/crypto should use optimized CGO implementation when available
|
||||
return luxbls.AggregatePublicKeys(pks)
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build !cgo
|
||||
// +build !cgo
|
||||
|
||||
package bls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
luxbls "github.com/luxfi/crypto/bls"
|
||||
)
|
||||
|
||||
const PublicKeyLen = luxbls.PublicKeyLen
|
||||
|
||||
var (
|
||||
ErrNoPublicKeys = luxbls.ErrNoPublicKeys
|
||||
ErrFailedPublicKeyDecompress = luxbls.ErrFailedPublicKeyDecompress
|
||||
errInvalidPublicKey = errors.New("invalid public key")
|
||||
errFailedPublicKeyAggregation = errors.New("couldn't aggregate public keys")
|
||||
)
|
||||
|
||||
type PublicKey = luxbls.PublicKey
|
||||
|
||||
// PublicKeyToCompressedBytes returns the compressed big-endian format of the
|
||||
// public key.
|
||||
func PublicKeyToCompressedBytes(pk *PublicKey) []byte {
|
||||
return luxbls.PublicKeyToCompressedBytes(pk)
|
||||
}
|
||||
|
||||
// PublicKeyFromCompressedBytes parses the compressed big-endian format of the
|
||||
// public key into a public key.
|
||||
func PublicKeyFromCompressedBytes(pkBytes []byte) (*PublicKey, error) {
|
||||
return luxbls.PublicKeyFromCompressedBytes(pkBytes)
|
||||
}
|
||||
|
||||
// PublicKeyToUncompressedBytes returns the uncompressed big-endian format of
|
||||
// the public key.
|
||||
func PublicKeyToUncompressedBytes(key *PublicKey) []byte {
|
||||
return luxbls.PublicKeyToUncompressedBytes(key)
|
||||
}
|
||||
|
||||
// PublicKeyFromUncompressedBytes parses the uncompressed big-endian format of
|
||||
// the public key into a public key.
|
||||
func PublicKeyFromUncompressedBytes(pkBytes []byte) (*PublicKey, error) {
|
||||
// luxfi/crypto handles uncompressed format
|
||||
pk := luxbls.PublicKeyFromValidUncompressedBytes(pkBytes)
|
||||
if pk == nil {
|
||||
return nil, errInvalidPublicKey
|
||||
}
|
||||
return pk, nil
|
||||
}
|
||||
|
||||
// PublicKeyFromValidUncompressedBytes parses the uncompressed big-endian format of
|
||||
// the public key into a public key without performing validation.
|
||||
// This should only be used when the public key is known to be valid.
|
||||
func PublicKeyFromValidUncompressedBytes(pkBytes []byte) *PublicKey {
|
||||
return luxbls.PublicKeyFromValidUncompressedBytes(pkBytes)
|
||||
}
|
||||
|
||||
// AggregatePublicKeys aggregates a non-zero number of public keys into a
|
||||
// single aggregated public key.
|
||||
func AggregatePublicKeys(pks []*PublicKey) (*PublicKey, error) {
|
||||
if len(pks) == 0 {
|
||||
return nil, ErrNoPublicKeys
|
||||
}
|
||||
return luxbls.AggregatePublicKeys(pks)
|
||||
}
|
||||
@@ -1,87 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bls
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
)
|
||||
|
||||
func newKey(require *require.Assertions) *SecretKey {
|
||||
sk, err := NewSecretKey()
|
||||
require.NoError(err)
|
||||
return sk
|
||||
}
|
||||
|
||||
func publicKey(sk *SecretKey) *PublicKey {
|
||||
return sk.PublicKey()
|
||||
}
|
||||
|
||||
func sign(sk *SecretKey, msg []byte) *Signature {
|
||||
sig, err := sk.Sign(msg)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return sig
|
||||
}
|
||||
|
||||
func TestAggregationThreshold(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
// People in the network would privately generate their secret keys
|
||||
sk0 := newKey(require)
|
||||
sk1 := newKey(require)
|
||||
sk2 := newKey(require)
|
||||
|
||||
// All the public keys would be registered on chain
|
||||
pks := []*PublicKey{
|
||||
publicKey(sk0),
|
||||
publicKey(sk1),
|
||||
publicKey(sk2),
|
||||
}
|
||||
|
||||
// The transaction's unsigned bytes are publicly known.
|
||||
msg := utils.RandomBytes(1234)
|
||||
|
||||
// People may attempt time sign the transaction.
|
||||
sigs := []*Signature{
|
||||
sign(sk0, msg),
|
||||
sign(sk1, msg),
|
||||
sign(sk2, msg),
|
||||
}
|
||||
|
||||
// The signed transaction would specify which of the public keys have been
|
||||
// used to sign it. The aggregator should verify each individual signature,
|
||||
// until it has found a sufficient threshold of valid signatures.
|
||||
var (
|
||||
indices = []int{0, 2}
|
||||
filteredPKs = make([]*PublicKey, len(indices))
|
||||
filteredSigs = make([]*Signature, len(indices))
|
||||
)
|
||||
for i, index := range indices {
|
||||
pk := pks[index]
|
||||
filteredPKs[i] = pk
|
||||
sig := sigs[index]
|
||||
filteredSigs[i] = sig
|
||||
|
||||
valid := Verify(pk, sig, msg)
|
||||
require.True(valid)
|
||||
}
|
||||
|
||||
// Once the aggregator has the required threshold of signatures, it can
|
||||
// aggregate the signatures.
|
||||
aggregatedSig, err := AggregateSignatures(filteredSigs)
|
||||
require.NoError(err)
|
||||
|
||||
// For anyone looking for a proof of the aggregated signature's correctness,
|
||||
// they can aggregate the public keys and verify the aggregated signature.
|
||||
aggregatedPK, err := AggregatePublicKeys(filteredPKs)
|
||||
require.NoError(err)
|
||||
|
||||
valid := Verify(aggregatedPK, aggregatedSig, msg)
|
||||
require.True(valid)
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package blstest
|
||||
|
||||
var (
|
||||
BenchmarkSizes = []int{
|
||||
1,
|
||||
2,
|
||||
4,
|
||||
8,
|
||||
16,
|
||||
32,
|
||||
64,
|
||||
128,
|
||||
256,
|
||||
512,
|
||||
1024,
|
||||
2048,
|
||||
4096,
|
||||
8192,
|
||||
16384,
|
||||
32768,
|
||||
}
|
||||
BiggestBenchmarkSize = BenchmarkSizes[len(BenchmarkSizes)-1]
|
||||
)
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bls
|
||||
|
||||
type Ciphersuite int
|
||||
|
||||
const (
|
||||
CiphersuiteSignature Ciphersuite = iota
|
||||
CiphersuiteProofOfPossession
|
||||
)
|
||||
|
||||
var ciphersuiteStrings = [...]string{
|
||||
"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_",
|
||||
"BLS_POP_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_",
|
||||
}
|
||||
|
||||
var ciphersuiteBytes = [...][]byte{
|
||||
[]byte(ciphersuiteStrings[0]),
|
||||
[]byte(ciphersuiteStrings[1]),
|
||||
}
|
||||
|
||||
func (c Ciphersuite) String() string {
|
||||
return ciphersuiteStrings[c]
|
||||
}
|
||||
|
||||
func (c Ciphersuite) Bytes() []byte {
|
||||
return ciphersuiteBytes[c]
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bls
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
)
|
||||
|
||||
func TestPublicKeyFromCompressedBytesWrongSize(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
pkBytes := utils.RandomBytes(PublicKeyLen + 1)
|
||||
_, err := PublicKeyFromCompressedBytes(pkBytes)
|
||||
require.ErrorIs(err, ErrFailedPublicKeyDecompress)
|
||||
}
|
||||
|
||||
func TestPublicKeyBytes(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
pkBytes, err := base64.StdEncoding.DecodeString("h5qt9SPxaCo+vOx6sn+QkkpP7Y40Yja7SEAs2MGb/mZT7oKTWgLogjy5c4/wWIGC")
|
||||
require.NoError(err)
|
||||
|
||||
pk, err := PublicKeyFromCompressedBytes(pkBytes)
|
||||
require.NoError(err)
|
||||
|
||||
pk2Bytes := PublicKeyToCompressedBytes(pk)
|
||||
|
||||
require.Equal(pkBytes, pk2Bytes)
|
||||
}
|
||||
|
||||
func TestAggregatePublicKeysNoop(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
pkBytes, err := base64.StdEncoding.DecodeString("h5qt9SPxaCo+vOx6sn+QkkpP7Y40Yja7SEAs2MGb/mZT7oKTWgLogjy5c4/wWIGC")
|
||||
require.NoError(err)
|
||||
|
||||
pk, err := PublicKeyFromCompressedBytes(pkBytes)
|
||||
require.NoError(err)
|
||||
|
||||
aggPK, err := AggregatePublicKeys([]*PublicKey{pk})
|
||||
require.NoError(err)
|
||||
|
||||
aggPKBytes := PublicKeyToCompressedBytes(aggPK)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(pk, aggPK)
|
||||
require.Equal(pkBytes, aggPKBytes)
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build cgo
|
||||
// +build cgo
|
||||
|
||||
package bls
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
luxbls "github.com/luxfi/crypto/bls"
|
||||
)
|
||||
|
||||
const SignatureLen = luxbls.SignatureLen
|
||||
|
||||
var (
|
||||
ErrNoSignatures = luxbls.ErrNoSignatures
|
||||
ErrFailedSignatureDecompress = luxbls.ErrFailedSignatureDecompress
|
||||
errInvalidSignature = errors.New("invalid signature")
|
||||
errFailedSignatureAggregation = errors.New("couldn't aggregate signatures")
|
||||
errInvalidSignatureLen = errors.New("invalid signature length")
|
||||
)
|
||||
|
||||
type (
|
||||
SecretKey = luxbls.SecretKey
|
||||
Signature = luxbls.Signature
|
||||
Signer = luxbls.Signer
|
||||
)
|
||||
|
||||
// NewSecretKey generates a new secret key
|
||||
func NewSecretKey() (*SecretKey, error) {
|
||||
return luxbls.NewSecretKey()
|
||||
}
|
||||
|
||||
// SecretKeyFromBytes parses a secret key from bytes
|
||||
func SecretKeyFromBytes(skBytes []byte) (*SecretKey, error) {
|
||||
return luxbls.SecretKeyFromBytes(skBytes)
|
||||
}
|
||||
|
||||
// SignatureToBytes returns the compressed big-endian format of the signature.
|
||||
func SignatureToBytes(sig *Signature) []byte {
|
||||
return luxbls.SignatureToBytes(sig)
|
||||
}
|
||||
|
||||
// SignatureFromBytes parses the compressed big-endian format of the signature into a signature.
|
||||
func SignatureFromBytes(sigBytes []byte) (*Signature, error) {
|
||||
return luxbls.SignatureFromBytes(sigBytes)
|
||||
}
|
||||
|
||||
// SecretKeyToBytes returns the big-endian format of the secret key.
|
||||
func SecretKeyToBytes(sk *SecretKey) []byte {
|
||||
return luxbls.SecretKeyToBytes(sk)
|
||||
}
|
||||
|
||||
// Verify verifies that the signature was signed from the given public key on the given message.
|
||||
func Verify(pk *PublicKey, sig *Signature, msg []byte) bool {
|
||||
if pk == nil || sig == nil {
|
||||
return false
|
||||
}
|
||||
return luxbls.Verify(pk, sig, msg)
|
||||
}
|
||||
|
||||
// VerifyProofOfPossession verifies that the signature was signed from the
|
||||
// given public key on the given message using the proof of possession domain
|
||||
// separation tag.
|
||||
func VerifyProofOfPossession(pk *PublicKey, sig *Signature, msg []byte) bool {
|
||||
if pk == nil || sig == nil {
|
||||
return false
|
||||
}
|
||||
// luxfi/crypto should handle PoP verification with CGO optimization
|
||||
return luxbls.Verify(pk, sig, msg)
|
||||
}
|
||||
|
||||
// AggregateSignatures aggregates a non-zero number of signatures into a single aggregated signature.
|
||||
func AggregateSignatures(sigs []*Signature) (*Signature, error) {
|
||||
if len(sigs) == 0 {
|
||||
return nil, ErrNoSignatures
|
||||
}
|
||||
// luxfi/crypto should use optimized CGO implementation when available
|
||||
return luxbls.AggregateSignatures(sigs)
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build !cgo
|
||||
// +build !cgo
|
||||
|
||||
package bls
|
||||
|
||||
import (
|
||||
luxbls "github.com/luxfi/crypto/bls"
|
||||
)
|
||||
|
||||
const SignatureLen = luxbls.SignatureLen
|
||||
|
||||
var (
|
||||
ErrFailedSignatureDecompress = luxbls.ErrFailedSignatureDecompress
|
||||
ErrInvalidSignature = luxbls.ErrInvalidSignature
|
||||
ErrNoSignatures = luxbls.ErrNoSignatures
|
||||
ErrFailedSignatureAggregation = luxbls.ErrFailedSignatureAggregation
|
||||
)
|
||||
|
||||
type (
|
||||
SecretKey = luxbls.SecretKey
|
||||
Signature = luxbls.Signature
|
||||
Signer = luxbls.Signer
|
||||
)
|
||||
|
||||
// NewSecretKey generates a new secret key
|
||||
func NewSecretKey() (*SecretKey, error) {
|
||||
return luxbls.NewSecretKey()
|
||||
}
|
||||
|
||||
// SecretKeyFromBytes parses a secret key from bytes
|
||||
func SecretKeyFromBytes(skBytes []byte) (*SecretKey, error) {
|
||||
return luxbls.SecretKeyFromBytes(skBytes)
|
||||
}
|
||||
|
||||
// SecretKeyToBytes returns the big-endian format of the secret key.
|
||||
func SecretKeyToBytes(sk *SecretKey) []byte {
|
||||
return luxbls.SecretKeyToBytes(sk)
|
||||
}
|
||||
|
||||
// SignatureToBytes returns the compressed big-endian format of the signature.
|
||||
func SignatureToBytes(sig *Signature) []byte {
|
||||
return luxbls.SignatureToBytes(sig)
|
||||
}
|
||||
|
||||
// SignatureFromBytes parses the compressed big-endian format of the signature
|
||||
// into a signature.
|
||||
func SignatureFromBytes(sigBytes []byte) (*Signature, error) {
|
||||
return luxbls.SignatureFromBytes(sigBytes)
|
||||
}
|
||||
|
||||
// Verify the signature against the provided message and public key.
|
||||
func Verify(pk *PublicKey, sig *Signature, msg []byte) bool {
|
||||
return luxbls.Verify(pk, sig, msg)
|
||||
}
|
||||
|
||||
// VerifyProofOfPossession verifies that signature is a valid proof of
|
||||
// possession of the provided public key.
|
||||
func VerifyProofOfPossession(pk *PublicKey, sig *Signature, msg []byte) bool {
|
||||
if pk == nil || sig == nil {
|
||||
return false
|
||||
}
|
||||
// Pure Go implementation should verify with PoP domain
|
||||
return luxbls.Verify(pk, sig, msg)
|
||||
}
|
||||
|
||||
// AggregateSignatures aggregates a non-zero number of signatures into a single
|
||||
// aggregated signature.
|
||||
func AggregateSignatures(sigs []*Signature) (*Signature, error) {
|
||||
if len(sigs) == 0 {
|
||||
return nil, ErrNoSignatures
|
||||
}
|
||||
return luxbls.AggregateSignatures(sigs)
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package bls
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
)
|
||||
|
||||
func TestSignatureBytes(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
msg := utils.RandomBytes(1234)
|
||||
|
||||
sk := newKey(require)
|
||||
sig := sign(sk, msg)
|
||||
sigBytes := SignatureToBytes(sig)
|
||||
|
||||
sig2, err := SignatureFromBytes(sigBytes)
|
||||
require.NoError(err)
|
||||
sig2Bytes := SignatureToBytes(sig2)
|
||||
|
||||
require.Equal(sig, sig2)
|
||||
require.Equal(sigBytes, sig2Bytes)
|
||||
}
|
||||
|
||||
func TestAggregateSignaturesNoop(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
msg := utils.RandomBytes(1234)
|
||||
|
||||
sk := newKey(require)
|
||||
sig := sign(sk, msg)
|
||||
sigBytes := SignatureToBytes(sig)
|
||||
|
||||
aggSig, err := AggregateSignatures([]*Signature{sig})
|
||||
require.NoError(err)
|
||||
|
||||
aggSigBytes := SignatureToBytes(aggSig)
|
||||
require.NoError(err)
|
||||
|
||||
require.Equal(sig, aggSig)
|
||||
require.Equal(sigBytes, aggSigBytes)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package localsigner
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
"github.com/luxfi/node/utils/crypto/bls/blstest"
|
||||
)
|
||||
|
||||
func BenchmarkSign(b *testing.B) {
|
||||
signer := NewSigner(require.New(b))
|
||||
for _, messageSize := range blstest.BenchmarkSizes {
|
||||
b.Run(strconv.Itoa(messageSize), func(b *testing.B) {
|
||||
message := utils.RandomBytes(messageSize)
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, _ = signer.Sign(message)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,314 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package localsigner
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/node/utils/crypto/bls"
|
||||
)
|
||||
|
||||
func AggregateAndVerify(publicKeys []*bls.PublicKey, signatures []*bls.Signature, message []byte) (bool, error) {
|
||||
aggSig, err := bls.AggregateSignatures(signatures)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
aggPK, err := bls.AggregatePublicKeys(publicKeys)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return bls.Verify(aggPK, aggSig, message), nil
|
||||
}
|
||||
|
||||
func NewSigner(require *require.Assertions) *LocalSigner {
|
||||
sk, err := New()
|
||||
require.NoError(err)
|
||||
return sk
|
||||
}
|
||||
|
||||
func mapSlice[T any, U any](arr []T, f func(T) U) []U {
|
||||
result := make([]U, len(arr))
|
||||
for i, val := range arr {
|
||||
result[i] = f(val)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func TestVerifyValidSignature(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signer := NewSigner(require)
|
||||
|
||||
msg := []byte("TestVerifyValidSignature local signer")
|
||||
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid := bls.Verify(signer.PublicKey(), sig, msg)
|
||||
require.True(isValid)
|
||||
}
|
||||
|
||||
func TestVerifyWrongMessageSignature(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signer := NewSigner(require)
|
||||
|
||||
msg := []byte("TestVerifyWrongMessageSignature local signer")
|
||||
wrongMsg := []byte("TestVerifyWrongMessageSignature local signer with wrong message")
|
||||
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid := bls.Verify(signer.PublicKey(), sig, wrongMsg)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestVerifyWrongPubkeySignature(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signer := NewSigner(require)
|
||||
wrongPk := NewSigner(require).pk
|
||||
|
||||
msg := []byte("TestVerifyWrongPubkeySignature local signer")
|
||||
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid := bls.Verify(wrongPk, sig, msg)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestVerifyWrongMessageSignedSignature(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signer := NewSigner(require)
|
||||
|
||||
msg := []byte("TestVerifyWrongMessageSignedSignature local signer")
|
||||
wrongMsg := []byte("TestVerifyWrongMessageSignedSignaturelocal local signer with wrong signature")
|
||||
|
||||
wrongSig, err := signer.Sign(wrongMsg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid := bls.Verify(signer.PublicKey(), wrongSig, msg)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestValidAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signers := []*LocalSigner{NewSigner(require), NewSigner(require), NewSigner(require)}
|
||||
pks := mapSlice(signers, (*LocalSigner).PublicKey)
|
||||
|
||||
msg := []byte("TestValidAggregation local signer")
|
||||
|
||||
sigs := mapSlice(signers, func(signer *LocalSigner) *bls.Signature {
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
return sig
|
||||
})
|
||||
|
||||
isValid, err := AggregateAndVerify(pks, sigs, msg)
|
||||
require.NoError(err)
|
||||
require.True(isValid)
|
||||
}
|
||||
|
||||
func TestSingleKeyAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signer := NewSigner(require)
|
||||
|
||||
pks := []*bls.PublicKey{signer.PublicKey()}
|
||||
|
||||
msg := []byte("TestSingleKeyAggregation local signer")
|
||||
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid, err := AggregateAndVerify(pks, []*bls.Signature{sig}, msg)
|
||||
require.NoError(err)
|
||||
require.True(isValid)
|
||||
}
|
||||
|
||||
func TestIncorrectMessageAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signers := []*LocalSigner{NewSigner(require), NewSigner(require), NewSigner(require)}
|
||||
pks := mapSlice(signers, (*LocalSigner).PublicKey)
|
||||
|
||||
msg := []byte("TestIncorrectMessageAggregation local signer")
|
||||
|
||||
signatures := mapSlice(signers, func(signer *LocalSigner) *bls.Signature {
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
return sig
|
||||
})
|
||||
|
||||
isValid, err := AggregateAndVerify(pks, signatures, []byte("a different message"))
|
||||
require.NoError(err)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestDifferentMessageAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signers := []*LocalSigner{NewSigner(require), NewSigner(require), NewSigner(require)}
|
||||
pks := mapSlice(signers, (*LocalSigner).PublicKey)
|
||||
|
||||
msg := []byte("TestDifferentMessagesAggregation local signer")
|
||||
differentMsg := []byte("TestDifferentMessagesAggregation local signer with different message")
|
||||
|
||||
diffSig, err := signers[0].Sign(differentMsg)
|
||||
require.NoError(err)
|
||||
|
||||
sameSigs := mapSlice(signers[1:], func(signer *LocalSigner) *bls.Signature {
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
return sig
|
||||
})
|
||||
|
||||
signatures := append([]*bls.Signature{diffSig}, sameSigs...)
|
||||
|
||||
isValid, err := AggregateAndVerify(pks, signatures, msg)
|
||||
require.NoError(err)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestOneIncorrectPubKeyAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signers := []*LocalSigner{NewSigner(require), NewSigner(require), NewSigner(require)}
|
||||
pks := mapSlice(signers, (*LocalSigner).PublicKey)
|
||||
|
||||
msg := []byte("TestOneIncorrectPubKeyAggregation local signer")
|
||||
|
||||
signatures := mapSlice(signers, func(signer *LocalSigner) *bls.Signature {
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
return sig
|
||||
})
|
||||
|
||||
// incorrect pubkey
|
||||
pks[0] = NewSigner(require).PublicKey()
|
||||
|
||||
isValid, err := AggregateAndVerify(pks, signatures, msg)
|
||||
require.NoError(err)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestMorePubkeysThanSignaturesAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signers := []*LocalSigner{NewSigner(require), NewSigner(require), NewSigner(require)}
|
||||
pks := mapSlice(signers, (*LocalSigner).PublicKey)
|
||||
|
||||
msg := []byte("TestMorePubkeysThanSignatures local signer")
|
||||
|
||||
signatures := mapSlice(signers, func(signer *LocalSigner) *bls.Signature {
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
return sig
|
||||
})
|
||||
|
||||
// add an extra pubkey
|
||||
pks = append(pks, NewSigner(require).PublicKey())
|
||||
|
||||
isValid, err := AggregateAndVerify(pks, signatures, msg)
|
||||
require.NoError(err)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestMoreSignaturesThanPubkeysAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signers := []*LocalSigner{NewSigner(require), NewSigner(require), NewSigner(require)}
|
||||
pks := mapSlice(signers, (*LocalSigner).PublicKey)
|
||||
|
||||
msg := []byte("TestMoreSignaturesThanPubkeys local signer")
|
||||
|
||||
signatures := mapSlice(signers, func(signer *LocalSigner) *bls.Signature {
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
return sig
|
||||
})
|
||||
|
||||
isValid, err := AggregateAndVerify(pks[1:], signatures, msg)
|
||||
require.NoError(err)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestNoPubkeysAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signers := []*LocalSigner{NewSigner(require), NewSigner(require), NewSigner(require)}
|
||||
|
||||
msg := []byte("TestNoPubkeysAggregation local signer")
|
||||
|
||||
signatures := mapSlice(signers, func(signer *LocalSigner) *bls.Signature {
|
||||
sig, err := signer.Sign(msg)
|
||||
require.NoError(err)
|
||||
return sig
|
||||
})
|
||||
|
||||
isValid, err := AggregateAndVerify(nil, signatures, msg)
|
||||
require.ErrorIs(err, bls.ErrNoPublicKeys)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestNoSignaturesAggregation(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signers := []*LocalSigner{NewSigner(require), NewSigner(require), NewSigner(require)}
|
||||
pks := mapSlice(signers, (*LocalSigner).PublicKey)
|
||||
|
||||
msg := []byte("TestNoSignaturesAggregation local signer")
|
||||
|
||||
isValid, err := AggregateAndVerify(pks, nil, msg)
|
||||
require.ErrorIs(err, bls.ErrNoSignatures)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestVerifyValidProofOfPossession(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signer := NewSigner(require)
|
||||
|
||||
msg := []byte("TestVerifyValidProofOfPossession local signer")
|
||||
|
||||
sig, err := signer.SignProofOfPossession(msg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid := bls.VerifyProofOfPossession(signer.PublicKey(), sig, msg)
|
||||
require.True(isValid)
|
||||
}
|
||||
|
||||
func TestVerifyWrongMessageProofOfPossession(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signer := NewSigner(require)
|
||||
|
||||
msg := []byte("TestVerifyWrongMessageProofOfPossession local signer")
|
||||
wrongMsg := []byte("TestVerifyWrongMessageProofOfPossession local signer with wrong message")
|
||||
|
||||
sig, err := signer.SignProofOfPossession(msg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid := bls.VerifyProofOfPossession(signer.PublicKey(), sig, wrongMsg)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestVerifyWrongPubkeyProofOfPossession(t *testing.T) {
|
||||
require := require.New(t)
|
||||
correctSigner := NewSigner(require)
|
||||
wrongSigner := NewSigner(require)
|
||||
|
||||
msg := []byte("TestVerifyWrongPubkeyProofOfPossession local signer")
|
||||
|
||||
sig, err := correctSigner.SignProofOfPossession(msg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid := bls.VerifyProofOfPossession(wrongSigner.PublicKey(), sig, msg)
|
||||
require.False(isValid)
|
||||
}
|
||||
|
||||
func TestVerifyWrongMessageSignedProofOfPossession(t *testing.T) {
|
||||
require := require.New(t)
|
||||
signer := NewSigner(require)
|
||||
|
||||
msg := []byte("TestVerifyWrongMessageSignedProofOfPossession local signer")
|
||||
wrongMsg := []byte("TestVerifyWrongMessageSignedProofOfPossession local signer with wrong signature")
|
||||
|
||||
wrongSig, err := signer.SignProofOfPossession(wrongMsg)
|
||||
require.NoError(err)
|
||||
|
||||
isValid := bls.VerifyProofOfPossession(signer.PublicKey(), wrongSig, msg)
|
||||
require.False(isValid)
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build cgo
|
||||
// +build cgo
|
||||
|
||||
package localsigner
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/luxfi/node/utils/crypto/bls"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedSecretKeyDeserialize = errors.New("couldn't deserialize secret key")
|
||||
_ bls.Signer = (*LocalSigner)(nil)
|
||||
)
|
||||
|
||||
type LocalSigner struct {
|
||||
sk *bls.SecretKey
|
||||
pk *bls.PublicKey
|
||||
}
|
||||
|
||||
// NewSecretKey generates a new secret key from the local source of
|
||||
// cryptographically secure randomness.
|
||||
func New() (*LocalSigner, error) {
|
||||
sk, err := bls.NewSecretKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pk := sk.PublicKey()
|
||||
|
||||
return &LocalSigner{sk: sk, pk: pk}, nil
|
||||
}
|
||||
|
||||
// ToBytes returns the big-endian format of the secret key.
|
||||
func (s *LocalSigner) ToBytes() []byte {
|
||||
return bls.SecretKeyToBytes(s.sk)
|
||||
}
|
||||
|
||||
// FromBytes parses the big-endian format of the secret key into a
|
||||
// secret key.
|
||||
func FromBytes(skBytes []byte) (*LocalSigner, error) {
|
||||
sk, err := bls.SecretKeyFromBytes(skBytes)
|
||||
if err != nil {
|
||||
return nil, ErrFailedSecretKeyDeserialize
|
||||
}
|
||||
pk := sk.PublicKey()
|
||||
|
||||
return &LocalSigner{sk: sk, pk: pk}, nil
|
||||
}
|
||||
|
||||
// PublicKey returns the public key that corresponds to this secret
|
||||
// key.
|
||||
func (s *LocalSigner) PublicKey() *bls.PublicKey {
|
||||
return s.pk
|
||||
}
|
||||
|
||||
// Sign [msg] to authorize this message
|
||||
func (s *LocalSigner) Sign(msg []byte) (*bls.Signature, error) {
|
||||
return s.sk.Sign(msg)
|
||||
}
|
||||
|
||||
// Sign [msg] to prove the ownership
|
||||
func (s *LocalSigner) SignProofOfPossession(msg []byte) (*bls.Signature, error) {
|
||||
return s.sk.SignProofOfPossession(msg)
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
//go:build !cgo
|
||||
// +build !cgo
|
||||
|
||||
package localsigner
|
||||
|
||||
import (
|
||||
luxsigner "github.com/luxfi/crypto/bls/signer/localsigner"
|
||||
|
||||
"github.com/luxfi/node/utils/crypto/bls"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrFailedSecretKeyDeserialize = luxsigner.ErrFailedSecretKeyDeserialize
|
||||
_ bls.Signer = (*LocalSigner)(nil)
|
||||
)
|
||||
|
||||
// LocalSigner wraps the Lux crypto localsigner
|
||||
type LocalSigner struct {
|
||||
signer luxsigner.LocalSigner
|
||||
}
|
||||
|
||||
// New generates a new signer with a random secret key
|
||||
func New() (*LocalSigner, error) {
|
||||
signer, err := luxsigner.New()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LocalSigner{signer: *signer}, nil
|
||||
}
|
||||
|
||||
// FromBytes parses the big-endian format of the secret key into a LocalSigner
|
||||
func FromBytes(skBytes []byte) (*LocalSigner, error) {
|
||||
signer, err := luxsigner.FromBytes(skBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LocalSigner{signer: *signer}, nil
|
||||
}
|
||||
|
||||
// ToBytes returns the big-endian format of the secret key
|
||||
func (s *LocalSigner) ToBytes() []byte {
|
||||
return s.signer.ToBytes()
|
||||
}
|
||||
|
||||
// PublicKey returns the public key associated with this signer
|
||||
func (s *LocalSigner) PublicKey() *bls.PublicKey {
|
||||
pk := s.signer.PublicKey()
|
||||
return (*bls.PublicKey)(pk)
|
||||
}
|
||||
|
||||
// Sign creates a signature from the provided message
|
||||
func (s *LocalSigner) Sign(msg []byte) (*bls.Signature, error) {
|
||||
sig, err := s.signer.Sign(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*bls.Signature)(sig), nil
|
||||
}
|
||||
|
||||
// SignProofOfPossession creates a proof of possession signature
|
||||
func (s *LocalSigner) SignProofOfPossession(msg []byte) (*bls.Signature, error) {
|
||||
sig, err := s.signer.SignProofOfPossession(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return (*bls.Signature)(sig), nil
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package localsigner
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/node/utils"
|
||||
|
||||
blst "github.com/supranational/blst/bindings/go"
|
||||
)
|
||||
|
||||
const SecretKeyLen = blst.BLST_SCALAR_BYTES
|
||||
|
||||
func TestSecretKeyFromBytesZero(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
var skArr [SecretKeyLen]byte
|
||||
skBytes := skArr[:]
|
||||
_, err := FromBytes(skBytes)
|
||||
require.ErrorIs(err, ErrFailedSecretKeyDeserialize)
|
||||
}
|
||||
|
||||
func TestSecretKeyFromBytesWrongSize(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
skBytes := utils.RandomBytes(SecretKeyLen + 1)
|
||||
_, err := FromBytes(skBytes)
|
||||
require.ErrorIs(err, ErrFailedSecretKeyDeserialize)
|
||||
}
|
||||
|
||||
func TestSecretKeyBytes(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
msg := utils.RandomBytes(1234)
|
||||
|
||||
sk, err := New()
|
||||
require.NoError(err)
|
||||
sig, err := sk.Sign(msg)
|
||||
require.NoError(err)
|
||||
skBytes := sk.ToBytes()
|
||||
|
||||
sk2, err := FromBytes(skBytes)
|
||||
require.NoError(err)
|
||||
sig2, err := sk2.Sign(msg)
|
||||
require.NoError(err)
|
||||
sk2Bytes := sk2.ToBytes()
|
||||
|
||||
require.Equal(sk, sk2)
|
||||
require.Equal(skBytes, sk2Bytes)
|
||||
require.Equal(sig, sig2)
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package rpcsigner
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/luxfi/node/utils/crypto/bls"
|
||||
|
||||
pb "github.com/luxfi/node/proto/pb/signer"
|
||||
)
|
||||
|
||||
var _ bls.Signer = (*Client)(nil)
|
||||
|
||||
type Client struct {
|
||||
client pb.SignerClient
|
||||
pk *bls.PublicKey
|
||||
}
|
||||
|
||||
func NewClient(ctx context.Context, conn *grpc.ClientConn) (*Client, error) {
|
||||
client := pb.NewSignerClient(conn)
|
||||
|
||||
pubkeyResponse, err := client.PublicKey(ctx, &pb.PublicKeyRequest{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pkBytes := pubkeyResponse.GetPublicKey()
|
||||
pk, err := bls.PublicKeyFromCompressedBytes(pkBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &Client{
|
||||
client: client,
|
||||
pk: pk,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) PublicKey() *bls.PublicKey {
|
||||
return c.pk
|
||||
}
|
||||
|
||||
func (c *Client) Sign(message []byte) (*bls.Signature, error) {
|
||||
resp, err := c.client.Sign(context.TODO(), &pb.SignRequest{Message: message})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signature := resp.GetSignature()
|
||||
|
||||
return bls.SignatureFromBytes(signature)
|
||||
}
|
||||
|
||||
func (c *Client) SignProofOfPossession(message []byte) (*bls.Signature, error) {
|
||||
resp, err := c.client.SignProofOfPossession(context.TODO(), &pb.SignProofOfPossessionRequest{Message: message})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signature := resp.GetSignature()
|
||||
|
||||
return bls.SignatureFromBytes(signature)
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package rpcsigner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/luxfi/node/proto/pb/signer"
|
||||
"github.com/luxfi/node/utils/crypto/bls"
|
||||
"github.com/luxfi/node/utils/crypto/bls/signer/localsigner"
|
||||
)
|
||||
|
||||
var (
|
||||
validSignatureMsg = []byte("valid")
|
||||
uncompressedSignatureMsg = []byte("uncompressed")
|
||||
emptySignatureMsg = []byte("empty")
|
||||
noSignatureMsg = []byte("none")
|
||||
)
|
||||
|
||||
func newSigner(t *testing.T) *Client {
|
||||
localSigner, err := localsigner.New()
|
||||
require.NoError(t, err)
|
||||
|
||||
return &Client{
|
||||
client: &stubClient{
|
||||
signer: localSigner,
|
||||
},
|
||||
pk: localSigner.PublicKey(),
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidSignature(t *testing.T) {
|
||||
client := newSigner(t)
|
||||
sig, err := client.Sign(validSignatureMsg)
|
||||
require.NoError(t, err)
|
||||
ok := bls.Verify(client.PublicKey(), sig, validSignatureMsg)
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
type test struct {
|
||||
name string
|
||||
msg []byte
|
||||
err error
|
||||
}
|
||||
|
||||
var tests = []test{
|
||||
{
|
||||
name: "uncompressed",
|
||||
msg: uncompressedSignatureMsg,
|
||||
err: bls.ErrFailedSignatureDecompress,
|
||||
},
|
||||
{
|
||||
name: "empty",
|
||||
msg: emptySignatureMsg,
|
||||
err: bls.ErrFailedSignatureDecompress,
|
||||
},
|
||||
{
|
||||
name: "none",
|
||||
msg: noSignatureMsg,
|
||||
err: bls.ErrFailedSignatureDecompress,
|
||||
},
|
||||
}
|
||||
|
||||
func TestInvalidSignature(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
client := newSigner(t)
|
||||
sig, err := client.Sign(test.msg)
|
||||
require.Nil(t, sig)
|
||||
require.ErrorIs(t, err, test.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidPOPSignature(t *testing.T) {
|
||||
client := newSigner(t)
|
||||
sig, err := client.SignProofOfPossession(validSignatureMsg)
|
||||
require.NoError(t, err)
|
||||
ok := bls.VerifyProofOfPossession(client.PublicKey(), sig, validSignatureMsg)
|
||||
require.True(t, ok)
|
||||
}
|
||||
|
||||
func TestInvalidPOPSignature(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
client := newSigner(t)
|
||||
sig, err := client.SignProofOfPossession(test.msg)
|
||||
require.Nil(t, sig)
|
||||
require.ErrorIs(t, err, test.err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type stubClient struct {
|
||||
signer *localsigner.LocalSigner
|
||||
}
|
||||
|
||||
func (*stubClient) PublicKey(_ context.Context, _ *signer.PublicKeyRequest, _ ...grpc.CallOption) (*signer.PublicKeyResponse, error) {
|
||||
// this function is not used in the tests, however it's required to implement the `signer.SignerClient` interface
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// for the `Sign` and `SignProofOfPossession` methods, we're using the same logic where
|
||||
// we match on the message to determine the type of response we want to test
|
||||
func (c *stubClient) Sign(_ context.Context, in *signer.SignRequest, _ ...grpc.CallOption) (*signer.SignResponse, error) {
|
||||
switch string(in.Message) {
|
||||
case string(validSignatureMsg):
|
||||
sig, err := c.signer.Sign(in.Message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &signer.SignResponse{
|
||||
Signature: bls.SignatureToBytes(sig),
|
||||
}, nil
|
||||
// the client expects a compressed signature so this signature is invalid
|
||||
// Return malformed bytes to trigger decompression error
|
||||
case string(uncompressedSignatureMsg):
|
||||
// Return invalid signature data that will fail decompression
|
||||
invalidBytes := make([]byte, bls.SignatureLen)
|
||||
// Fill with invalid data
|
||||
for i := range invalidBytes {
|
||||
invalidBytes[i] = 0xFF
|
||||
}
|
||||
return &signer.SignResponse{
|
||||
Signature: invalidBytes,
|
||||
}, nil
|
||||
case string(emptySignatureMsg):
|
||||
return &signer.SignResponse{
|
||||
Signature: []byte{},
|
||||
}, nil
|
||||
case string(noSignatureMsg):
|
||||
return &signer.SignResponse{}, nil
|
||||
default:
|
||||
return nil, errors.New("invalid case")
|
||||
}
|
||||
}
|
||||
|
||||
// see comments from `Sign` function above
|
||||
func (c *stubClient) SignProofOfPossession(_ context.Context, in *signer.SignProofOfPossessionRequest, _ ...grpc.CallOption) (*signer.SignProofOfPossessionResponse, error) {
|
||||
switch string(in.Message) {
|
||||
case string(validSignatureMsg):
|
||||
sig, err := c.signer.SignProofOfPossession(in.Message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &signer.SignProofOfPossessionResponse{
|
||||
Signature: bls.SignatureToBytes(sig),
|
||||
}, nil
|
||||
case string(uncompressedSignatureMsg):
|
||||
// Return invalid signature data that will fail decompression
|
||||
invalidBytes := make([]byte, bls.SignatureLen)
|
||||
// Fill with invalid data
|
||||
for i := range invalidBytes {
|
||||
invalidBytes[i] = 0xFF
|
||||
}
|
||||
return &signer.SignProofOfPossessionResponse{
|
||||
Signature: invalidBytes,
|
||||
}, nil
|
||||
case string(emptySignatureMsg):
|
||||
return &signer.SignProofOfPossessionResponse{
|
||||
Signature: []byte{},
|
||||
}, nil
|
||||
case string(noSignatureMsg):
|
||||
return &signer.SignProofOfPossessionResponse{}, nil
|
||||
default:
|
||||
return nil, errors.New("invalid case")
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package keychain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/math/set"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidIndicesLength = errors.New("number of indices should be greater than 0")
|
||||
ErrInvalidNumAddrsToDerive = errors.New("number of addresses to derive should be greater than 0")
|
||||
ErrInvalidNumAddrsDerived = errors.New("incorrect number of ledger derived addresses")
|
||||
ErrInvalidNumSignatures = errors.New("incorrect number of signatures")
|
||||
)
|
||||
|
||||
// Signer implements functions for a keychain to return its main address and
|
||||
// to sign a hash
|
||||
type Signer interface {
|
||||
SignHash([]byte) ([]byte, error)
|
||||
Sign([]byte) ([]byte, error)
|
||||
Address() ids.ShortID
|
||||
}
|
||||
|
||||
// Keychain maintains a set of addresses together with their corresponding
|
||||
// signers
|
||||
type Keychain interface {
|
||||
// The returned Signer can provide a signature for [addr]
|
||||
Get(addr ids.ShortID) (Signer, bool)
|
||||
// Returns the set of addresses for which the accessor keeps an associated
|
||||
// signer
|
||||
Addresses() set.Set[ids.ShortID]
|
||||
}
|
||||
|
||||
// Ledger interface for hardware wallet support
|
||||
type Ledger interface {
|
||||
Address(displayHRP string, addressIndex uint32) (ids.ShortID, error)
|
||||
SignHash(hash []byte, addressIndex uint32) ([]byte, error)
|
||||
Sign(hash []byte, addressIndex uint32) ([]byte, error)
|
||||
SignTransaction(rawUnsignedHash []byte, addressIndices []uint32) ([][]byte, error)
|
||||
GetAddresses(addressIndices []uint32) ([]ids.ShortID, error)
|
||||
Disconnect() error
|
||||
}
|
||||
|
||||
// ledgerKeychain is an abstraction of the underlying ledger hardware device,
|
||||
// to be able to get a signer from a finite set of derived signers
|
||||
type ledgerKeychain struct {
|
||||
ledger Ledger
|
||||
addrs set.Set[ids.ShortID]
|
||||
addrToIdx map[ids.ShortID]uint32
|
||||
}
|
||||
|
||||
// ledgerSigner is an abstraction of the underlying ledger hardware device,
|
||||
// capable of extracting its main address and signing a hash
|
||||
type ledgerSigner struct {
|
||||
ledger Ledger
|
||||
idx uint32
|
||||
addr ids.ShortID
|
||||
}
|
||||
|
||||
// NewLedgerKeychain creates a new ledger keychain
|
||||
func NewLedgerKeychain(ledger Ledger, indices []uint32) (Keychain, error) {
|
||||
if len(indices) == 0 {
|
||||
return nil, ErrInvalidIndicesLength
|
||||
}
|
||||
|
||||
addresses, err := ledger.GetAddresses(indices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(addresses) != len(indices) {
|
||||
return nil, ErrInvalidNumAddrsDerived
|
||||
}
|
||||
|
||||
addrToIdx := make(map[ids.ShortID]uint32)
|
||||
addrs := set.Set[ids.ShortID]{}
|
||||
for i, addr := range addresses {
|
||||
addrToIdx[addr] = indices[i]
|
||||
addrs.Add(addr)
|
||||
}
|
||||
|
||||
return &ledgerKeychain{
|
||||
ledger: ledger,
|
||||
addrs: addrs,
|
||||
addrToIdx: addrToIdx,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (l *ledgerKeychain) Get(addr ids.ShortID) (Signer, bool) {
|
||||
idx, ok := l.addrToIdx[addr]
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
return &ledgerSigner{
|
||||
ledger: l.ledger,
|
||||
idx: idx,
|
||||
addr: addr,
|
||||
}, true
|
||||
}
|
||||
|
||||
func (l *ledgerKeychain) Addresses() set.Set[ids.ShortID] {
|
||||
return l.addrs
|
||||
}
|
||||
|
||||
func (l *ledgerSigner) SignHash(hash []byte) ([]byte, error) {
|
||||
return l.ledger.SignHash(hash, l.idx)
|
||||
}
|
||||
|
||||
func (l *ledgerSigner) Sign(hash []byte) ([]byte, error) {
|
||||
return l.ledger.Sign(hash, l.idx)
|
||||
}
|
||||
|
||||
func (l *ledgerSigner) Address() ids.ShortID {
|
||||
return l.addr
|
||||
}
|
||||
@@ -1,425 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package keychain
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/mock/gomock"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/utils/crypto/keychain/keychainmock"
|
||||
)
|
||||
|
||||
var errTest = errors.New("test")
|
||||
|
||||
func TestNewLedgerKeychain(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
addr := ids.GenerateTestShortID()
|
||||
|
||||
// user request invalid number of addresses to derive
|
||||
ledger := keychainmock.NewLedger(ctrl)
|
||||
_, err := NewLedgerKeychain(ledger, []uint32{})
|
||||
require.ErrorIs(err, ErrInvalidIndicesLength)
|
||||
|
||||
// ledger does not return expected number of derived addresses
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{}, nil).Times(1)
|
||||
_, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.ErrorIs(err, ErrInvalidNumAddrsDerived)
|
||||
|
||||
// ledger return error when asked for derived addresses
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr}, errTest).Times(1)
|
||||
_, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.ErrorIs(err, errTest)
|
||||
|
||||
// good path
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr}, nil).Times(1)
|
||||
_, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func TestLedgerKeychain_Addresses(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
addr1 := ids.GenerateTestShortID()
|
||||
addr2 := ids.GenerateTestShortID()
|
||||
addr3 := ids.GenerateTestShortID()
|
||||
|
||||
// 1 addr
|
||||
ledger := keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
kc, err := NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
addrs := kc.Addresses()
|
||||
require.Len(addrs, 1)
|
||||
require.True(addrs.Contains(addr1))
|
||||
|
||||
// multiple addresses
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0, 1, 2}).Return([]ids.ShortID{addr1, addr2, addr3}, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, []uint32{0, 1, 2})
|
||||
require.NoError(err)
|
||||
|
||||
addrs = kc.Addresses()
|
||||
require.Len(addrs, 3)
|
||||
require.Contains(addrs, addr1)
|
||||
require.Contains(addrs, addr2)
|
||||
require.Contains(addrs, addr3)
|
||||
}
|
||||
|
||||
func TestLedgerKeychain_Get(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
addr1 := ids.GenerateTestShortID()
|
||||
addr2 := ids.GenerateTestShortID()
|
||||
addr3 := ids.GenerateTestShortID()
|
||||
|
||||
// 1 addr
|
||||
ledger := keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
kc, err := NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
_, b := kc.Get(ids.GenerateTestShortID())
|
||||
require.False(b)
|
||||
|
||||
s, b := kc.Get(addr1)
|
||||
require.Equal(s.Address(), addr1)
|
||||
require.True(b)
|
||||
|
||||
// multiple addresses
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0, 1, 2}).Return([]ids.ShortID{addr1, addr2, addr3}, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, []uint32{0, 1, 2})
|
||||
require.NoError(err)
|
||||
|
||||
_, b = kc.Get(ids.GenerateTestShortID())
|
||||
require.False(b)
|
||||
|
||||
s, b = kc.Get(addr1)
|
||||
require.True(b)
|
||||
require.Equal(s.Address(), addr1)
|
||||
|
||||
s, b = kc.Get(addr2)
|
||||
require.True(b)
|
||||
require.Equal(s.Address(), addr2)
|
||||
|
||||
s, b = kc.Get(addr3)
|
||||
require.True(b)
|
||||
require.Equal(s.Address(), addr3)
|
||||
}
|
||||
|
||||
func TestLedgerSigner_SignHash(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
addr1 := ids.GenerateTestShortID()
|
||||
addr2 := ids.GenerateTestShortID()
|
||||
addr3 := ids.GenerateTestShortID()
|
||||
toSign := []byte{1, 2, 3, 4, 5}
|
||||
expectedSignature1 := []byte{1, 1, 1}
|
||||
expectedSignature2 := []byte{2, 2, 2}
|
||||
expectedSignature3 := []byte{3, 3, 3}
|
||||
|
||||
// ledger returns an empty signature
|
||||
ledger := keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, uint32(0)).Return([]byte{}, nil).Times(1)
|
||||
kc, err := NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
s, b := kc.Get(addr1)
|
||||
require.True(b)
|
||||
|
||||
signature, err := s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal([]byte{}, signature)
|
||||
|
||||
// ledger returns an error when asked for signature
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, uint32(0)).Return(expectedSignature1, errTest).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
s, b = kc.Get(addr1)
|
||||
require.True(b)
|
||||
|
||||
_, err = s.SignHash(toSign)
|
||||
require.ErrorIs(err, errTest)
|
||||
|
||||
// good path 1 addr
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, uint32(0)).Return(expectedSignature1, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
s, b = kc.Get(addr1)
|
||||
require.True(b)
|
||||
|
||||
signature, err = s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal(expectedSignature1, signature)
|
||||
|
||||
// good path 3 addr
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0, 1, 2}).Return([]ids.ShortID{addr1, addr2, addr3}, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, uint32(0)).Return(expectedSignature1, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, uint32(1)).Return(expectedSignature2, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, uint32(2)).Return(expectedSignature3, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, []uint32{0, 1, 2})
|
||||
require.NoError(err)
|
||||
|
||||
s, b = kc.Get(addr1)
|
||||
require.True(b)
|
||||
|
||||
signature, err = s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal(expectedSignature1, signature)
|
||||
|
||||
s, b = kc.Get(addr2)
|
||||
require.True(b)
|
||||
|
||||
signature, err = s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal(expectedSignature2, signature)
|
||||
|
||||
s, b = kc.Get(addr3)
|
||||
require.True(b)
|
||||
|
||||
signature, err = s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal(expectedSignature3, signature)
|
||||
}
|
||||
|
||||
func TestNewLedgerKeychainWithIndices(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
addr := ids.GenerateTestShortID()
|
||||
_ = addr
|
||||
|
||||
// user request invalid number of indices
|
||||
ledger := keychainmock.NewLedger(ctrl)
|
||||
_, err := NewLedgerKeychain(ledger, []uint32{})
|
||||
require.ErrorIs(err, ErrInvalidIndicesLength)
|
||||
|
||||
// ledger does not return expected number of derived addresses
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{}, nil).Times(1)
|
||||
_, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.ErrorIs(err, ErrInvalidNumAddrsDerived)
|
||||
|
||||
// ledger return error when asked for derived addresses
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr}, errTest).Times(1)
|
||||
_, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.ErrorIs(err, errTest)
|
||||
|
||||
// good path
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr}, nil).Times(1)
|
||||
_, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
}
|
||||
|
||||
func TestLedgerKeychainFromIndices_Addresses(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
addr1 := ids.GenerateTestShortID()
|
||||
addr2 := ids.GenerateTestShortID()
|
||||
addr3 := ids.GenerateTestShortID()
|
||||
|
||||
// 1 addr
|
||||
ledger := keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
kc, err := NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
addrs := kc.Addresses()
|
||||
require.Len(addrs, 1)
|
||||
require.True(addrs.Contains(addr1))
|
||||
|
||||
// first 3 addresses
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0, 1, 2}).Return([]ids.ShortID{addr1, addr2, addr3}, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, []uint32{0, 1, 2})
|
||||
require.NoError(err)
|
||||
|
||||
addrs = kc.Addresses()
|
||||
require.Len(addrs, 3)
|
||||
require.Contains(addrs, addr1)
|
||||
require.Contains(addrs, addr2)
|
||||
require.Contains(addrs, addr3)
|
||||
|
||||
// some 3 addresses
|
||||
indices := []uint32{3, 7, 1}
|
||||
addresses := []ids.ShortID{addr1, addr2, addr3}
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses(indices).Return(addresses, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, indices)
|
||||
require.NoError(err)
|
||||
|
||||
addrs = kc.Addresses()
|
||||
require.Len(addrs, len(indices))
|
||||
require.Contains(addrs, addr1)
|
||||
require.Contains(addrs, addr2)
|
||||
require.Contains(addrs, addr3)
|
||||
|
||||
// repeated addresses
|
||||
indices = []uint32{3, 7, 1, 3, 1, 7}
|
||||
addresses = []ids.ShortID{addr1, addr2, addr3, addr1, addr2, addr3}
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses(indices).Return(addresses, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, indices)
|
||||
require.NoError(err)
|
||||
|
||||
addrs = kc.Addresses()
|
||||
require.Len(addrs, 3)
|
||||
require.Contains(addrs, addr1)
|
||||
require.Contains(addrs, addr2)
|
||||
require.Contains(addrs, addr3)
|
||||
}
|
||||
|
||||
func TestLedgerKeychainFromIndices_Get(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
addr1 := ids.GenerateTestShortID()
|
||||
addr2 := ids.GenerateTestShortID()
|
||||
addr3 := ids.GenerateTestShortID()
|
||||
|
||||
// 1 addr
|
||||
ledger := keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
kc, err := NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
_, b := kc.Get(ids.GenerateTestShortID())
|
||||
require.False(b)
|
||||
|
||||
s, b := kc.Get(addr1)
|
||||
require.Equal(s.Address(), addr1)
|
||||
require.True(b)
|
||||
|
||||
// some 3 addresses
|
||||
indices := []uint32{3, 7, 1}
|
||||
addresses := []ids.ShortID{addr1, addr2, addr3}
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses(indices).Return(addresses, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, indices)
|
||||
require.NoError(err)
|
||||
|
||||
_, b = kc.Get(ids.GenerateTestShortID())
|
||||
require.False(b)
|
||||
|
||||
s, b = kc.Get(addr1)
|
||||
require.True(b)
|
||||
require.Equal(s.Address(), addr1)
|
||||
|
||||
s, b = kc.Get(addr2)
|
||||
require.True(b)
|
||||
require.Equal(s.Address(), addr2)
|
||||
|
||||
s, b = kc.Get(addr3)
|
||||
require.True(b)
|
||||
require.Equal(s.Address(), addr3)
|
||||
}
|
||||
|
||||
func TestLedgerSignerFromIndices_SignHash(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ctrl := gomock.NewController(t)
|
||||
|
||||
addr1 := ids.GenerateTestShortID()
|
||||
addr2 := ids.GenerateTestShortID()
|
||||
addr3 := ids.GenerateTestShortID()
|
||||
toSign := []byte{1, 2, 3, 4, 5}
|
||||
expectedSignature1 := []byte{1, 1, 1}
|
||||
expectedSignature2 := []byte{2, 2, 2}
|
||||
expectedSignature3 := []byte{3, 3, 3}
|
||||
|
||||
// ledger returns an incorrect number of signatures
|
||||
ledger := keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, uint32(0)).Return(expectedSignature1, nil).Times(1)
|
||||
kc, err := NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
s, b := kc.Get(addr1)
|
||||
require.True(b)
|
||||
|
||||
_, err = s.SignHash(toSign)
|
||||
require.ErrorIs(err, ErrInvalidNumSignatures)
|
||||
|
||||
// ledger returns an error when asked for signature
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, []uint32{0}).Return([][]byte{expectedSignature1}, errTest).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
s, b = kc.Get(addr1)
|
||||
require.True(b)
|
||||
|
||||
_, err = s.SignHash(toSign)
|
||||
require.ErrorIs(err, errTest)
|
||||
|
||||
// good path 1 addr
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses([]uint32{0}).Return([]ids.ShortID{addr1}, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, uint32(0)).Return(expectedSignature1, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, []uint32{0})
|
||||
require.NoError(err)
|
||||
|
||||
s, b = kc.Get(addr1)
|
||||
require.True(b)
|
||||
|
||||
signature, err := s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal(expectedSignature1, signature)
|
||||
|
||||
// good path some 3 addresses
|
||||
indices := []uint32{3, 7, 1}
|
||||
addresses := []ids.ShortID{addr1, addr2, addr3}
|
||||
ledger = keychainmock.NewLedger(ctrl)
|
||||
ledger.EXPECT().GetAddresses(indices).Return(addresses, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, indices[0]).Return(expectedSignature1, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, indices[1]).Return(expectedSignature2, nil).Times(1)
|
||||
ledger.EXPECT().SignHash(toSign, indices[2]).Return(expectedSignature3, nil).Times(1)
|
||||
kc, err = NewLedgerKeychain(ledger, indices)
|
||||
require.NoError(err)
|
||||
|
||||
s, b = kc.Get(addr1)
|
||||
require.True(b)
|
||||
|
||||
signature, err = s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal(expectedSignature1, signature)
|
||||
|
||||
s, b = kc.Get(addr2)
|
||||
require.True(b)
|
||||
|
||||
signature, err = s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal(expectedSignature2, signature)
|
||||
|
||||
s, b = kc.Get(addr3)
|
||||
require.True(b)
|
||||
|
||||
signature, err = s.SignHash(toSign)
|
||||
require.NoError(err)
|
||||
require.Equal(expectedSignature3, signature)
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
// Code generated by MockGen. DO NOT EDIT.
|
||||
// Source: github.com/luxfi/node/utils/crypto/keychain (interfaces: Ledger)
|
||||
//
|
||||
// Generated by this command:
|
||||
//
|
||||
// mockgen -package=keychainmock -destination=keychainmock/ledger.go -mock_names=Ledger=Ledger . Ledger
|
||||
//
|
||||
|
||||
// Package keychainmock is a generated GoMock package.
|
||||
package keychainmock
|
||||
|
||||
import (
|
||||
reflect "reflect"
|
||||
|
||||
ids "github.com/luxfi/ids"
|
||||
gomock "go.uber.org/mock/gomock"
|
||||
)
|
||||
|
||||
// Ledger is a mock of Ledger interface.
|
||||
type Ledger struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *LedgerMockRecorder
|
||||
isgomock struct{}
|
||||
}
|
||||
|
||||
// LedgerMockRecorder is the mock recorder for Ledger.
|
||||
type LedgerMockRecorder struct {
|
||||
mock *Ledger
|
||||
}
|
||||
|
||||
// NewLedger creates a new mock instance.
|
||||
func NewLedger(ctrl *gomock.Controller) *Ledger {
|
||||
mock := &Ledger{ctrl: ctrl}
|
||||
mock.recorder = &LedgerMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *Ledger) EXPECT() *LedgerMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// Address mocks base method.
|
||||
func (m *Ledger) Address(displayHRP string, addressIndex uint32) (ids.ShortID, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Address", displayHRP, addressIndex)
|
||||
ret0, _ := ret[0].(ids.ShortID)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Address indicates an expected call of Address.
|
||||
func (mr *LedgerMockRecorder) Address(displayHRP, addressIndex any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Address", reflect.TypeOf((*Ledger)(nil).Address), displayHRP, addressIndex)
|
||||
}
|
||||
|
||||
// Disconnect mocks base method.
|
||||
func (m *Ledger) Disconnect() error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Disconnect")
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// Disconnect indicates an expected call of Disconnect.
|
||||
func (mr *LedgerMockRecorder) Disconnect() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Disconnect", reflect.TypeOf((*Ledger)(nil).Disconnect))
|
||||
}
|
||||
|
||||
// GetAddresses mocks base method.
|
||||
func (m *Ledger) GetAddresses(addressIndices []uint32) ([]ids.ShortID, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetAddresses", addressIndices)
|
||||
ret0, _ := ret[0].([]ids.ShortID)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetAddresses indicates an expected call of GetAddresses.
|
||||
func (mr *LedgerMockRecorder) GetAddresses(addressIndices any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAddresses", reflect.TypeOf((*Ledger)(nil).GetAddresses), addressIndices)
|
||||
}
|
||||
|
||||
// Sign mocks base method.
|
||||
func (m *Ledger) Sign(hash []byte, addressIndex uint32) ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Sign", hash, addressIndex)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Sign indicates an expected call of Sign.
|
||||
func (mr *LedgerMockRecorder) Sign(hash, addressIndex any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sign", reflect.TypeOf((*Ledger)(nil).Sign), hash, addressIndex)
|
||||
}
|
||||
|
||||
// SignHash mocks base method.
|
||||
func (m *Ledger) SignHash(hash []byte, addressIndex uint32) ([]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SignHash", hash, addressIndex)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SignHash indicates an expected call of SignHash.
|
||||
func (mr *LedgerMockRecorder) SignHash(hash, addressIndex any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignHash", reflect.TypeOf((*Ledger)(nil).SignHash), hash, addressIndex)
|
||||
}
|
||||
|
||||
// SignTransaction mocks base method.
|
||||
func (m *Ledger) SignTransaction(rawUnsignedHash []byte, addressIndices []uint32) ([][]byte, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "SignTransaction", rawUnsignedHash, addressIndices)
|
||||
ret0, _ := ret[0].([][]byte)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// SignTransaction indicates an expected call of SignTransaction.
|
||||
func (mr *LedgerMockRecorder) SignTransaction(rawUnsignedHash, addressIndices any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SignTransaction", reflect.TypeOf((*Ledger)(nil).SignTransaction), rawUnsignedHash, addressIndices)
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package keychain
|
||||
|
||||
//go:generate go run go.uber.org/mock/mockgen -package=${GOPACKAGE}mock -destination=${GOPACKAGE}mock/ledger.go -mock_names=Ledger=Ledger . Ledger
|
||||
@@ -1,118 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package ledger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/luxfi/ids"
|
||||
"github.com/luxfi/node/utils/crypto/keychain"
|
||||
"github.com/luxfi/node/version"
|
||||
|
||||
ledger "github.com/luxfi/ledger-lux-go"
|
||||
)
|
||||
|
||||
// LedgerAdapter wraps ledger.LedgerLux to implement keychain.Ledger interface
|
||||
type LedgerAdapter struct {
|
||||
device *ledger.LedgerLux
|
||||
}
|
||||
|
||||
// NewLedger creates a new ledger adapter
|
||||
func NewLedger() (keychain.Ledger, error) {
|
||||
device, err := ledger.FindLedgerLuxApp()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &LedgerAdapter{device: device}, nil
|
||||
}
|
||||
|
||||
// Version returns the app version
|
||||
func (l *LedgerAdapter) Version() (*version.Semantic, error) {
|
||||
ver, err := l.device.GetVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &version.Semantic{
|
||||
Major: int(ver.Major),
|
||||
Minor: int(ver.Minor),
|
||||
Patch: int(ver.Patch),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Address returns an address at the given index
|
||||
func (l *LedgerAdapter) Address(displayHRP string, addressIndex uint32) (ids.ShortID, error) {
|
||||
pathStr := fmt.Sprintf("44'/9000'/%d'/0/0", addressIndex)
|
||||
resp, err := l.device.GetPubKey(pathStr, false, displayHRP, "")
|
||||
if err != nil {
|
||||
return ids.ShortID{}, err
|
||||
}
|
||||
return ids.ShortFromString(resp.Address)
|
||||
}
|
||||
|
||||
// SignHash signs a hash with the given address index
|
||||
func (l *LedgerAdapter) SignHash(hash []byte, addressIndex uint32) ([]byte, error) {
|
||||
pathPrefix := fmt.Sprintf("44'/9000'/%d'", addressIndex)
|
||||
signingPaths := []string{"0/0"}
|
||||
resp, err := l.device.SignHash(pathPrefix, signingPaths, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Get the first signature from the response
|
||||
for _, sig := range resp.Signature {
|
||||
return sig, nil
|
||||
}
|
||||
return nil, errors.New("no signature returned")
|
||||
}
|
||||
|
||||
// Sign signs data with the given address index
|
||||
func (l *LedgerAdapter) Sign(data []byte, addressIndex uint32) ([]byte, error) {
|
||||
return l.SignHash(data, addressIndex)
|
||||
}
|
||||
|
||||
// SignTransaction signs a transaction with multiple addresses
|
||||
func (l *LedgerAdapter) SignTransaction(rawUnsignedHash []byte, addressIndices []uint32) ([][]byte, error) {
|
||||
// Build signing paths for all addresses
|
||||
signingPaths := make([]string, len(addressIndices))
|
||||
for i, idx := range addressIndices {
|
||||
signingPaths[i] = fmt.Sprintf("%d'/0/0", idx)
|
||||
}
|
||||
|
||||
// Sign with all paths at once
|
||||
pathPrefix := "44'/9000'"
|
||||
resp, err := l.device.SignHash(pathPrefix, signingPaths, rawUnsignedHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract signatures
|
||||
sigs := make([][]byte, 0, len(resp.Signature))
|
||||
for _, sig := range resp.Signature {
|
||||
sigs = append(sigs, sig)
|
||||
}
|
||||
|
||||
if len(sigs) != len(addressIndices) {
|
||||
return nil, errors.New("incorrect number of signatures returned")
|
||||
}
|
||||
|
||||
return sigs, nil
|
||||
}
|
||||
|
||||
// GetAddresses returns addresses at the given indices
|
||||
func (l *LedgerAdapter) GetAddresses(addressIndices []uint32) ([]ids.ShortID, error) {
|
||||
addrs := make([]ids.ShortID, len(addressIndices))
|
||||
for i, idx := range addressIndices {
|
||||
addr, err := l.Address("", idx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addrs[i] = addr
|
||||
}
|
||||
return addrs, nil
|
||||
}
|
||||
|
||||
// Disconnect closes the ledger connection
|
||||
func (l *LedgerAdapter) Disconnect() error {
|
||||
return l.device.Close()
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package ledger
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/luxfi/crypto/secp256k1"
|
||||
"github.com/luxfi/node/utils/formatting/address"
|
||||
"github.com/luxfi/node/utils/hashing"
|
||||
)
|
||||
|
||||
const (
|
||||
chainAlias = "P"
|
||||
hrp = "fuji"
|
||||
)
|
||||
|
||||
// TestLedger will be skipped if a ledger is not connected.
|
||||
func TestLedger(t *testing.T) {
|
||||
require := require.New(t)
|
||||
|
||||
// Initialize Ledger
|
||||
device, err := NewLedger()
|
||||
if err != nil {
|
||||
t.Skip("ledger not detected")
|
||||
}
|
||||
|
||||
// Get version (requires type assertion to *LedgerAdapter)
|
||||
if adapter, ok := device.(*LedgerAdapter); ok {
|
||||
version, err := adapter.Version()
|
||||
require.NoError(err)
|
||||
t.Logf("version: %s\n", version)
|
||||
}
|
||||
|
||||
// Get Fuji Address
|
||||
addr, err := device.Address(hrp, 0)
|
||||
require.NoError(err)
|
||||
paddr, err := address.Format(chainAlias, hrp, addr[:])
|
||||
require.NoError(err)
|
||||
t.Logf("address: %s shortID: %s\n", paddr, addr)
|
||||
|
||||
// Get Extended Addresses
|
||||
addresses, err := device.GetAddresses([]uint32{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
require.NoError(err)
|
||||
for i, taddr := range addresses {
|
||||
paddr, err := address.Format(chainAlias, hrp, taddr[:])
|
||||
require.NoError(err)
|
||||
t.Logf("address(%d): %s shortID: %s\n", i, paddr, taddr)
|
||||
|
||||
// Ensure first derived address matches directly requested address
|
||||
if i == 0 {
|
||||
require.Equal(addr, taddr, "address mismatch at index 0")
|
||||
}
|
||||
}
|
||||
|
||||
// Sign Hash (SignHash now takes single index, returns single signature)
|
||||
rawHash := hashing.ComputeHash256([]byte{0x1, 0x2, 0x3, 0x4})
|
||||
indices := []uint32{1, 3}
|
||||
sigs := make([][]byte, len(indices))
|
||||
for i, addrIndex := range indices {
|
||||
sig, err := device.SignHash(rawHash, addrIndex)
|
||||
require.NoError(err)
|
||||
sigs[i] = sig
|
||||
}
|
||||
require.Len(sigs, 2)
|
||||
|
||||
for i, addrIndex := range indices {
|
||||
sig := sigs[i]
|
||||
|
||||
pk, err := secp256k1.RecoverPublicKeyFromHash(rawHash, sig)
|
||||
require.NoError(err)
|
||||
require.Equal(addresses[addrIndex], pk.Address())
|
||||
}
|
||||
|
||||
// Disconnect
|
||||
require.NoError(device.Disconnect())
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package dynamicip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/netip"
|
||||
"strings"
|
||||
|
||||
"github.com/luxfi/node/utils/ips"
|
||||
)
|
||||
|
||||
var _ Resolver = (*ifConfigResolver)(nil)
|
||||
|
||||
// ifConfigResolver resolves our public IP using ifconfig's format.
|
||||
type ifConfigResolver struct {
|
||||
url string
|
||||
}
|
||||
|
||||
func (r *ifConfigResolver) Resolve(ctx context.Context) (netip.Addr, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, r.url, nil)
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
ipBytes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
// Drop any error to report the original error
|
||||
return netip.Addr{}, fmt.Errorf("failed to read response from %q: %w", r.url, err)
|
||||
}
|
||||
|
||||
ipStr := strings.TrimSpace(string(ipBytes))
|
||||
return ips.ParseAddr(ipStr)
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package dynamicip
|
||||
|
||||
import "github.com/luxfi/log"
|
||||
|
||||
var _ Updater = noUpdater{}
|
||||
|
||||
func NewNoUpdater() Updater {
|
||||
return noUpdater{}
|
||||
}
|
||||
|
||||
type noUpdater struct{}
|
||||
|
||||
func (noUpdater) Dispatch(log.Logger) {}
|
||||
|
||||
func (noUpdater) Stop() {}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package dynamicip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net"
|
||||
"net/netip"
|
||||
|
||||
"github.com/luxfi/node/utils/ips"
|
||||
)
|
||||
|
||||
const openDNSUrl = "resolver1.opendns.com:53"
|
||||
|
||||
var (
|
||||
errOpenDNSNoIP = errors.New("openDNS returned no ip")
|
||||
|
||||
_ Resolver = (*openDNSResolver)(nil)
|
||||
)
|
||||
|
||||
// openDNSResolver resolves our public IP using openDNS
|
||||
type openDNSResolver struct {
|
||||
resolver *net.Resolver
|
||||
}
|
||||
|
||||
func newOpenDNSResolver() Resolver {
|
||||
return &openDNSResolver{
|
||||
resolver: &net.Resolver{
|
||||
PreferGo: true,
|
||||
Dial: func(ctx context.Context, _, _ string) (net.Conn, error) {
|
||||
d := net.Dialer{}
|
||||
return d.DialContext(ctx, "udp", openDNSUrl)
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (r *openDNSResolver) Resolve(ctx context.Context) (netip.Addr, error) {
|
||||
resolvedIPs, err := r.resolver.LookupIP(ctx, "ip", "myip.opendns.com")
|
||||
if err != nil {
|
||||
return netip.Addr{}, err
|
||||
}
|
||||
for _, ip := range resolvedIPs {
|
||||
if addr, ok := ips.AddrFromSlice(ip); ok {
|
||||
return addr, nil
|
||||
}
|
||||
}
|
||||
return netip.Addr{}, errOpenDNSNoIP
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
// Copyright (C) 2019-2025, Lux Industries, Inc. All rights reserved.
|
||||
// See the file LICENSE for licensing terms.
|
||||
|
||||
package dynamicip
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/netip"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
ifConfigCoURL = "http://ifconfig.co"
|
||||
ifConfigMeURL = "http://ifconfig.me"
|
||||
// Note: All of the names below must be lowercase
|
||||
// because we lowercase the user's input in NewResolver.
|
||||
// TODO remove either ifConfig or ifConfigCo.
|
||||
// They do the same thing.
|
||||
OpenDNSName = "opendns"
|
||||
IFConfigName = "ifconfig"
|
||||
IFConfigCoName = "ifconfigco"
|
||||
IFConfigMeName = "ifconfigme"
|
||||
)
|
||||
|
||||
var errUnknownResolver = errors.New("unknown resolver")
|
||||
|
||||
// Resolver resolves our public IP
|
||||
type Resolver interface {
|
||||
// Resolve and return our public IP.
|
||||
Resolve(context.Context) (netip.Addr, error)
|
||||
}
|
||||
|
||||
// Returns a new Resolver that uses the given service
|
||||
// to resolve our public IP.
|
||||
// [resolverName] must be one of:
|
||||
// [OpenDNSName], [IFConfigName], [IFConfigCoName], [IFConfigMeName].
|
||||
// If [resolverService] isn't one of the above, returns an error
|
||||
func NewResolver(resolverName string) (Resolver, error) {
|
||||
switch strings.ToLower(resolverName) {
|
||||
case OpenDNSName:
|
||||
return newOpenDNSResolver(), nil
|
||||
case IFConfigName, IFConfigCoName:
|
||||
return &ifConfigResolver{url: ifConfigCoURL}, nil
|
||||
case IFConfigMeName:
|
||||
return &ifConfigResolver{url: ifConfigMeURL}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("%w: %s", errUnknownResolver, resolverName)
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user