From 0ba527add339fd07d7d3c29dcb20c830319961e3 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:49:13 +0100 Subject: [PATCH 01/16] chore: add QueryCache interface --- pkg/client/interface.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/client/interface.go b/pkg/client/interface.go index 365c24b74..be0f88296 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -360,3 +360,11 @@ type BankQueryClient interface { // GetBalance queries the chain for the uPOKT balance of the account provided GetBalance(ctx context.Context, address string) (*cosmostypes.Coin, error) } + +// QueryCache handles a single type of cached data +type QueryCache[T any] interface { + Get(key string) (T, error) + Set(key string, value T) error + Delete(key string) + Clear() +} From af418e75abf0c5205a974fe5a89bd0cd16587a4a Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:49:41 +0100 Subject: [PATCH 02/16] chore: add HistoricalQueryCache interface --- pkg/client/interface.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pkg/client/interface.go b/pkg/client/interface.go index be0f88296..fdbc3ef31 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -368,3 +368,12 @@ type QueryCache[T any] interface { Delete(key string) Clear() } + +// HistoricalQueryCache extends QueryCache to support historical values at different heights +type HistoricalQueryCache[T any] interface { + QueryCache[T] + // GetAtHeight retrieves the nearest value <= the specified height + GetAtHeight(key string, height int64) (T, error) + // SetAtHeight adds or updates a value at a specific height + SetAtHeight(key string, value T, height int64) error +} From 6df478ab7ba4ca82aad86284a7d176f44fe05566 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:53:19 +0100 Subject: [PATCH 03/16] feat: add InMemoryCache implementation --- pkg/client/query/cache/config.go | 76 ++++++ pkg/client/query/cache/errors.go | 10 + pkg/client/query/cache/memory.go | 245 ++++++++++++++++++ pkg/client/query/cache/memory_test.go | 343 ++++++++++++++++++++++++++ 4 files changed, 674 insertions(+) create mode 100644 pkg/client/query/cache/config.go create mode 100644 pkg/client/query/cache/errors.go create mode 100644 pkg/client/query/cache/memory.go create mode 100644 pkg/client/query/cache/memory_test.go diff --git a/pkg/client/query/cache/config.go b/pkg/client/query/cache/config.go new file mode 100644 index 000000000..10153f139 --- /dev/null +++ b/pkg/client/query/cache/config.go @@ -0,0 +1,76 @@ +package cache + +import ( + "time" +) + +// EvictionPolicy determines how items are removed when cache is full. +type EvictionPolicy int64 + +const ( + FirstInFirstOut = EvictionPolicy(iota) + LeastRecentlyUsed + LeastFrequentlyUsed +) + +// CacheConfig is the configuration options for a cache. +type CacheConfig struct { + // MaxKeys is the maximum number of items the cache can hold. + MaxKeys int64 + // EvictionPolicy is how items should be removed when the cache is full. + EvictionPolicy EvictionPolicy + // TTL is how long items should remain in the cache + TTL time.Duration + + // historical is whether the cache will cache a single value for each key + // (false) or whether it will cache a history of values for each key (true). + historical bool + // pruneOlderThan is the number of past blocks for which to keep historical + // values. If 0, no historical pruning is performed. + pruneOlderThan int64 +} + +// CacheOption defines a function that configures a CacheConfig +type CacheOption func(*CacheConfig) + +// HistoricalCacheConfig extends the basic CacheConfig with historical settings. +type HistoricalCacheConfig struct { + CacheConfig + + // MaxHeightsPerKey is the maximum number of different heights to store per key + MaxHeightsPerKey int + // PruneOlderThan specifies how many blocks back to maintain in history + // If 0, no historical pruning is performed + PruneOlderThan int64 +} + +// WithHistoricalMode enables historical caching with the given pruneOlderThan +// configuration, if 0 no historical pruning is performed. +func WithHistoricalMode(pruneOlderThan int64) CacheOption { + return func(cfg *CacheConfig) { + cfg.historical = true + cfg.pruneOlderThan = pruneOlderThan + } +} + +// WithMaxKeys sets the maximum number of distinct key/value pairs the cache will +// hold before evicting according to the configured eviction policy. +func WithMaxKeys(size int64) CacheOption { + return func(cfg *CacheConfig) { + cfg.MaxKeys = size + } +} + +// WithEvictionPolicy sets the eviction policy +func WithEvictionPolicy(policy EvictionPolicy) CacheOption { + return func(cfg *CacheConfig) { + cfg.EvictionPolicy = policy + } +} + +// WithTTL sets the time-to-live for cache entries +func WithTTL(ttl time.Duration) CacheOption { + return func(cfg *CacheConfig) { + cfg.TTL = ttl + } +} diff --git a/pkg/client/query/cache/errors.go b/pkg/client/query/cache/errors.go new file mode 100644 index 000000000..10e017568 --- /dev/null +++ b/pkg/client/query/cache/errors.go @@ -0,0 +1,10 @@ +package cache + +import "cosmossdk.io/errors" + +const codesace = "client/query/cache" + +var ( + ErrCacheMiss = errors.Register(codesace, 1, "cache miss") + ErrHistoricalModeNotEnabled = errors.Register(codesace, 2, "historical mode not enabled") +) diff --git a/pkg/client/query/cache/memory.go b/pkg/client/query/cache/memory.go new file mode 100644 index 000000000..06732f55a --- /dev/null +++ b/pkg/client/query/cache/memory.go @@ -0,0 +1,245 @@ +package cache + +import ( + "sync" + "sync/atomic" + "time" + + "github.com/pokt-network/poktroll/pkg/client" +) + +var ( + _ client.QueryCache[any] = (*InMemoryCache[any])(nil) + _ client.HistoricalQueryCache[any] = (*InMemoryCache[any])(nil) +) + +// InMemoryCache provides a concurrency-safe in-memory cache implementation with +// optional historical value support. +type InMemoryCache[T any] struct { + config CacheConfig + latestHeight atomic.Int64 + + itemsMu sync.RWMutex + // items type depends on historical mode: + // | historical mode | type | + // | --------------- | --------------------------------------- | + // | false | map[string]cacheItem[T] | + // | true | map[string]map[int64]heightCacheItem[T] | + items map[string]any +} + +// cacheItem wraps cached values with metadata +type cacheItem[T any] struct { + value T + timestamp time.Time +} + +// heightCacheItem is used when the cache is in historical mode +type heightCacheItem[T any] struct { + value T + timestamp time.Time +} + +// NewInMemoryCache creates a new cache with the given configuration +func NewInMemoryCache[T any](opts ...CacheOption) *InMemoryCache[T] { + config := CacheConfig{ + EvictionPolicy: FirstInFirstOut, + } + + for _, opt := range opts { + opt(&config) + } + + return &InMemoryCache[T]{ + items: make(map[string]interface{}), + config: config, + } +} + +// Get retrieves an item from the cache +func (c *InMemoryCache[T]) Get(key string) (T, error) { + if c.config.historical { + return c.GetAtHeight(key, c.latestHeight.Load()) + } + + c.itemsMu.RLock() + defer c.itemsMu.RUnlock() + + var zero T + + item, exists := c.items[key] + if !exists { + return zero, ErrCacheMiss + } + + cItem := item.(cacheItem[T]) + if c.config.TTL > 0 && time.Since(cItem.timestamp) > c.config.TTL { + // TODO_QUESTION: should we prune here? + return zero, ErrCacheMiss + } + + return cItem.value, nil +} + +// GetAtHeight retrieves an item from the cache at or before the specified height +func (c *InMemoryCache[T]) GetAtHeight(key string, height int64) (T, error) { + var zero T + + if !c.config.historical { + return zero, ErrHistoricalModeNotEnabled + } + + c.itemsMu.RLock() + defer c.itemsMu.RUnlock() + + heightMap, exists := c.items[key] + if !exists { + return zero, ErrCacheMiss + } + + versions := heightMap.(map[int64]heightCacheItem[T]) + var nearestHeight int64 = -1 + for h := range versions { + if h <= height && h > nearestHeight { + nearestHeight = h + } + } + + if nearestHeight == -1 { + return zero, ErrCacheMiss + } + + item := versions[nearestHeight] + if c.config.TTL > 0 && time.Since(item.timestamp) > c.config.TTL { + return zero, ErrCacheMiss + } + + return item.value, nil +} + +// Set adds or updates an item in the cache +func (c *InMemoryCache[T]) Set(key string, value T) error { + if c.config.historical { + return c.SetAtHeight(key, value, c.latestHeight.Load()) + } + + if c.config.MaxKeys > 0 && int64(len(c.items)) >= c.config.MaxKeys { + c.evict() + } + + c.itemsMu.Lock() + defer c.itemsMu.Unlock() + + c.items[key] = cacheItem[T]{ + value: value, + timestamp: time.Now(), + } + + return nil +} + +// SetAtHeight adds or updates an item in the cache at a specific height +func (c *InMemoryCache[T]) SetAtHeight(key string, value T, height int64) error { + if !c.config.historical { + return ErrHistoricalModeNotEnabled + } + + // Update latest height if this is newer + latestHeight := c.latestHeight.Load() + if height > latestHeight { + // NB: Only update if c.latestHeight hasn't changed since we loaded it above. + c.latestHeight.CompareAndSwap(latestHeight, height) + } + + c.itemsMu.Lock() + defer c.itemsMu.Unlock() + + var history map[int64]heightCacheItem[T] + if existing, exists := c.items[key]; exists { + history = existing.(map[int64]heightCacheItem[T]) + } else { + history = make(map[int64]heightCacheItem[T]) + c.items[key] = history + } + + // Prune old heights if configured + if c.config.pruneOlderThan > 0 { + for h := range history { + if height-h > c.config.pruneOlderThan { + delete(history, h) + } + } + } + + history[height] = heightCacheItem[T]{ + value: value, + timestamp: time.Now(), + } + + return nil +} + +// Delete removes an item from the cache. +func (c *InMemoryCache[T]) Delete(key string) { + c.itemsMu.Lock() + defer c.itemsMu.Unlock() + + delete(c.items, key) +} + +// Clear removes all items from the cache +func (c *InMemoryCache[T]) Clear() { + c.itemsMu.Lock() + defer c.itemsMu.Unlock() + + c.items = make(map[string]interface{}) + c.latestHeight.Store(0) +} + +// evict removes one item according to the configured eviction policy +func (c *InMemoryCache[T]) evict() { + switch c.config.EvictionPolicy { + case FirstInFirstOut: + var oldestKey string + var oldestTime time.Time + first := true + + for key, item := range c.items { + var itemTime time.Time + if c.config.historical { + versions := item.(map[int64]heightCacheItem[T]) + for _, v := range versions { + if itemTime.IsZero() || v.timestamp.Before(itemTime) { + itemTime = v.timestamp + } + } + } else { + itemTime = item.(cacheItem[T]).timestamp + } + + if first || itemTime.Before(oldestTime) { + oldestKey = key + oldestTime = itemTime + first = false + } + } + delete(c.items, oldestKey) + + case LeastRecentlyUsed: + // TODO: Implement LRU eviction + // This will require tracking access times + panic("LRU eviction not implemented") + + case LeastFrequentlyUsed: + // TODO: Implement LFU eviction + // This will require tracking access times + panic("LFU eviction not implemented") + + default: + // Default to FIFO if policy not recognized + for key := range c.items { + delete(c.items, key) + return + } + } +} diff --git a/pkg/client/query/cache/memory_test.go b/pkg/client/query/cache/memory_test.go new file mode 100644 index 000000000..ae8b6a92f --- /dev/null +++ b/pkg/client/query/cache/memory_test.go @@ -0,0 +1,343 @@ +package cache + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +// TestInMemoryCache_NonHistorical tests the basic cache functionality without historical mode +func TestInMemoryCache_NonHistorical(t *testing.T) { + t.Run("basic operations", func(t *testing.T) { + cache := NewInMemoryCache[string]() + + // Test Set and Get + err := cache.Set("key1", "value1") + require.NoError(t, err) + val, err := cache.Get("key1") + require.NoError(t, err) + require.Equal(t, "value1", val) + + // Test missing key + _, err = cache.Get("nonexistent") + require.ErrorIs(t, err, ErrCacheMiss) + + // Test Delete + cache.Delete("key1") + _, err = cache.Get("key1") + require.ErrorIs(t, err, ErrCacheMiss) + + // Test Clear + err = cache.Set("key2", "value2") + require.NoError(t, err) + cache.Clear() + _, err = cache.Get("key2") + require.ErrorIs(t, err, ErrCacheMiss) + }) + + t.Run("TTL expiration", func(t *testing.T) { + cache := NewInMemoryCache[string]( + WithTTL(100 * time.Millisecond), + ) + + err := cache.Set("key", "value") + require.NoError(t, err) + + // Value should be available immediately + val, err := cache.Get("key") + require.NoError(t, err) + require.Equal(t, "value", val) + + // Wait for TTL to expire + time.Sleep(150 * time.Millisecond) + + // Value should now be expired + _, err = cache.Get("key") + require.ErrorIs(t, err, ErrCacheMiss) + }) + + t.Run("max size eviction", func(t *testing.T) { + cache := NewInMemoryCache[string]( + WithMaxKeys(2), + WithEvictionPolicy(FirstInFirstOut), + ) + + // Add items up to max size + err := cache.Set("key1", "value1") + require.NoError(t, err) + err = cache.Set("key2", "value2") + require.NoError(t, err) + + // Add one more item, should trigger eviction + err = cache.Set("key3", "value3") + require.NoError(t, err) + + // First item should be evicted + _, err = cache.Get("key1") + require.ErrorIs(t, err, ErrCacheMiss) + + // Other items should still be present + val, err := cache.Get("key2") + require.NoError(t, err) + require.Equal(t, "value2", val) + + val, err = cache.Get("key3") + require.NoError(t, err) + require.Equal(t, "value3", val) + }) +} + +// TestInMemoryCache_Historical tests the historical mode functionality +func TestInMemoryCache_Historical(t *testing.T) { + t.Run("basic historical operations", func(t *testing.T) { + cache := NewInMemoryCache[string]( + WithHistoricalMode(100), + ) + + // Test SetAtHeight and GetAtHeight + err := cache.SetAtHeight("key", "value1", 10) + require.NoError(t, err) + err = cache.SetAtHeight("key", "value2", 20) + require.NoError(t, err) + + // Test getting exact heights + val, err := cache.GetAtHeight("key", 10) + require.NoError(t, err) + require.Equal(t, "value1", val) + + val, err = cache.GetAtHeight("key", 20) + require.NoError(t, err) + require.Equal(t, "value2", val) + + // Test getting intermediate height (should return nearest lower height) + val, err = cache.GetAtHeight("key", 15) + require.NoError(t, err) + require.Equal(t, "value1", val) + + // Test getting height before first entry + _, err = cache.GetAtHeight("key", 5) + require.ErrorIs(t, err, ErrCacheMiss) + + // Test getting height after last entry + val, err = cache.GetAtHeight("key", 25) + require.NoError(t, err) + require.Equal(t, "value2", val) + }) + + t.Run("historical TTL expiration", func(t *testing.T) { + cache := NewInMemoryCache[string]( + WithHistoricalMode(100), + WithTTL(100*time.Millisecond), + ) + + err := cache.SetAtHeight("key", "value1", 10) + require.NoError(t, err) + + // Value should be available immediately + val, err := cache.GetAtHeight("key", 10) + require.NoError(t, err) + require.Equal(t, "value1", val) + + // Wait for TTL to expire + time.Sleep(150 * time.Millisecond) + + // Value should now be expired + _, err = cache.GetAtHeight("key", 10) + require.ErrorIs(t, err, ErrCacheMiss) + }) + + t.Run("pruning old heights", func(t *testing.T) { + cache := NewInMemoryCache[string]( + WithHistoricalMode(10), // Prune entries older than 10 blocks + ) + + // Add entries at different heights + err := cache.SetAtHeight("key", "value1", 10) + require.NoError(t, err) + err = cache.SetAtHeight("key", "value2", 20) + require.NoError(t, err) + err = cache.SetAtHeight("key", "value3", 30) + require.NoError(t, err) + + // Add a new entry that should trigger pruning + err = cache.SetAtHeight("key", "value4", 40) + require.NoError(t, err) + + // Entries more than 10 blocks old should be pruned + _, err = cache.GetAtHeight("key", 10) + require.ErrorIs(t, err, ErrCacheMiss) + _, err = cache.GetAtHeight("key", 20) + require.ErrorIs(t, err, ErrCacheMiss) + + // Recent entries should still be available + val, err := cache.GetAtHeight("key", 30) + require.NoError(t, err) + require.Equal(t, "value3", val) + + val, err = cache.GetAtHeight("key", 40) + require.NoError(t, err) + require.Equal(t, "value4", val) + }) + + t.Run("non-historical operations on historical cache", func(t *testing.T) { + cache := NewInMemoryCache[string]( + WithHistoricalMode(100), + ) + + // Set some historical values + err := cache.SetAtHeight("key", "value1", 10) + require.NoError(t, err) + err = cache.SetAtHeight("key", "value2", 20) + require.NoError(t, err) + + // Regular Set should work with latest height + err = cache.Set("key", "value3") + require.NoError(t, err) + + // Regular Get should return the latest value + val, err := cache.Get("key") + require.NoError(t, err) + require.Equal(t, "value3", val) + + // Delete should remove all historical values + cache.Delete("key") + _, err = cache.GetAtHeight("key", 10) + require.ErrorIs(t, err, ErrCacheMiss) + _, err = cache.GetAtHeight("key", 20) + require.ErrorIs(t, err, ErrCacheMiss) + _, err = cache.Get("key") + require.ErrorIs(t, err, ErrCacheMiss) + }) +} + +// TestInMemoryCache_ErrorCases tests various error conditions +func TestInMemoryCache_ErrorCases(t *testing.T) { + t.Run("historical operations on non-historical cache", func(t *testing.T) { + cache := NewInMemoryCache[string]() + + // Attempting historical operations should return error + err := cache.SetAtHeight("key", "value", 10) + require.ErrorIs(t, err, ErrHistoricalModeNotEnabled) + + _, err = cache.GetAtHeight("key", 10) + require.ErrorIs(t, err, ErrHistoricalModeNotEnabled) + }) + + t.Run("zero values", func(t *testing.T) { + cache := NewInMemoryCache[string]() + + // Test with empty key + err := cache.Set("", "value") + require.NoError(t, err) + val, err := cache.Get("") + require.NoError(t, err) + require.Equal(t, "value", val) + + // Test with empty value + err = cache.Set("key", "") + require.NoError(t, err) + val, err = cache.Get("key") + require.NoError(t, err) + require.Equal(t, "", val) + }) +} + +// TestInMemoryCache_ConcurrentAccess tests thread safety of the cache +func TestInMemoryCache_ConcurrentAccess(t *testing.T) { + t.Run("concurrent access non-historical", func(t *testing.T) { + cache := NewInMemoryCache[int]() + const numGoroutines = 10 + const numOperations = 100 + + // Create a context with timeout + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + var wg sync.WaitGroup + wg.Add(numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(routineID int) { + defer wg.Done() + for j := 0; j < numOperations; j++ { + // Check for timeout + select { + case <-ctx.Done(): + t.Errorf("test timed out: %v", ctx.Err()) + return + default: + key := "key" + err := cache.Set(key, j) + require.NoError(t, err) + _, _ = cache.Get(key) + } + } + }(i) + } + + // Wait with timeout + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + + select { + case <-ctx.Done(): + t.Errorf("test timed out waiting for goroutines to complete: %v", ctx.Err()) + case <-done: + // Test completed successfully + } + }) + + t.Run("concurrent access historical", func(t *testing.T) { + cache := NewInMemoryCache[int]( + WithHistoricalMode(100), + ) + const numGoroutines = 10 + const numOpsPerGoRoutine = 100 + + // Create a context with timeout + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + t.Cleanup(cancel) + + var wg sync.WaitGroup + wg.Add(numGoroutines) + + for i := 0; i < numGoroutines; i++ { + go func(routineID int) { + defer wg.Done() + for j := 0; j < numOpsPerGoRoutine; j++ { + // Check for timeout + select { + case <-ctx.Done(): + t.Errorf("test timed out: %v", ctx.Err()) + return + default: + key := "key" + err := cache.SetAtHeight(key, j, int64(j)) + require.NoError(t, err) + _, _ = cache.GetAtHeight(key, int64(j)) + } + } + }(i) + } + + // Wait with timeout + done := make(chan struct{}) + go func() { + wg.Wait() + close(done) + }() + + select { + case <-ctx.Done(): + t.Errorf("test timed out waiting for goroutines to complete: %v", ctx.Err()) + case <-done: + // Test completed successfully + } + }) +} From 0e5b214e1aa51d4e7c9474e3cccb9c6476564d97 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:49:59 +0100 Subject: [PATCH 04/16] chore: add ParamsQuerier interface --- pkg/client/interface.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pkg/client/interface.go b/pkg/client/interface.go index fdbc3ef31..326b73050 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -377,3 +377,18 @@ type HistoricalQueryCache[T any] interface { // SetAtHeight adds or updates a value at a specific height SetAtHeight(key string, value T, height int64) error } + +// ParamsQuerier represents a generic querier for module parameters. +// This interface should be implemented by any module-specific querier +// that needs to access and cache on-chain parameters. +// +// DEV_NOTE: Can't use cosmostypes.Msg instead of any because M +// would be a pointer but Keeper#GetParams() returns a value. 🙄 +type ParamsQuerier[P any] interface { + // GetParams queries the chain for the current module parameters, where + // P is the params type of a given module (e.g. sharedtypes.Params). + GetParams(ctx context.Context) (P, error) + // GetParamsAtHeight returns the parameters as they were at the specified + // height, where M is the params type of a given module (e.g. sharedtypes.Params). + GetParamsAtHeight(ctx context.Context, height int64) (P, error) +} From 4f989a1544e3a6b224900a364f96f77ba8e012d7 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:44:51 +0100 Subject: [PATCH 05/16] feat: add ParamsQuerier --- pkg/client/interface.go | 1 + pkg/client/query/options.go | 57 +++++++++++++ pkg/client/query/paramsquerier.go | 131 ++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+) create mode 100644 pkg/client/query/options.go create mode 100644 pkg/client/query/paramsquerier.go diff --git a/pkg/client/interface.go b/pkg/client/interface.go index 326b73050..535f40b0f 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -1,3 +1,4 @@ +//go:generate mockgen -destination=../../testutil/mockclient/grpc_conn_mock.go -package=mockclient github.com/cosmos/gogoproto/grpc ClientConn //go:generate mockgen -destination=../../testutil/mockclient/events_query_client_mock.go -package=mockclient . Dialer,Connection,EventsQueryClient //go:generate mockgen -destination=../../testutil/mockclient/block_client_mock.go -package=mockclient . Block,BlockClient //go:generate mockgen -destination=../../testutil/mockclient/delegation_client_mock.go -package=mockclient . DelegationClient diff --git a/pkg/client/query/options.go b/pkg/client/query/options.go new file mode 100644 index 000000000..ee110d800 --- /dev/null +++ b/pkg/client/query/options.go @@ -0,0 +1,57 @@ +package query + +import ( + sdkerrors "cosmossdk.io/errors" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + + "github.com/pokt-network/poktroll/pkg/client/query/cache" +) + +// ParamsQuerierConfig holds the configuration for parameter queriers +type ParamsQuerierConfig struct { + // CacheOpts are the options passed to create the params cache + CacheOpts []cache.CacheOption + // ModuleName is used for logging and error context + ModuleName string + // ModuleParamError is the base error type for parameter query errors + ModuleParamError *sdkerrors.Error +} + +// ParamsQuerierOptionFn defines a function that configures a ParamsQuerierConfig +type ParamsQuerierOptionFn func(*ParamsQuerierConfig) + +// DefaultParamsQuerierConfig returns the default configuration for parameter queriers +func DefaultParamsQuerierConfig() *ParamsQuerierConfig { + return &ParamsQuerierConfig{ + CacheOpts: []cache.CacheOption{ + // TODO_IN_THIS_COMMIT: extract to constants. + cache.WithHistoricalMode(100), + // TODO_IN_THIS_COMMIT: reconcile the fact that MaxKeys doesn't apply to historical mode... + cache.WithMaxKeys(1), + // TODO_IN_THIS_COMMIT: extract to constants. + cache.WithEvictionPolicy(cache.FirstInFirstOut), + }, + } +} + +// WithModuleInfo sets the module-specific information for the querier +func WithModuleInfo[R cosmostypes.Msg](moduleName string, moduleParamError *sdkerrors.Error) ParamsQuerierOptionFn { + return func(cfg *ParamsQuerierConfig) { + cfg.ModuleName = moduleName + cfg.ModuleParamError = moduleParamError + } +} + +// WithParamsCacheOptions adds cache configuration options to the params querier +func WithParamsCacheOptions(opts ...cache.CacheOption) ParamsQuerierOptionFn { + return func(cfg *ParamsQuerierConfig) { + cfg.CacheOpts = append(cfg.CacheOpts, opts...) + } +} + +// WithCacheOptions adds cache configuration options to the shared querier +func WithCacheOptions(opts ...cache.CacheOption) ParamsQuerierOptionFn { + return func(cfg *ParamsQuerierConfig) { + cfg.CacheOpts = append(cfg.CacheOpts, opts...) + } +} diff --git a/pkg/client/query/paramsquerier.go b/pkg/client/query/paramsquerier.go new file mode 100644 index 000000000..58d80ebba --- /dev/null +++ b/pkg/client/query/paramsquerier.go @@ -0,0 +1,131 @@ +package query + +import ( + "context" + "errors" + + "cosmossdk.io/depinject" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + gogogrpc "github.com/cosmos/gogoproto/grpc" + + "github.com/pokt-network/poktroll/pkg/client" + "github.com/pokt-network/poktroll/pkg/client/query/cache" + "github.com/pokt-network/poktroll/pkg/polylog" +) + +var _ client.ParamsQuerier[cosmostypes.Msg] = (*cachedParamsQuerier[cosmostypes.Msg, paramsQuerierIface[cosmostypes.Msg]])(nil) + +// paramsQuerierIface is an interface which generated query clients MUST implement +// to be compatible with the cachedParamsQuerier. +// DEV_NOTE: It is mainly required due to syntactic constraints imposed by the generics +// (i.e. otherwise, P here MUST be a value type, and there's no way to express that Q +// (below) SHOULD be the concrete type of P in NewCachedParamsQuerier). +type paramsQuerierIface[P cosmostypes.Msg] interface { + GetParams(context.Context) (P, error) +} + +// NewCachedParamsQuerier creates a new params querier with the given configuration +func NewCachedParamsQuerier[P cosmostypes.Msg, Q paramsQuerierIface[P]]( + deps depinject.Config, + queryClientConstructor func(conn gogogrpc.ClientConn) Q, + opts ...ParamsQuerierOptionFn, +) (_ client.ParamsQuerier[P], err error) { + cfg := DefaultParamsQuerierConfig() + for _, opt := range opts { + opt(cfg) + } + + querier := &cachedParamsQuerier[P, Q]{ + config: cfg, + paramsCache: cache.NewInMemoryCache[P](cfg.CacheOpts...), + } + + if err = depinject.Inject( + deps, + &querier.clientConn, + ); err != nil { + return nil, err + } + + querier.queryClient = queryClientConstructor(querier.clientConn) + + return querier, nil +} + +// TODO_IN_THIS_COMMIT: update godoc... +// cachedParamsQuerier provides common functionality for all params queriers. +// It handles parameter caching and chain querying in a generic way, where +// R is the type of the parameters and Q is the type of the query client. +type cachedParamsQuerier[P cosmostypes.Msg, Q paramsQuerierIface[P]] struct { + clientConn gogogrpc.ClientConn + queryClient Q + paramsCache client.HistoricalQueryCache[P] + config *ParamsQuerierConfig +} + +// TODO_IN_THIS_COMMIT: update godoc... +// GetParams implements the common parameter querying with caching +func (bq *cachedParamsQuerier[P, Q]) GetParams(ctx context.Context) (P, error) { + logger := polylog.Ctx(ctx).With( + "querier", bq.config.ModuleName, + "method", "GetParams", + ) + + // Check cache first + var paramsZero P + cached, err := bq.paramsCache.Get("params") + switch { + case err == nil: + logger.Debug().Msg("cache hit") + return cached, nil + case !errors.Is(err, cache.ErrCacheMiss): + return paramsZero, err + } + + logger.Debug().Msg("cache miss") + + // Query chain on cache miss + params, err := bq.queryClient.GetParams(ctx) + if err != nil { + if bq.config.ModuleParamError != nil { + return paramsZero, bq.config.ModuleParamError.Wrap(err.Error()) + } + return paramsZero, err + } + + // Cache the result before returning + if err = bq.paramsCache.Set("params", params); err != nil { + return paramsZero, err + } + + return params, nil +} + +// TODO_IN_THIS_COMMIT: update godoc... +// GetParamsAtHeight returns parameters as they were at a specific height +func (bq *cachedParamsQuerier[P, Q]) GetParamsAtHeight(ctx context.Context, height int64) (P, error) { + logger := polylog.Ctx(ctx).With( + "querier", bq.config.ModuleName, + "method", "GetParamsAtHeight", + "height", height, + ) + + // Try to get from cache at specific height + cached, err := bq.paramsCache.GetAtHeight("params", height) + switch { + case err == nil: + logger.Debug().Msg("cache hit") + return cached, nil + case !errors.Is(err, cache.ErrCacheMiss): + return cached, err + } + + logger.Debug().Msg("cache miss") + + // TODO_MAINNET(@bryanchriswhite): Implement querying historical params from chain + err = cache.ErrCacheMiss.Wrapf("TODO: on-chain historical data not implemented") + logger.Error().Msgf("%s", err) + + // Meanwhile, return current params as fallback. 😬 + return bq.GetParams(ctx) +} From b4beeed5991077265b45485c71f1b085998f113e Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:45:28 +0100 Subject: [PATCH 06/16] refactor: app query client --- pkg/client/interface.go | 2 ++ pkg/client/query/appquerier.go | 42 +++++++++++++++++++---- x/application/types/expected_keepers.go | 4 +++ x/application/types/query_client.go | 31 +++++++++++++++++ x/proof/types/application_query_client.go | 10 +++++- 5 files changed, 82 insertions(+), 7 deletions(-) create mode 100644 x/application/types/query_client.go diff --git a/pkg/client/interface.go b/pkg/client/interface.go index 535f40b0f..2eb7e2592 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -268,6 +268,8 @@ type AccountQueryClient interface { // ApplicationQueryClient defines an interface that enables the querying of the // on-chain application information type ApplicationQueryClient interface { + ParamsQuerier[*apptypes.Params] + // GetApplication queries the chain for the details of the application provided GetApplication(ctx context.Context, appAddress string) (apptypes.Application, error) diff --git a/pkg/client/query/appquerier.go b/pkg/client/query/appquerier.go index 9477c35f9..09eccadc2 100644 --- a/pkg/client/query/appquerier.go +++ b/pkg/client/query/appquerier.go @@ -4,10 +4,13 @@ import ( "context" "cosmossdk.io/depinject" - grpc "github.com/cosmos/gogoproto/grpc" + cosmostypes "github.com/cosmos/cosmos-sdk/types" + accounttypes "github.com/cosmos/cosmos-sdk/x/auth/types" + gogogrpc "github.com/cosmos/gogoproto/grpc" "github.com/pokt-network/poktroll/pkg/client" apptypes "github.com/pokt-network/poktroll/x/application/types" + sharedtypes "github.com/pokt-network/poktroll/x/shared/types" ) var _ client.ApplicationQueryClient = (*appQuerier)(nil) @@ -16,7 +19,9 @@ var _ client.ApplicationQueryClient = (*appQuerier)(nil) // querying of on-chain application information through a single exposed method // which returns an apptypes.Application interface type appQuerier struct { - clientConn grpc.ClientConn + client.ParamsQuerier[*apptypes.Params] + + clientConn gogogrpc.ClientConn applicationQuerier apptypes.QueryClient } @@ -24,11 +29,30 @@ type appQuerier struct { // by injecting the dependecies provided by the depinject.Config // // Required dependencies: -// - clientCtx -func NewApplicationQuerier(deps depinject.Config) (client.ApplicationQueryClient, error) { - aq := &appQuerier{} +// - clientCtx (gogogrpc.ClientConn) +func NewApplicationQuerier( + deps depinject.Config, + opts ...ParamsQuerierOptionFn, +) (client.ApplicationQueryClient, error) { + cfg := DefaultParamsQuerierConfig() + for _, opt := range opts { + opt(cfg) + } - if err := depinject.Inject( + paramsQuerier, err := NewCachedParamsQuerier[*apptypes.Params, apptypes.ApplicationQueryClient]( + deps, apptypes.NewAppQueryClient, + WithModuleInfo[*sharedtypes.Params](sharedtypes.ModuleName, sharedtypes.ErrSharedParamInvalid), + WithParamsCacheOptions(cfg.CacheOpts...), + ) + if err != nil { + return nil, err + } + + aq := &appQuerier{ + ParamsQuerier: paramsQuerier, + } + + if err = depinject.Inject( deps, &aq.clientConn, ); err != nil { @@ -45,6 +69,12 @@ func (aq *appQuerier) GetApplication( ctx context.Context, appAddress string, ) (apptypes.Application, error) { + defer client.AllQueriesTotalCounter.With( + "method", "account", + "client_type", "account", + "msg_type", cosmostypes.MsgTypeURL(new(accounttypes.QueryAccountRequest)), + ).Add(1) + req := apptypes.QueryGetApplicationRequest{Address: appAddress} res, err := aq.applicationQuerier.Application(ctx, &req) if err != nil { diff --git a/x/application/types/expected_keepers.go b/x/application/types/expected_keepers.go index 3c908df63..55756ad3e 100644 --- a/x/application/types/expected_keepers.go +++ b/x/application/types/expected_keepers.go @@ -37,3 +37,7 @@ type SharedKeeper interface { GetParams(ctx context.Context) sharedtypes.Params GetSessionEndHeight(ctx context.Context, queryHeight int64) int64 } + +type ApplicationKeeper interface { + GetParams(ctx context.Context) Params +} diff --git a/x/application/types/query_client.go b/x/application/types/query_client.go new file mode 100644 index 000000000..682da5594 --- /dev/null +++ b/x/application/types/query_client.go @@ -0,0 +1,31 @@ +package types + +import ( + "context" + + gogogrpc "github.com/cosmos/gogoproto/grpc" +) + +// TODO_IN_THIS_COMMIT: godoc... +type ApplicationQueryClient interface { + QueryClient + + GetParams(context.Context) (*Params, error) +} + +// TODO_IN_THIS_COMMIT: godoc... +func NewAppQueryClient(conn gogogrpc.ClientConn) ApplicationQueryClient { + return NewQueryClient(conn).(ApplicationQueryClient) +} + +// TODO_IN_THIS_COMMIT: investigate generalization... +// TODO_IN_THIS_COMMIT: godoc... +func (c *queryClient) GetParams(ctx context.Context) (*Params, error) { + res, err := c.Params(ctx, &QueryParamsRequest{}) + if err != nil { + return nil, err + } + + params := res.GetParams() + return ¶ms, nil +} diff --git a/x/proof/types/application_query_client.go b/x/proof/types/application_query_client.go index 1cd887314..7c6d09997 100644 --- a/x/proof/types/application_query_client.go +++ b/x/proof/types/application_query_client.go @@ -5,6 +5,7 @@ import ( "github.com/pokt-network/poktroll/pkg/client" apptypes "github.com/pokt-network/poktroll/x/application/types" + sharedkeeper "github.com/pokt-network/poktroll/x/shared/keeper" ) var _ client.ApplicationQueryClient = (*AppKeeperQueryClient)(nil) @@ -13,6 +14,8 @@ var _ client.ApplicationQueryClient = (*AppKeeperQueryClient)(nil) // It does not rely on the QueryClient, and therefore does not make any // network requests as in the off-chain implementation. type AppKeeperQueryClient struct { + *sharedkeeper.KeeperParamsQuerier[apptypes.Params, ApplicationKeeper] + keeper ApplicationKeeper } @@ -22,7 +25,12 @@ type AppKeeperQueryClient struct { // has delegated its signing power to. // It should be injected into the RingClient when initialized from within the a keeper. func NewAppKeeperQueryClient(appKeeper ApplicationKeeper) client.ApplicationQueryClient { - return &AppKeeperQueryClient{keeper: appKeeper} + keeperParamsQuerier := sharedkeeper.NewKeeperParamsQuerier[apptypes.Params](appKeeper) + + return &AppKeeperQueryClient{ + keeper: appKeeper, + KeeperParamsQuerier: keeperParamsQuerier, + } } // GetApplication returns the application corresponding to the given address. From 12e3dd3128ae8a890faa777e49d0448e6b8dae61 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:44:29 +0100 Subject: [PATCH 07/16] fix: application transfer integration test --- .../application/application_transfer_test.go | 22 +++++++++---------- testutil/integration/suites/application.go | 2 -- testutil/integration/suites/gateway.go | 2 -- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/tests/integration/application/application_transfer_test.go b/tests/integration/application/application_transfer_test.go index d9b842fb5..a4c76d4ba 100644 --- a/tests/integration/application/application_transfer_test.go +++ b/tests/integration/application/application_transfer_test.go @@ -1,5 +1,3 @@ -//go:build integration - package application import ( @@ -20,11 +18,10 @@ import ( var ( appFundAmount = int64(100000000) - stakeAmount = int64(100) + stakeAmount = int64(10000000) service1Id = "svc1" service2Id = "svc2" - service3Id = "svc3" ) type appTransferTestSuite struct { @@ -66,8 +63,8 @@ func (s *appTransferTestSuite) SetupTest() { // Stake app1 and app2. s.setupStakeApps(map[string][]string{ - s.app1: {service1Id, service3Id}, - s.app2: {service1Id, service2Id}, + s.app1: {service1Id}, + s.app2: {service2Id}, }) // Delegate app 1 to gateway 1 and 3 and app 2 to gateways 1 and 2. @@ -231,8 +228,8 @@ func (s *appTransferTestSuite) TestMultipleSourceToSameNonexistentDestinationMer require.EqualValues(s.T(), expectedPendingTransfer, pendingTransfer) // Query and assert application pending transfer field updated in the store. - foundSrcApp, err := s.GetAppQueryClient().GetApplication(s.SdkCtx(), expectedSrcBech32) - require.NoError(s.T(), err) + foundSrcApp, srcAppErr := s.GetAppQueryClient().GetApplication(s.SdkCtx(), expectedSrcBech32) + require.NoError(s.T(), srcAppErr) require.EqualValues(s.T(), expectedPendingTransfer, foundSrcApp.GetPendingTransfer()) // Assert that the "message" type event (tx result event) is observed which @@ -299,7 +296,6 @@ func (s *appTransferTestSuite) TestMultipleSourceToSameNonexistentDestinationMer expectedApp3ServiceIds := []string{ service1Id, service2Id, - service3Id, } for _, serviceId := range expectedApp3ServiceIds { require.Contains(s.T(), @@ -477,11 +473,15 @@ func (s *appTransferTestSuite) shouldObserveTransferEndEvent( return false } - eventSrcAddr, hasSrcAddr := events.GetAttributeValue(event, "source_address") + _, hasSrcAddr := events.GetAttributeValue(event, "source_address") require.True(s.T(), hasSrcAddr) - return eventSrcAddr == expectedDstApp.GetAddress() + eventDstAddr, hasDstAddr := events.GetAttributeValue(event, "destination_address") + require.True(s.T(), hasDstAddr) + + return eventDstAddr == expectedDstApp.GetAddress() }) + require.NotNil(s.T(), targetTransferEndEvent) evtSrcAddr, hasSrcAddrAttr := events.GetAttributeValue(targetTransferEndEvent, "source_address") diff --git a/testutil/integration/suites/application.go b/testutil/integration/suites/application.go index aa2537196..86b22fccf 100644 --- a/testutil/integration/suites/application.go +++ b/testutil/integration/suites/application.go @@ -1,5 +1,3 @@ -//go:build integration - package suites import ( diff --git a/testutil/integration/suites/gateway.go b/testutil/integration/suites/gateway.go index 22845a44f..f69673782 100644 --- a/testutil/integration/suites/gateway.go +++ b/testutil/integration/suites/gateway.go @@ -1,5 +1,3 @@ -//go:build integration - package suites import ( From d7e6a1049b47967d485fee2c09cabc29a0f385ce Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:45:34 +0100 Subject: [PATCH 08/16] refactor: proof query client --- pkg/client/interface.go | 5 ++-- pkg/client/query/proofquerier.go | 50 ++++++++++++++++++++----------- x/proof/types/expected_keepers.go | 1 + x/proof/types/query_client.go | 31 +++++++++++++++++++ 4 files changed, 68 insertions(+), 19 deletions(-) create mode 100644 x/proof/types/query_client.go diff --git a/pkg/client/interface.go b/pkg/client/interface.go index 2eb7e2592..614b8a460 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -336,6 +336,8 @@ type BlockQueryClient interface { // protobuf message. Since the generated go types don't include interface types, this // is necessary to prevent dependency cycles. type ProofParams interface { + cosmostypes.Msg + GetProofRequestProbability() float64 GetProofRequirementThreshold() *cosmostypes.Coin GetProofMissingPenalty() *cosmostypes.Coin @@ -345,8 +347,7 @@ type ProofParams interface { // ProofQueryClient defines an interface that enables the querying of the // on-chain proof module params. type ProofQueryClient interface { - // GetParams queries the chain for the current shared module parameters. - GetParams(ctx context.Context) (ProofParams, error) + ParamsQuerier[ProofParams] } // ServiceQueryClient defines an interface that enables the querying of the diff --git a/pkg/client/query/proofquerier.go b/pkg/client/query/proofquerier.go index 30c2984cd..78dbba230 100644 --- a/pkg/client/query/proofquerier.go +++ b/pkg/client/query/proofquerier.go @@ -1,8 +1,6 @@ package query import ( - "context" - "cosmossdk.io/depinject" "github.com/cosmos/gogoproto/grpc" @@ -10,9 +8,17 @@ import ( prooftypes "github.com/pokt-network/poktroll/x/proof/types" ) +// TODO_IN_THIS_COMMIT: comment explaining why we can't use client.ProofQueryClient; +// tl;dr, it defines ian interface for ProofParams to avoid a dependency cycle +// (i.e. instead of importing prooftypes). +// +//var _ prooftypes.ProofQueryClient = (*proofQuerier)(nil) + // proofQuerier is a wrapper around the prooftypes.QueryClient that enables the // querying of on-chain proof module params. type proofQuerier struct { + client.ParamsQuerier[*prooftypes.Params] + clientConn grpc.ClientConn proofQuerier prooftypes.QueryClient } @@ -22,10 +28,32 @@ type proofQuerier struct { // // Required dependencies: // - grpc.ClientConn -func NewProofQuerier(deps depinject.Config) (client.ProofQueryClient, error) { - querier := &proofQuerier{} +func NewProofQuerier( + deps depinject.Config, + paramsQuerierOpts ...ParamsQuerierOptionFn, + // TODO_IN_THIS_COMMIT: comment explaining why we can't use client.ProofQueryClient; + // tl;dr, it defines ian interface for ProofParams to avoid a dependency cycle + // (i.e. instead of importing prooftypes). +) (paramsQuerierIface[*prooftypes.Params], error) { + paramsQuerierCfg := DefaultParamsQuerierConfig() + for _, opt := range paramsQuerierOpts { + opt(paramsQuerierCfg) + } + + paramsQuerier, err := NewCachedParamsQuerier[*prooftypes.Params, prooftypes.ProofQueryClient]( + deps, prooftypes.NewProofQueryClient, + WithModuleInfo[*prooftypes.Params](prooftypes.ModuleName, prooftypes.ErrProofParamInvalid), + WithParamsCacheOptions(paramsQuerierCfg.CacheOpts...), + ) + if err != nil { + return nil, err + } + + querier := &proofQuerier{ + ParamsQuerier: paramsQuerier, + } - if err := depinject.Inject( + if err = depinject.Inject( deps, &querier.clientConn, ); err != nil { @@ -36,15 +64,3 @@ func NewProofQuerier(deps depinject.Config) (client.ProofQueryClient, error) { return querier, nil } - -// GetParams queries the chain for the current proof module parameters. -func (pq *proofQuerier) GetParams( - ctx context.Context, -) (client.ProofParams, error) { - req := &prooftypes.QueryParamsRequest{} - res, err := pq.proofQuerier.Params(ctx, req) - if err != nil { - return nil, err - } - return &res.Params, nil -} diff --git a/x/proof/types/expected_keepers.go b/x/proof/types/expected_keepers.go index 9d1fd765e..22bfc9522 100644 --- a/x/proof/types/expected_keepers.go +++ b/x/proof/types/expected_keepers.go @@ -49,6 +49,7 @@ type ApplicationKeeper interface { GetApplication(ctx context.Context, address string) (app apptypes.Application, found bool) GetAllApplications(ctx context.Context) []apptypes.Application SetApplication(context.Context, apptypes.Application) + GetParams(ctx context.Context) apptypes.Params } // SharedKeeper defines the expected interface needed to retrieve shared information. diff --git a/x/proof/types/query_client.go b/x/proof/types/query_client.go new file mode 100644 index 000000000..a94986e8c --- /dev/null +++ b/x/proof/types/query_client.go @@ -0,0 +1,31 @@ +package types + +import ( + "context" + + gogogrpc "github.com/cosmos/gogoproto/grpc" +) + +// TODO_IN_THIS_COMMIT: godoc... +type ProofQueryClient interface { + QueryClient + + GetParams(context.Context) (*Params, error) +} + +// TODO_IN_THIS_COMMIT: godoc... +func NewProofQueryClient(conn gogogrpc.ClientConn) ProofQueryClient { + return NewQueryClient(conn).(ProofQueryClient) +} + +// TODO_IN_THIS_COMMIT: investigate generalization... +// TODO_IN_THIS_COMMIT: godoc... +func (c *queryClient) GetParams(ctx context.Context) (*Params, error) { + res, err := c.Params(ctx, &QueryParamsRequest{}) + if err != nil { + return nil, err + } + + params := res.GetParams() + return ¶ms, nil +} From f25bcc5a1686327ca1692cbcd44ab86550d058ff Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:45:38 +0100 Subject: [PATCH 09/16] refactor: service query client --- pkg/client/interface.go | 2 ++ pkg/client/query/servicequerier.go | 43 +++++++++++++++++++++++------- x/service/types/query_client.go | 31 +++++++++++++++++++++ 3 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 x/service/types/query_client.go diff --git a/pkg/client/interface.go b/pkg/client/interface.go index 614b8a460..3b16197d3 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -353,6 +353,8 @@ type ProofQueryClient interface { // ServiceQueryClient defines an interface that enables the querying of the // on-chain service information type ServiceQueryClient interface { + ParamsQuerier[*servicetypes.Params] + // GetService queries the chain for the details of the service provided GetService(ctx context.Context, serviceId string) (sharedtypes.Service, error) GetServiceRelayDifficulty(ctx context.Context, serviceId string) (servicetypes.RelayMiningDifficulty, error) diff --git a/pkg/client/query/servicequerier.go b/pkg/client/query/servicequerier.go index cb0629681..84beed5ad 100644 --- a/pkg/client/query/servicequerier.go +++ b/pkg/client/query/servicequerier.go @@ -4,7 +4,7 @@ import ( "context" "cosmossdk.io/depinject" - "github.com/cosmos/gogoproto/grpc" + gogogrpc "github.com/cosmos/gogoproto/grpc" "github.com/pokt-network/poktroll/pkg/client" servicetypes "github.com/pokt-network/poktroll/x/service/types" @@ -17,7 +17,9 @@ var _ client.ServiceQueryClient = (*serviceQuerier)(nil) // querying of on-chain service information through a single exposed method // which returns a sharedtypes.Service struct type serviceQuerier struct { - clientConn grpc.ClientConn + client.ParamsQuerier[*servicetypes.Params] + + clientConn gogogrpc.ClientConn serviceQuerier servicetypes.QueryClient } @@ -25,20 +27,39 @@ type serviceQuerier struct { // injecting the dependecies provided by the depinject.Config. // // Required dependencies: -// - clientCtx (grpc.ClientConn) -func NewServiceQuerier(deps depinject.Config) (client.ServiceQueryClient, error) { - servq := &serviceQuerier{} +// - clientCtx (gogogrpc.ClientConn) +func NewServiceQuerier( + deps depinject.Config, + paramsQuerierOpts ...ParamsQuerierOptionFn, +) (client.ServiceQueryClient, error) { + paramsQuerierCfg := DefaultParamsQuerierConfig() + for _, opt := range paramsQuerierOpts { + opt(paramsQuerierCfg) + } + + paramsQuerier, err := NewCachedParamsQuerier[*servicetypes.Params, servicetypes.ServiceQueryClient]( + deps, servicetypes.NewServiceQueryClient, + WithModuleInfo[*servicetypes.Params](servicetypes.ModuleName, servicetypes.ErrServiceParamInvalid), + WithParamsCacheOptions(paramsQuerierCfg.CacheOpts...), + ) + if err != nil { + return nil, err + } + + querier := &serviceQuerier{ + ParamsQuerier: paramsQuerier, + } - if err := depinject.Inject( + if err = depinject.Inject( deps, - &servq.clientConn, + &querier.clientConn, ); err != nil { return nil, err } - servq.serviceQuerier = servicetypes.NewQueryClient(servq.clientConn) + querier.serviceQuerier = servicetypes.NewQueryClient(querier.clientConn) - return servq, nil + return querier, nil } // GetService returns a sharedtypes.Service struct for a given serviceId. @@ -51,6 +72,8 @@ func (servq *serviceQuerier) GetService( Id: serviceId, } + // TODO_IN_THIS_COMMIT: historically cache services... + res, err := servq.serviceQuerier.Service(ctx, req) if err != nil { return sharedtypes.Service{}, ErrQueryRetrieveService.Wrapf( @@ -71,6 +94,8 @@ func (servq *serviceQuerier) GetServiceRelayDifficulty( ServiceId: serviceId, } + // TODO_IN_THIS_COMMIT: historically cache relay mining difficulties... + res, err := servq.serviceQuerier.RelayMiningDifficulty(ctx, req) if err != nil { return servicetypes.RelayMiningDifficulty{}, err diff --git a/x/service/types/query_client.go b/x/service/types/query_client.go new file mode 100644 index 000000000..d321cee0c --- /dev/null +++ b/x/service/types/query_client.go @@ -0,0 +1,31 @@ +package types + +import ( + "context" + + gogogrpc "github.com/cosmos/gogoproto/grpc" +) + +// TODO_IN_THIS_COMMIT: godoc... +type ServiceQueryClient interface { + QueryClient + + GetParams(context.Context) (*Params, error) +} + +// TODO_IN_THIS_COMMIT: godoc... +func NewServiceQueryClient(conn gogogrpc.ClientConn) ServiceQueryClient { + return NewQueryClient(conn).(ServiceQueryClient) +} + +// TODO_IN_THIS_COMMIT: investigate generalization... +// TODO_IN_THIS_COMMIT: godoc... +func (c *queryClient) GetParams(ctx context.Context) (*Params, error) { + res, err := c.Params(ctx, &QueryParamsRequest{}) + if err != nil { + return nil, err + } + + params := res.GetParams() + return ¶ms, nil +} From c8af216a9625fc5008f3f2a06b0304dd22baf6f6 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:45:42 +0100 Subject: [PATCH 10/16] refactor: session query client --- pkg/client/interface.go | 5 +- pkg/client/query/sessionquerier.go | 91 ++++++++++++++++++++++++------ x/session/types/query_client.go | 31 ++++++++++ 3 files changed, 106 insertions(+), 21 deletions(-) create mode 100644 x/session/types/query_client.go diff --git a/pkg/client/interface.go b/pkg/client/interface.go index 3b16197d3..85b343bc5 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -287,6 +287,8 @@ type SupplierQueryClient interface { // SessionQueryClient defines an interface that enables the querying of the // on-chain session information type SessionQueryClient interface { + ParamsQuerier[*sessiontypes.Params] + // GetSession queries the chain for the details of the session provided GetSession( ctx context.Context, @@ -294,9 +296,6 @@ type SessionQueryClient interface { serviceId string, blockHeight int64, ) (*sessiontypes.Session, error) - - // GetParams queries the chain for the session module parameters. - GetParams(ctx context.Context) (*sessiontypes.Params, error) } // SharedQueryClient defines an interface that enables the querying of the diff --git a/pkg/client/query/sessionquerier.go b/pkg/client/query/sessionquerier.go index 8553e3313..77804a9c5 100644 --- a/pkg/client/query/sessionquerier.go +++ b/pkg/client/query/sessionquerier.go @@ -2,11 +2,16 @@ package query import ( "context" + "errors" + "fmt" + "time" "cosmossdk.io/depinject" "github.com/cosmos/gogoproto/grpc" "github.com/pokt-network/poktroll/pkg/client" + "github.com/pokt-network/poktroll/pkg/client/query/cache" + "github.com/pokt-network/poktroll/pkg/polylog" sessiontypes "github.com/pokt-network/poktroll/x/session/types" ) @@ -16,8 +21,11 @@ var _ client.SessionQueryClient = (*sessionQuerier)(nil) // querying of on-chain session information through a single exposed method // which returns an sessiontypes.Session struct type sessionQuerier struct { + client.ParamsQuerier[*sessiontypes.Params] + clientConn grpc.ClientConn sessionQuerier sessiontypes.QueryClient + sessionCache client.QueryCache[*sessiontypes.Session] } // NewSessionQuerier returns a new instance of a client.SessionQueryClient by @@ -25,50 +33,97 @@ type sessionQuerier struct { // // Required dependencies: // - clientCtx (grpc.ClientConn) -func NewSessionQuerier(deps depinject.Config) (client.SessionQueryClient, error) { - sessq := &sessionQuerier{} +func NewSessionQuerier( + deps depinject.Config, + paramsQuerierOpts ...ParamsQuerierOptionFn, +) (client.SessionQueryClient, error) { + paramsQuerierCfg := DefaultParamsQuerierConfig() + for _, opt := range paramsQuerierOpts { + opt(paramsQuerierCfg) + } + + paramsQuerier, err := NewCachedParamsQuerier[*sessiontypes.Params, sessiontypes.SessionQueryClient]( + deps, sessiontypes.NewSessionQueryClient, + WithModuleInfo[*sessiontypes.Params](sessiontypes.ModuleName, sessiontypes.ErrSessionParamInvalid), + WithParamsCacheOptions(paramsQuerierCfg.CacheOpts...), + ) + if err != nil { + return nil, err + } + + // Initialize session cache with historical mode since sessions can vary by height + // TODO_IN_THIS_COMMIT: consider supporting multiple cache configs per query client. + sessionCache := cache.NewInMemoryCache[*sessiontypes.Session]( + // TODO_IN_THIS_COMMIT: extract to an option fn. + cache.WithMaxKeys(100), + cache.WithEvictionPolicy(cache.LeastRecentlyUsed), + // TODO_IN_THIS_COMMIT: extract to a constant. + cache.WithTTL(time.Hour*3), + ) - if err := depinject.Inject( + querier := &sessionQuerier{ + ParamsQuerier: paramsQuerier, + sessionCache: sessionCache, + } + + if err = depinject.Inject( deps, - &sessq.clientConn, + &querier.clientConn, ); err != nil { return nil, err } - sessq.sessionQuerier = sessiontypes.NewQueryClient(sessq.clientConn) + querier.sessionQuerier = sessiontypes.NewQueryClient(querier.clientConn) - return sessq, nil + return querier, nil } // GetSession returns an sessiontypes.Session struct for a given appAddress, // serviceId and blockHeight. It implements the SessionQueryClient#GetSession function. -func (sessq *sessionQuerier) GetSession( +func (sq *sessionQuerier) GetSession( ctx context.Context, appAddress string, serviceId string, blockHeight int64, ) (*sessiontypes.Session, error) { + logger := polylog.Ctx(ctx).With( + "querier", "session", + "method", "GetSession", + ) + + // Create cache key from query parameters + cacheKey := fmt.Sprintf("%s:%s:%d", appAddress, serviceId, blockHeight) + + // Check cache first + cached, err := sq.sessionCache.Get(cacheKey) + switch { + case err == nil: + logger.Debug().Msg("cache hit") + return cached, nil + case !errors.Is(err, cache.ErrCacheMiss): + return nil, err + default: + logger.Debug().Msg("cache miss") + } + + // If not cached, query the chain req := &sessiontypes.QueryGetSessionRequest{ ApplicationAddress: appAddress, ServiceId: serviceId, BlockHeight: blockHeight, } - res, err := sessq.sessionQuerier.GetSession(ctx, req) + res, err := sq.sessionQuerier.GetSession(ctx, req) if err != nil { return nil, ErrQueryRetrieveSession.Wrapf( - "address: %s; serviceId: %s; block height: %d; error: [%v]", + "address: %s; serviceId: %s; block height: %d; error: %s", appAddress, serviceId, blockHeight, err, ) } - return res.Session, nil -} -// GetParams queries & returns the session module on-chain parameters. -func (sessq *sessionQuerier) GetParams(ctx context.Context) (*sessiontypes.Params, error) { - req := &sessiontypes.QueryParamsRequest{} - res, err := sessq.sessionQuerier.Params(ctx, req) - if err != nil { - return nil, ErrQuerySessionParams.Wrapf("[%v]", err) + // Cache the result before returning + if err = sq.sessionCache.Set(cacheKey, res.Session); err != nil { + return nil, err } - return &res.Params, nil + + return res.Session, nil } diff --git a/x/session/types/query_client.go b/x/session/types/query_client.go new file mode 100644 index 000000000..95a03921a --- /dev/null +++ b/x/session/types/query_client.go @@ -0,0 +1,31 @@ +package types + +import ( + "context" + + gogogrpc "github.com/cosmos/gogoproto/grpc" +) + +// TODO_IN_THIS_COMMIT: godoc... +type SessionQueryClient interface { + QueryClient + + GetParams(context.Context) (*Params, error) +} + +// TODO_IN_THIS_COMMIT: godoc... +func NewSessionQueryClient(conn gogogrpc.ClientConn) SessionQueryClient { + return NewQueryClient(conn).(SessionQueryClient) +} + +// TODO_IN_THIS_COMMIT: investigate generalization... +// TODO_IN_THIS_COMMIT: godoc... +func (c *queryClient) GetParams(ctx context.Context) (*Params, error) { + res, err := c.Params(ctx, &QueryParamsRequest{}) + if err != nil { + return nil, err + } + + params := res.GetParams() + return ¶ms, nil +} From 3d98b18f344b6645a8cf9c902b00e53e25611d05 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:45:47 +0100 Subject: [PATCH 11/16] refactor: shared query client --- pkg/client/interface.go | 4 +- pkg/client/query/sharedquerier.go | 66 +++++++------ pkg/client/query/sharedquerier_test.go | 122 +++++++++++++++++++++++++ x/proof/types/shared_query_client.go | 58 ++++++++---- x/shared/keeper/params_query_client.go | 83 +++++++++++++++++ x/shared/types/query_client.go | 31 +++++++ 6 files changed, 316 insertions(+), 48 deletions(-) create mode 100644 pkg/client/query/sharedquerier_test.go create mode 100644 x/shared/keeper/params_query_client.go create mode 100644 x/shared/types/query_client.go diff --git a/pkg/client/interface.go b/pkg/client/interface.go index 85b343bc5..8b6e26efb 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -301,8 +301,8 @@ type SessionQueryClient interface { // SharedQueryClient defines an interface that enables the querying of the // on-chain shared module params. type SharedQueryClient interface { - // GetParams queries the chain for the current shared module parameters. - GetParams(ctx context.Context) (*sharedtypes.Params, error) + ParamsQuerier[*sharedtypes.Params] + // GetSessionGracePeriodEndHeight returns the block height at which the grace period // for the session that includes queryHeight elapses. // The grace period is the number of blocks after the session ends during which relays diff --git a/pkg/client/query/sharedquerier.go b/pkg/client/query/sharedquerier.go index 06e0ed90a..1970778e2 100644 --- a/pkg/client/query/sharedquerier.go +++ b/pkg/client/query/sharedquerier.go @@ -13,48 +13,54 @@ import ( var _ client.SharedQueryClient = (*sharedQuerier)(nil) // sharedQuerier is a wrapper around the sharedtypes.QueryClient that enables the -// querying of on-chain shared information through a single exposed method -// which returns an sharedtypes.Session struct +// querying of on-chain shared information type sharedQuerier struct { + client.ParamsQuerier[*sharedtypes.Params] + clientConn grpc.ClientConn sharedQuerier sharedtypes.QueryClient blockQuerier client.BlockQueryClient } // NewSharedQuerier returns a new instance of a client.SharedQueryClient by -// injecting the dependecies provided by the depinject.Config. +// injecting the dependencies provided by the depinject.Config. // // Required dependencies: // - clientCtx (grpc.ClientConn) // - client.BlockQueryClient -func NewSharedQuerier(deps depinject.Config) (client.SharedQueryClient, error) { - querier := &sharedQuerier{} +func NewSharedQuerier( + deps depinject.Config, + paramsQuerierOpts ...ParamsQuerierOptionFn, +) (client.SharedQueryClient, error) { + paramsQuerierCfg := DefaultParamsQuerierConfig() + for _, opt := range paramsQuerierOpts { + opt(paramsQuerierCfg) + } + + paramsQuerier, err := NewCachedParamsQuerier[*sharedtypes.Params, sharedtypes.SharedQueryClient]( + deps, sharedtypes.NewSharedQueryClient, + WithModuleInfo[*sharedtypes.Params](sharedtypes.ModuleName, sharedtypes.ErrSharedParamInvalid), + WithParamsCacheOptions(paramsQuerierCfg.CacheOpts...), + ) + if err != nil { + return nil, err + } - if err := depinject.Inject( + sq := &sharedQuerier{ + ParamsQuerier: paramsQuerier, + } + + if err = depinject.Inject( deps, - &querier.clientConn, - &querier.blockQuerier, + &sq.clientConn, + &sq.blockQuerier, ); err != nil { return nil, err } - querier.sharedQuerier = sharedtypes.NewQueryClient(querier.clientConn) + sq.sharedQuerier = sharedtypes.NewQueryClient(sq.clientConn) - return querier, nil -} - -// GetParams queries & returns the shared module on-chain parameters. -// -// TODO_TECHDEBT(#543): We don't really want to have to query the params for every method call. -// Once `ModuleParamsClient` is implemented, use its replay observable's `#Last()` method -// to get the most recently (asynchronously) observed (and cached) value. -func (sq *sharedQuerier) GetParams(ctx context.Context) (*sharedtypes.Params, error) { - req := &sharedtypes.QueryParamsRequest{} - res, err := sq.sharedQuerier.Params(ctx, req) - if err != nil { - return nil, ErrQuerySessionParams.Wrapf("[%v]", err) - } - return &res.Params, nil + return sq, nil } // GetClaimWindowOpenHeight returns the block height at which the claim window of @@ -118,7 +124,11 @@ func (sq *sharedQuerier) GetSessionGracePeriodEndHeight( // to get the most recently (asynchronously) observed (and cached) value. // TODO_MAINNET(@bryanchriswhite, #543): We also don't really want to use the current value of the params. // Instead, we should be using the value that the params had for the session which includes queryHeight. -func (sq *sharedQuerier) GetEarliestSupplierClaimCommitHeight(ctx context.Context, queryHeight int64, supplierOperatorAddr string) (int64, error) { +func (sq *sharedQuerier) GetEarliestSupplierClaimCommitHeight( + ctx context.Context, + queryHeight int64, + supplierOperatorAddr string, +) (int64, error) { sharedParams, err := sq.GetParams(ctx) if err != nil { return 0, err @@ -151,7 +161,11 @@ func (sq *sharedQuerier) GetEarliestSupplierClaimCommitHeight(ctx context.Contex // to get the most recently (asynchronously) observed (and cached) value. // TODO_MAINNET(@bryanchriswhite, #543): We also don't really want to use the current value of the params. // Instead, we should be using the value that the params had for the session which includes queryHeight. -func (sq *sharedQuerier) GetEarliestSupplierProofCommitHeight(ctx context.Context, queryHeight int64, supplierOperatorAddr string) (int64, error) { +func (sq *sharedQuerier) GetEarliestSupplierProofCommitHeight( + ctx context.Context, + queryHeight int64, + supplierOperatorAddr string, +) (int64, error) { sharedParams, err := sq.GetParams(ctx) if err != nil { return 0, err diff --git a/pkg/client/query/sharedquerier_test.go b/pkg/client/query/sharedquerier_test.go new file mode 100644 index 000000000..bde227919 --- /dev/null +++ b/pkg/client/query/sharedquerier_test.go @@ -0,0 +1,122 @@ +package query_test + +import ( + "context" + "testing" + "time" + + "cosmossdk.io/depinject" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + "google.golang.org/grpc" + + "github.com/pokt-network/poktroll/pkg/client" + "github.com/pokt-network/poktroll/pkg/client/query" + "github.com/pokt-network/poktroll/pkg/client/query/cache" + _ "github.com/pokt-network/poktroll/pkg/polylog/polyzero" + "github.com/pokt-network/poktroll/testutil/mockclient" + sharedtypes "github.com/pokt-network/poktroll/x/shared/types" +) + +type SharedQuerierTestSuite struct { + suite.Suite + ctrl *gomock.Controller + ctx context.Context + querier client.SharedQueryClient + mockConn *mockclient.MockClientConn + mockBlock *mockclient.MockCometRPC + TTL time.Duration +} + +func TestSharedQuerierSuite(t *testing.T) { + suite.Run(t, new(SharedQuerierTestSuite)) +} + +func (s *SharedQuerierTestSuite) SetupTest() { + s.ctrl = gomock.NewController(s.T()) + s.ctx = context.Background() + s.mockConn = mockclient.NewMockClientConn(s.ctrl) + s.mockBlock = mockclient.NewMockCometRPC(s.ctrl) + s.TTL = 200 * time.Millisecond + + deps := depinject.Supply(s.mockConn, s.mockBlock) + + // Create querier with test-specific cache settings + querier, err := query.NewSharedQuerier(deps, + query.WithCacheOptions( + cache.WithTTL(s.TTL), + cache.WithHistoricalMode(100), + ), + ) + require.NoError(s.T(), err) + require.NotNil(s.T(), querier) + + s.querier = querier +} + +func (s *SharedQuerierTestSuite) TearDownTest() { + s.ctrl.Finish() +} + +func (s *SharedQuerierTestSuite) TestRetrievesAndCachesParamsValues() { + multiplier := uint64(1000) + + // First query - params with multiplier 1000 + s.expectMockConnToReturnParamsWithMultiplierOnce(multiplier) + + // Initial query should fetch from chain. + params1, err := s.querier.GetParams(s.ctx) + s.NoError(err) + s.Equal(multiplier, params1.ComputeUnitsToTokensMultiplier) + + // Second query - should use cache, no mock expectation needed, this is + // asserted here due to the mock expectation calling Times(1). + params2, err := s.querier.GetParams(s.ctx) + s.NoError(err) + s.Equal(multiplier, params2.ComputeUnitsToTokensMultiplier) + + // Third query after 90% of the TTL - should still use cache. + time.Sleep(time.Duration(float64(s.TTL) * .9)) + params3, err := s.querier.GetParams(s.ctx) + s.NoError(err) + s.Equal(multiplier, params3.ComputeUnitsToTokensMultiplier) +} + +func (s *SharedQuerierTestSuite) TestHandlesCacheExpiration() { + // First query + s.expectMockConnToReturnParamsWithMultiplierOnce(2000) + + params1, err := s.querier.GetParams(s.ctx) + s.NoError(err) + s.Equal(uint64(2000), params1.ComputeUnitsToTokensMultiplier) + + // Wait for cache to expire + time.Sleep(300 * time.Millisecond) + + // Next query should hit the chain again + s.expectMockConnToReturnParamsWithMultiplierOnce(3000) + + params2, err := s.querier.GetParams(s.ctx) + s.NoError(err) + s.Equal(uint64(3000), params2.ComputeUnitsToTokensMultiplier) +} + +func (s *SharedQuerierTestSuite) expectMockConnToReturnParamsWithMultiplierOnce(multiplier uint64) { + s.mockConn.EXPECT(). + Invoke( + gomock.Any(), + "/poktroll.shared.Query/Params", + gomock.Any(), + gomock.Any(), + gomock.Any(), + ). + DoAndReturn(func(_ context.Context, _ string, _, reply any, _ ...grpc.CallOption) error { + resp := reply.(*sharedtypes.QueryParamsResponse) + params := sharedtypes.DefaultParams() + params.ComputeUnitsToTokensMultiplier = multiplier + + resp.Params = params + return nil + }).Times(1) +} diff --git a/x/proof/types/shared_query_client.go b/x/proof/types/shared_query_client.go index 574735e7e..9155b7cb7 100644 --- a/x/proof/types/shared_query_client.go +++ b/x/proof/types/shared_query_client.go @@ -4,6 +4,7 @@ import ( "context" "github.com/pokt-network/poktroll/pkg/client" + "github.com/pokt-network/poktroll/x/shared/keeper" sharedtypes "github.com/pokt-network/poktroll/x/shared/types" ) @@ -13,6 +14,8 @@ var _ client.SharedQueryClient = (*SharedKeeperQueryClient)(nil) // It does not rely on the QueryClient, and therefore does not make any // network requests as in the off-chain implementation. type SharedKeeperQueryClient struct { + *keeper.KeeperParamsQuerier[sharedtypes.Params, SharedKeeper] + sharedKeeper SharedKeeper sessionKeeper SessionKeeper } @@ -23,20 +26,15 @@ func NewSharedKeeperQueryClient( sharedKeeper SharedKeeper, sessionKeeper SessionKeeper, ) client.SharedQueryClient { + keeperParamsQuerier := keeper.NewKeeperParamsQuerier[sharedtypes.Params](sharedKeeper) + return &SharedKeeperQueryClient{ - sharedKeeper: sharedKeeper, - sessionKeeper: sessionKeeper, + KeeperParamsQuerier: keeperParamsQuerier, + sharedKeeper: sharedKeeper, + sessionKeeper: sessionKeeper, } } -// GetParams queries & returns the shared module on-chain parameters. -func (sqc *SharedKeeperQueryClient) GetParams( - ctx context.Context, -) (params *sharedtypes.Params, err error) { - sharedParams := sqc.sharedKeeper.GetParams(ctx) - return &sharedParams, nil -} - // GetSessionGracePeriodEndHeight returns the block height at which the grace period // for the session which includes queryHeight elapses. // The grace period is the number of blocks after the session ends during which relays @@ -48,8 +46,12 @@ func (sqc *SharedKeeperQueryClient) GetSessionGracePeriodEndHeight( ctx context.Context, queryHeight int64, ) (int64, error) { - sharedParams := sqc.sharedKeeper.GetParams(ctx) - return sharedtypes.GetSessionGracePeriodEndHeight(&sharedParams, queryHeight), nil + sharedParams, err := sqc.GetParamsAtHeight(ctx, queryHeight) + if err != nil { + return 0, err + } + + return sharedtypes.GetSessionGracePeriodEndHeight(sharedParams, queryHeight), nil } // GetClaimWindowOpenHeight returns the block height at which the claim window of @@ -61,8 +63,12 @@ func (sqc *SharedKeeperQueryClient) GetClaimWindowOpenHeight( ctx context.Context, queryHeight int64, ) (int64, error) { - sharedParams := sqc.sharedKeeper.GetParams(ctx) - return sharedtypes.GetClaimWindowOpenHeight(&sharedParams, queryHeight), nil + sharedParams, err := sqc.GetParamsAtHeight(ctx, queryHeight) + if err != nil { + return 0, err + } + + return sharedtypes.GetClaimWindowOpenHeight(sharedParams, queryHeight), nil } // GetProofWindowOpenHeight returns the block height at which the proof window of @@ -74,8 +80,12 @@ func (sqc *SharedKeeperQueryClient) GetProofWindowOpenHeight( ctx context.Context, queryHeight int64, ) (int64, error) { - sharedParams := sqc.sharedKeeper.GetParams(ctx) - return sharedtypes.GetProofWindowOpenHeight(&sharedParams, queryHeight), nil + sharedParams, err := sqc.GetParamsAtHeight(ctx, queryHeight) + if err != nil { + return 0, err + } + + return sharedtypes.GetProofWindowOpenHeight(sharedParams, queryHeight), nil } // GetEarliestSupplierClaimCommitHeight returns the earliest block height at which a claim @@ -109,8 +119,12 @@ func (sqc *SharedKeeperQueryClient) GetEarliestSupplierProofCommitHeight( queryHeight int64, supplierOperatorAddr string, ) (int64, error) { - sharedParams := sqc.sharedKeeper.GetParams(ctx) - proofWindowOpenHeight := sharedtypes.GetProofWindowOpenHeight(&sharedParams, queryHeight) + sharedParams, err := sqc.GetParamsAtHeight(ctx, queryHeight) + if err != nil { + return 0, err + } + + proofWindowOpenHeight := sharedtypes.GetProofWindowOpenHeight(sharedParams, queryHeight) // Fetch the proof window open block hash so that it can be used as part of the // pseudo-random seed for generating the proof distribution offset. @@ -119,7 +133,7 @@ func (sqc *SharedKeeperQueryClient) GetEarliestSupplierProofCommitHeight( // Get the earliest proof commit height for the given supplier. return sharedtypes.GetEarliestSupplierProofCommitHeight( - &sharedParams, + sharedParams, queryHeight, proofWindowOpenBlockHash, supplierOperatorAddr, @@ -133,6 +147,10 @@ func (sqc *SharedKeeperQueryClient) GetEarliestSupplierProofCommitHeight( // Since this will be a non-frequent occurrence, accounting for this edge case is // not an immediate blocker. func (sqc *SharedKeeperQueryClient) GetComputeUnitsToTokensMultiplier(ctx context.Context) (uint64, error) { - sharedParams := sqc.sharedKeeper.GetParams(ctx) + sharedParams, err := sqc.GetParamsAtHeight(ctx, 0) + if err != nil { + return 0, err + } + return sharedParams.GetComputeUnitsToTokensMultiplier(), nil } diff --git a/x/shared/keeper/params_query_client.go b/x/shared/keeper/params_query_client.go new file mode 100644 index 000000000..b4ded162c --- /dev/null +++ b/x/shared/keeper/params_query_client.go @@ -0,0 +1,83 @@ +package keeper + +import ( + "context" + "errors" + "fmt" + + "github.com/pokt-network/poktroll/pkg/client" + "github.com/pokt-network/poktroll/pkg/client/query/cache" + sharedtypes "github.com/pokt-network/poktroll/x/shared/types" +) + +var _ client.ParamsQuerier[*sharedtypes.Params] = (*KeeperParamsQuerier[sharedtypes.Params, Keeper])(nil) + +// DEV_NOTE: Can't use cosmostypes.Msg instead of any because M +// would be a pointer but GetParams() returns a value. 🙄 +type paramsKeeperIface[M any] interface { + GetParams(context.Context) M +} + +// KeeperParamsQuerier provides a base implementation of ParamsQuerier for keeper-based clients +type KeeperParamsQuerier[M any, K paramsKeeperIface[M]] struct { + keeper K + cache client.HistoricalQueryCache[M] +} + +// NewKeeperParamsQuerier creates a new KeeperParamsQuerier instance +func NewKeeperParamsQuerier[M any, K paramsKeeperIface[M]]( + keeper K, + opts ...cache.CacheOption, +) *KeeperParamsQuerier[M, K] { + // Use sensible defaults for keeper-based params cache + defaultOpts := []cache.CacheOption{ + cache.WithHistoricalMode(100), // Keep history of last 100 blocks + cache.WithEvictionPolicy(cache.FirstInFirstOut), + } + opts = append(defaultOpts, opts...) + + // TODO_IMPROVE: Implement and call a goroutine that subscribes to params updates to keep the cache hot. + + return &KeeperParamsQuerier[M, K]{ + keeper: keeper, + cache: cache.NewInMemoryCache[M](opts...), + } +} + +// GetParams retrieves current parameters from the keeper +func (kpq *KeeperParamsQuerier[M, K]) GetParams(ctx context.Context) (*M, error) { + // Check cache first + cached, err := kpq.cache.Get("params") + if err == nil { + return &cached, nil + } + if err != nil && !errors.Is(err, cache.ErrCacheMiss) { + return &cached, err + } + + // On cache miss, get from keeper + params := kpq.keeper.GetParams(ctx) + + // Cache the result + if err := kpq.cache.Set("params", params); err != nil { + return ¶ms, fmt.Errorf("failed to cache params: %w", err) + } + + return ¶ms, nil +} + +// GetParamsAtHeight retrieves parameters as they were at a specific height +func (kpq *KeeperParamsQuerier[M, K]) GetParamsAtHeight(ctx context.Context, height int64) (*M, error) { + // Try cache first + cached, err := kpq.cache.GetAtHeight("params", height) + if err == nil { + return &cached, nil + } + if err != nil && !errors.Is(err, cache.ErrCacheMiss) { + return &cached, err + } + + // For now, return current params as historical params are not yet implemented + // TODO_MAINNET: Implement historical parameter querying from state + return kpq.GetParams(ctx) +} diff --git a/x/shared/types/query_client.go b/x/shared/types/query_client.go new file mode 100644 index 000000000..443a374c6 --- /dev/null +++ b/x/shared/types/query_client.go @@ -0,0 +1,31 @@ +package types + +import ( + "context" + + gogogrpc "github.com/cosmos/gogoproto/grpc" +) + +// TODO_IN_THIS_COMMIT: godoc... +type SharedQueryClient interface { + QueryClient + + GetParams(context.Context) (*Params, error) +} + +// TODO_IN_THIS_COMMIT: godoc... +func NewSharedQueryClient(conn gogogrpc.ClientConn) SharedQueryClient { + return NewQueryClient(conn).(SharedQueryClient) +} + +// TODO_IN_THIS_COMMIT: investigate generalization... +// TODO_IN_THIS_COMMIT: godoc... +func (c *queryClient) GetParams(ctx context.Context) (*Params, error) { + res, err := c.Params(ctx, &QueryParamsRequest{}) + if err != nil { + return nil, err + } + + params := res.GetParams() + return ¶ms, nil +} From 938848916af7de84a449648e0ab1a5b21f9e323a Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:45:52 +0100 Subject: [PATCH 12/16] refactor: supplier query client --- pkg/client/interface.go | 3 +++ pkg/client/query/supplierquerier.go | 39 +++++++++++++++++++++++------ x/supplier/types/query_client.go | 31 +++++++++++++++++++++++ 3 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 x/supplier/types/query_client.go diff --git a/pkg/client/interface.go b/pkg/client/interface.go index 8b6e26efb..42bca3fa4 100644 --- a/pkg/client/interface.go +++ b/pkg/client/interface.go @@ -35,6 +35,7 @@ import ( servicetypes "github.com/pokt-network/poktroll/x/service/types" sessiontypes "github.com/pokt-network/poktroll/x/session/types" sharedtypes "github.com/pokt-network/poktroll/x/shared/types" + suppliertypes "github.com/pokt-network/poktroll/x/supplier/types" ) // MsgCreateClaim is an interface satisfying proof.MsgCreateClaim concrete type @@ -280,6 +281,8 @@ type ApplicationQueryClient interface { // SupplierQueryClient defines an interface that enables the querying of the // on-chain supplier information type SupplierQueryClient interface { + ParamsQuerier[*suppliertypes.Params] + // GetSupplier queries the chain for the details of the supplier provided GetSupplier(ctx context.Context, supplierOperatorAddress string) (sharedtypes.Supplier, error) } diff --git a/pkg/client/query/supplierquerier.go b/pkg/client/query/supplierquerier.go index 040a4303f..e1c4c1424 100644 --- a/pkg/client/query/supplierquerier.go +++ b/pkg/client/query/supplierquerier.go @@ -11,10 +11,14 @@ import ( suppliertypes "github.com/pokt-network/poktroll/x/supplier/types" ) +var _ client.SupplierQueryClient = (*supplierQuerier)(nil) + // supplierQuerier is a wrapper around the suppliertypes.QueryClient that enables the // querying of on-chain supplier information through a single exposed method // which returns an sharedtypes.Supplier struct type supplierQuerier struct { + client.ParamsQuerier[*suppliertypes.Params] + clientConn grpc.ClientConn supplierQuerier suppliertypes.QueryClient } @@ -24,28 +28,47 @@ type supplierQuerier struct { // // Required dependencies: // - grpc.ClientConn -func NewSupplierQuerier(deps depinject.Config) (client.SupplierQueryClient, error) { - supq := &supplierQuerier{} +func NewSupplierQuerier( + deps depinject.Config, + paramsQuerierOpts ...ParamsQuerierOptionFn, +) (client.SupplierQueryClient, error) { + paramsQuerierCfg := DefaultParamsQuerierConfig() + for _, opt := range paramsQuerierOpts { + opt(paramsQuerierCfg) + } + + paramsQuerier, err := NewCachedParamsQuerier[*suppliertypes.Params, suppliertypes.SupplierQueryClient]( + deps, suppliertypes.NewSupplierQueryClient, + WithModuleInfo[*suppliertypes.Params](suppliertypes.ModuleName, suppliertypes.ErrSupplierParamInvalid), + WithParamsCacheOptions(paramsQuerierCfg.CacheOpts...), + ) + if err != nil { + return nil, err + } + + sq := &supplierQuerier{ + ParamsQuerier: paramsQuerier, + } - if err := depinject.Inject( + if err = depinject.Inject( deps, - &supq.clientConn, + &sq.clientConn, ); err != nil { return nil, err } - supq.supplierQuerier = suppliertypes.NewQueryClient(supq.clientConn) + sq.supplierQuerier = suppliertypes.NewQueryClient(sq.clientConn) - return supq, nil + return sq, nil } // GetSupplier returns an suppliertypes.Supplier struct for a given address -func (supq *supplierQuerier) GetSupplier( +func (sq *supplierQuerier) GetSupplier( ctx context.Context, operatorAddress string, ) (sharedtypes.Supplier, error) { req := &suppliertypes.QueryGetSupplierRequest{OperatorAddress: operatorAddress} - res, err := supq.supplierQuerier.Supplier(ctx, req) + res, err := sq.supplierQuerier.Supplier(ctx, req) if err != nil { return sharedtypes.Supplier{}, suppliertypes.ErrSupplierNotFound.Wrapf( "address: %s [%v]", diff --git a/x/supplier/types/query_client.go b/x/supplier/types/query_client.go new file mode 100644 index 000000000..a26c712f3 --- /dev/null +++ b/x/supplier/types/query_client.go @@ -0,0 +1,31 @@ +package types + +import ( + "context" + + gogogrpc "github.com/cosmos/gogoproto/grpc" +) + +// TODO_IN_THIS_COMMIT: godoc... +type SupplierQueryClient interface { + QueryClient + + GetParams(context.Context) (*Params, error) +} + +// TODO_IN_THIS_COMMIT: godoc... +func NewSupplierQueryClient(conn gogogrpc.ClientConn) SupplierQueryClient { + return NewQueryClient(conn).(SupplierQueryClient) +} + +// TODO_IN_THIS_COMMIT: investigate generalization... +// TODO_IN_THIS_COMMIT: godoc... +func (c *queryClient) GetParams(ctx context.Context) (*Params, error) { + res, err := c.Params(ctx, &QueryParamsRequest{}) + if err != nil { + return nil, err + } + + params := res.GetParams() + return ¶ms, nil +} From aadbd8fb78bfbbb73ea72975129867d6c9130f05 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:46:23 +0100 Subject: [PATCH 13/16] fixup: UNKNOWN - tokenomics expected keepers GetParams() --- x/tokenomics/types/expected_keepers.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/tokenomics/types/expected_keepers.go b/x/tokenomics/types/expected_keepers.go index c7c0de0c5..0d864409d 100644 --- a/x/tokenomics/types/expected_keepers.go +++ b/x/tokenomics/types/expected_keepers.go @@ -46,6 +46,7 @@ type ApplicationKeeper interface { GetAllApplications(ctx context.Context) []apptypes.Application UnbondApplication(ctx context.Context, app *apptypes.Application) error EndBlockerUnbondApplications(ctx context.Context) error + GetParams(ctx context.Context) apptypes.Params } type ProofKeeper interface { From cdf11f8d0cb5b8499e5e6b48e437f54639c8b9c8 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:46:34 +0100 Subject: [PATCH 14/16] wip: badger cache --- pkg/client/query/cache/badger.go | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 pkg/client/query/cache/badger.go diff --git a/pkg/client/query/cache/badger.go b/pkg/client/query/cache/badger.go new file mode 100644 index 000000000..c29cdab54 --- /dev/null +++ b/pkg/client/query/cache/badger.go @@ -0,0 +1,11 @@ +package cache + +// TODO_UP_NEXT(@bryanchriswhite): Implement a persistent cache using badger. +// +// var _ query.QueryCache[any] = (*BadgerCache[any])(nil) + +// BadgerCache is a persistent cache backed by a badger database. +type BadgerCache[T any] struct { + // db *badger.DB + // config CacheConfig +} From 16acda70a141b0294fb3561282233d5f6ea365a8 Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:46:41 +0100 Subject: [PATCH 15/16] chore: client query metrics --- pkg/client/block/client.go | 7 +++++++ pkg/client/metrics.go | 38 ++++++++++++++++++++++++++++++++++ pkg/client/query/accquerier.go | 21 ++++++++++++------- 3 files changed, 59 insertions(+), 7 deletions(-) create mode 100644 pkg/client/metrics.go diff --git a/pkg/client/block/client.go b/pkg/client/block/client.go index b76ecbac2..60674d6a9 100644 --- a/pkg/client/block/client.go +++ b/pkg/client/block/client.go @@ -187,6 +187,13 @@ func (b *blockReplayClient) queryLatestBlock( errCh := make(chan error) go func() { + // TODO_IN_THIS_COMMIT: extract labels (and values?) to constants. + defer client.AllQueriesTotalCounter.With( + "method", "block", + "client_type", "block", + "msg_type", "", + ).Add(1) + queryBlockResult, err := b.onStartQueryClient.Block(ctx, nil) if err != nil { errCh <- err diff --git a/pkg/client/metrics.go b/pkg/client/metrics.go new file mode 100644 index 000000000..51c1962b8 --- /dev/null +++ b/pkg/client/metrics.go @@ -0,0 +1,38 @@ +package client + +import ( + "github.com/go-kit/kit/metrics/prometheus" + stdprometheus "github.com/prometheus/client_golang/prometheus" +) + +const ( + clientSubsystem = "client" + + allQueriesTotal = "all_queries_total" + paramsQueriesTotal = "params_queries_total" +) + +var ( + // TODO_IN_THIS_COMMIT: godoc... + AllQueriesTotalCounter = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ + Subsystem: clientSubsystem, + Name: allQueriesTotal, + Help: "Total number of all query messages, of any type, sent by the client.", + // TODO_IN_THIS_COMMIT: extract labels to constants. + //}, []string{"client_type", "method", "msg_type", "claim_proof_lifecycle_stage"}) + }, []string{"client_type", "method", "msg_type"}) + + // TODO_IN_THIS_COMMIT: godoc... + ParamsQueriesTotalCounter = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ + Subsystem: clientSubsystem, + Name: paramsQueriesTotal, + Help: "Total number of QueryParamsRequest messages sent by the client.", + }, []string{"client_type", "method", "claim_proof_lifecycle_stage"}) + + // TODO_IN_THIS_COMMIT: godoc & use... + AllWebsocketEventsTotalCounter = prometheus.NewCounterFrom(stdprometheus.CounterOpts{ + Subsystem: clientSubsystem, + Name: "all_websocket_events_total", + Help: "Total number of websocket events received by the client.", + }, []string{"client_type", "event_type"}) +) diff --git a/pkg/client/query/accquerier.go b/pkg/client/query/accquerier.go index 2b4d9c2bc..0545e03d5 100644 --- a/pkg/client/query/accquerier.go +++ b/pkg/client/query/accquerier.go @@ -6,9 +6,9 @@ import ( "cosmossdk.io/depinject" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" - "github.com/cosmos/cosmos-sdk/types" + cosmostypes "github.com/cosmos/cosmos-sdk/types" accounttypes "github.com/cosmos/cosmos-sdk/x/auth/types" - grpc "github.com/cosmos/gogoproto/grpc" + "github.com/cosmos/gogoproto/grpc" "github.com/pokt-network/poktroll/pkg/client" ) @@ -24,7 +24,7 @@ type accQuerier struct { // accountCache is a cache of accounts that have already been queried. // TODO_TECHDEBT: Add a size limit to the cache and consider an LRU cache. - accountCache map[string]types.AccountI + accountCache map[string]cosmostypes.AccountI accountCacheMu sync.Mutex } @@ -34,7 +34,7 @@ type accQuerier struct { // Required dependencies: // - clientCtx func NewAccountQuerier(deps depinject.Config) (client.AccountQueryClient, error) { - aq := &accQuerier{accountCache: make(map[string]types.AccountI)} + aq := &accQuerier{accountCache: make(map[string]cosmostypes.AccountI)} if err := depinject.Inject( deps, @@ -52,9 +52,16 @@ func NewAccountQuerier(deps depinject.Config) (client.AccountQueryClient, error) func (aq *accQuerier) GetAccount( ctx context.Context, address string, -) (types.AccountI, error) { +) (cosmostypes.AccountI, error) { aq.accountCacheMu.Lock() - defer aq.accountCacheMu.Unlock() + defer func() { + aq.accountCacheMu.Unlock() + client.AllQueriesTotalCounter.With( + "method", "account", + "client_type", "account", + "msg_type", cosmostypes.MsgTypeURL(new(accounttypes.QueryAccountRequest)), + ).Add(1) + }() if foundAccount, isAccountFound := aq.accountCache[address]; isAccountFound { return foundAccount, nil @@ -68,7 +75,7 @@ func (aq *accQuerier) GetAccount( } // Unpack and cache the account object - var fetchedAccount types.AccountI + var fetchedAccount cosmostypes.AccountI if err = queryCodec.UnpackAny(res.Account, &fetchedAccount); err != nil { return nil, ErrQueryUnableToDeserializeAccount.Wrapf("address: %s [%v]", address, err) } From b07a8b807b5b29f548ddbf92a1beded7dc7f4b2d Mon Sep 17 00:00:00 2001 From: Bryan White Date: Tue, 10 Dec 2024 15:47:05 +0100 Subject: [PATCH 16/16] revert: up next --- go.mod | 2 ++ go.sum | 5 ++--- tools/scripts/params/application_min_stake.json | 2 +- .../params/proof_proof_request_probability.json | 2 +- tools/scripts/params/shared_all.json | 10 +++++----- .../shared_application_unbonding_period_sessions.json | 2 +- .../scripts/params/shared_num_blocks_per_session.json | 2 +- .../shared_supplier_unbonding_period_sessions.json | 2 +- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/go.mod b/go.mod index 024364e1b..6a52c4c27 100644 --- a/go.mod +++ b/go.mod @@ -12,6 +12,8 @@ go 1.23.0 // github.com/pokt-network/smt/kvstore/pebble => ../smt/kvstore/pebble // ) +replace nhooyr.io/websocket => github.com/coder/websocket v1.8.6 + // replace broken goleveldb replace github.com/syndtr/goleveldb => github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 diff --git a/go.sum b/go.sum index 62dc61bb0..782cd3a8d 100644 --- a/go.sum +++ b/go.sum @@ -366,6 +366,8 @@ github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZ github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= +github.com/coder/websocket v1.8.6 h1:OmNKdwUvLj7VvHnl5o8skaVghSPLjWdHGCnFbkWqs9w= +github.com/coder/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= github.com/cometbft/cometbft v0.38.10 h1:2ePuglchT+j0Iao+cfmt/nw5U7K2lnGDzXSUPGVdXaU= github.com/cometbft/cometbft v0.38.10/go.mod h1:jHPx9vQpWzPHEAiYI/7EDKaB1NXhK6o3SArrrY8ExKc= github.com/cometbft/cometbft-db v0.9.1 h1:MIhVX5ja5bXNHF8EYrThkG9F7r9kSfv8BX4LWaxWJ4M= @@ -1861,9 +1863,6 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -nhooyr.io/websocket v1.8.6/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= -nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= -nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= pgregory.net/rapid v1.1.0 h1:CMa0sjHSru3puNx+J0MIAuiiEV4N0qj8/cMWGBBCsjw= pgregory.net/rapid v1.1.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/tools/scripts/params/application_min_stake.json b/tools/scripts/params/application_min_stake.json index 7b21a6f29..973760366 100644 --- a/tools/scripts/params/application_min_stake.json +++ b/tools/scripts/params/application_min_stake.json @@ -7,7 +7,7 @@ "name": "min_stake", "as_coin": { "denom": "upokt", - "amount": "1000000" + "amount": "1" } } ] diff --git a/tools/scripts/params/proof_proof_request_probability.json b/tools/scripts/params/proof_proof_request_probability.json index 0de0f9946..fccb4bd16 100644 --- a/tools/scripts/params/proof_proof_request_probability.json +++ b/tools/scripts/params/proof_proof_request_probability.json @@ -5,7 +5,7 @@ "@type": "/poktroll.proof.MsgUpdateParam", "authority": "pokt10d07y265gmmuvt4z0w9aw880jnsr700j8yv32t", "name": "proof_request_probability", - "as_float": "0.25" + "as_float": "1" } ] } diff --git a/tools/scripts/params/shared_all.json b/tools/scripts/params/shared_all.json index 2a489f27c..8aa03d761 100644 --- a/tools/scripts/params/shared_all.json +++ b/tools/scripts/params/shared_all.json @@ -5,12 +5,12 @@ "@type": "/poktroll.shared.MsgUpdateParams", "authority": "pokt10d07y265gmmuvt4z0w9aw880jnsr700j8yv32t", "params": { - "num_blocks_per_session": "10", - "grace_period_end_offset_blocks": "1", - "claim_window_open_offset_blocks": "1", - "claim_window_close_offset_blocks": "4", + "num_blocks_per_session": "4", + "grace_period_end_offset_blocks": "0", + "claim_window_open_offset_blocks": "0", + "claim_window_close_offset_blocks": "1", "proof_window_open_offset_blocks": "0", - "proof_window_close_offset_blocks": "4", + "proof_window_close_offset_blocks": "1", "supplier_unbonding_period_sessions": "1", "application_unbonding_period_sessions": "1", "compute_units_to_tokens_multiplier": "42" diff --git a/tools/scripts/params/shared_application_unbonding_period_sessions.json b/tools/scripts/params/shared_application_unbonding_period_sessions.json index cd62a759c..eb5fc83da 100644 --- a/tools/scripts/params/shared_application_unbonding_period_sessions.json +++ b/tools/scripts/params/shared_application_unbonding_period_sessions.json @@ -5,7 +5,7 @@ "@type": "/poktroll.shared.MsgUpdateParam", "authority": "pokt10d07y265gmmuvt4z0w9aw880jnsr700j8yv32t", "name": "application_unbonding_period_sessions", - "as_int64": "4" + "as_uint64": "1" } ] } diff --git a/tools/scripts/params/shared_num_blocks_per_session.json b/tools/scripts/params/shared_num_blocks_per_session.json index e2d33a017..341eaf4d0 100644 --- a/tools/scripts/params/shared_num_blocks_per_session.json +++ b/tools/scripts/params/shared_num_blocks_per_session.json @@ -5,7 +5,7 @@ "@type": "/poktroll.shared.MsgUpdateParam", "authority": "pokt10d07y265gmmuvt4z0w9aw880jnsr700j8yv32t", "name": "num_blocks_per_session", - "as_int64": "4" + "as_uint64": "10" } ] } diff --git a/tools/scripts/params/shared_supplier_unbonding_period_sessions.json b/tools/scripts/params/shared_supplier_unbonding_period_sessions.json index 99c5a4d3f..a8133f3c2 100644 --- a/tools/scripts/params/shared_supplier_unbonding_period_sessions.json +++ b/tools/scripts/params/shared_supplier_unbonding_period_sessions.json @@ -5,7 +5,7 @@ "@type": "/poktroll.shared.MsgUpdateParam", "authority": "pokt10d07y265gmmuvt4z0w9aw880jnsr700j8yv32t", "name": "supplier_unbonding_period_sessions", - "as_int64": "4" + "as_uint64": "1" } ] }