Skip to content

Commit

Permalink
[Utility] trustless relays servicer token validation (#803)
Browse files Browse the repository at this point in the history
## Description

Add validation of application's session tokens to the servicer.

## Issue

Part of work on #754 

## Type of change

Please mark the relevant option(s):

- [x] New feature, functionality or library
- [ ] Bug fix
- [ ] Code health or cleanup
- [ ] Major breaking change
- [ ] Documentation
- [ ] Other <!-- add details here if it a different type of change -->

## List of changes

- Servicer now validates the availability of tokens in the current
session before executing relays.

## Testing

- [x] `make develop_test`; if any code changes were made
- [ ] `make test_e2e` on [k8s
LocalNet](https://github.com/pokt-network/pocket/blob/main/build/localnet/README.md);
if any code changes were made
- [ ] `e2e-devnet-test` passes tests on
[DevNet](https://pocketnetwork.notion.site/How-to-DevNet-ff1598f27efe44c09f34e2aa0051f0dd);
if any code was changed
- [ ] [Docker Compose
LocalNet](https://github.com/pokt-network/pocket/blob/main/docs/development/README.md);
if any major functionality was changed or introduced
- [ ] [k8s
LocalNet](https://github.com/pokt-network/pocket/blob/main/build/localnet/README.md);
if any infrastructure or configuration changes were made

## Required Checklist

- [x] I have performed a self-review of my own code
- [x] I have commented my code, particularly in hard-to-understand areas
- [] I have added, or updated, [`godoc` format
comments](https://go.dev/blog/godoc) on touched members (see:
[tip.golang.org/doc/comment](https://tip.golang.org/doc/comment))
- [x] I have tested my changes using the available tooling
- [x] I have updated the corresponding CHANGELOG

### If Applicable Checklist

- [ ] I have updated the corresponding README(s); local and/or global
- [ ] I have added tests that prove my fix is effective or that my
feature works
- [ ] I have added, or updated,
[mermaid.js](https://mermaid-js.github.io) diagrams in the corresponding
README(s)
- [ ] I have added, or updated, documentation and
[mermaid.js](https://mermaid-js.github.io) diagrams in `shared/docs/*`
if I updated `shared/*`README(s)
  • Loading branch information
adshmh authored Jun 29, 2023
1 parent fd30526 commit 208ffbb
Show file tree
Hide file tree
Showing 20 changed files with 736 additions and 76 deletions.
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,10 @@ test_persistence: ## Run all go unit tests in the Persistence module
test_persistence_state_hash: ## Run all go unit tests in the Persistence module related to the state hash
go test ${VERBOSE_TEST} -count=1 -tags=test -run TestStateHash ./persistence/...

.PHONY: test_servicer_relay
test_servicer_relay: ## Run all go unit tests related to servicer relays
go test ${VERBOSE_TEST} -count=1 -tags=test ./utility/servicer -run TestRelay

.PHONY: test_p2p
test_p2p: ## Run all p2p related tests
go test ${VERBOSE_TEST} -count=1 -tags=test ./p2p/...
Expand Down
4 changes: 4 additions & 0 deletions persistence/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.60] - 2023-06-26

- Add place-holder for local context and servicer token usage support methods

## [0.0.0.59] - 2023-06-14

- Refactors the persistence treeStore to be an IntegratableModule
Expand Down
80 changes: 80 additions & 0 deletions persistence/local/module.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package local

import (
"math/big"

"github.com/pokt-network/pocket/logger"
coreTypes "github.com/pokt-network/pocket/shared/core/types"
"github.com/pokt-network/pocket/shared/modules"
"github.com/pokt-network/pocket/shared/modules/base_modules"
)

const (
LocalModuleName = "local"
)

var _ modules.PersistenceLocalContext = &persistenceLocalContext{}

type persistenceLocalContext struct {
base_modules.IntegratableModule

logger *modules.Logger
databasePath string
}

func WithLocalDatabasePath(databasePath string) modules.ModuleOption {
return func(m modules.InitializableModule) {
if plc, ok := m.(*persistenceLocalContext); ok {
plc.databasePath = databasePath
}
}
}

func CreateLocalContext(bus modules.Bus, options ...modules.ModuleOption) (modules.PersistenceLocalContext, error) {
m, err := new(persistenceLocalContext).Create(bus, options...)
if err != nil {
return nil, err
}
return m.(modules.PersistenceLocalContext), nil
}

func (*persistenceLocalContext) Create(bus modules.Bus, options ...modules.ModuleOption) (modules.Module, error) {
m := &persistenceLocalContext{}

for _, option := range options {
option(m)
}

bus.RegisterModule(m)

m.logger = logger.Global.CreateLoggerForModule(m.GetModuleName())

return m, nil
}

func (m *persistenceLocalContext) GetModuleName() string {
return LocalModuleName
}

// INCOMPLETE(#826): implement this
func (m *persistenceLocalContext) Start() error {
return nil
}

// INCOMPLETE(#826): implement this
func (m *persistenceLocalContext) Stop() error {
return nil
}

// INCOMPLETE(#826): implement this
// OPTIMIZE: both the relay and the response can be large structures: we may need to truncate the stored values
// StoreServicedRelay implements the PersistenceLocalContext interface
func (local *persistenceLocalContext) StoreServicedRelay(session *coreTypes.Session, relayDigest, relayReqResBytes []byte) error {
return nil
}

// INCOMPLETE(#826): implement this
// GetSessionTokensUsed implements the PersistenceLocalContext interface
func (local *persistenceLocalContext) GetSessionTokensUsed(*coreTypes.Session) (*big.Int, error) {
return nil, nil
}
7 changes: 7 additions & 0 deletions persistence/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/pokt-network/pocket/logger"
"github.com/pokt-network/pocket/persistence/blockstore"
"github.com/pokt-network/pocket/persistence/indexer"
"github.com/pokt-network/pocket/persistence/local"
"github.com/pokt-network/pocket/persistence/trees"
"github.com/pokt-network/pocket/runtime/configs"
"github.com/pokt-network/pocket/runtime/genesis"
Expand Down Expand Up @@ -249,6 +250,12 @@ func (m *persistenceModule) NewWriteContext() modules.PersistenceRWContext {
return m.writeContext
}

// INCOMPLETE(#826): implement this
// GetLocalContext returns a new local context for storing off-chain, i.e. node-specific, data.
func (m *persistenceModule) GetLocalContext() (modules.PersistenceLocalContext, error) {
return local.CreateLocalContext(m.GetBus())
}

// HACK(olshansky): Simplify and externalize the logic for whether genesis should be populated and
//
// move the if logic out of this file.
Expand Down
13 changes: 13 additions & 0 deletions persistence/servicer.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package persistence

import (
"encoding/hex"
"math/big"

"github.com/pokt-network/pocket/persistence/types"
coreTypes "github.com/pokt-network/pocket/shared/core/types"
Expand Down Expand Up @@ -79,3 +80,15 @@ func (p *PostgresContext) SetServicerPauseHeight(address []byte, height int64) e
func (p *PostgresContext) GetServicerOutputAddress(operator []byte, height int64) (output []byte, err error) {
return p.GetActorOutputAddress(types.ServicerActor, operator, height)
}

// INCOMPLETE: implement this
// DISCUSS: both the relay and the response can be large structures: we may need to truncate the stored values
func (p *PostgresContext) RecordRelayService(applicationAddress string, key []byte, relay *coreTypes.Relay, response *coreTypes.RelayResponse) error {
return nil
}

// INCOMPLETE: implement this
// GetServicerTokenUsage returns the number of tokens used by the servicer in the current session, i.e. for the application associated with the session
func (p *PostgresContext) GetServicerTokenUsage(session *coreTypes.Session) (*big.Int, error) {
return nil, nil
}
4 changes: 4 additions & 0 deletions rpc/doc/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.24] - 2023-06-21

- Update handlers to use the new relay payload types

## [0.0.0.23] - 2023-06-19

- Remove AAT reference from handlers
Expand Down
3 changes: 2 additions & 1 deletion rpc/v1/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1696,11 +1696,12 @@ components:
Payload:
type: object
required:
- method
- jsonrpc
- method
properties:
id:
type: string
format: byte
jsonrpc:
type: string
method:
Expand Down
21 changes: 21 additions & 0 deletions runtime/configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ func NewDefaultConfig(options ...func(*Config)) *Config {
VaultMountPath: defaults.DefaultKeybaseVaultMountPath,
},
Validator: &ValidatorConfig{},
// INCOMPLETE(#858): use defaultServicerConfig once the default configuration issue is resolved, i.e. once configuring fisherman disables default servicer
Servicer: &ServicerConfig{},
Fisherman: &FishermanConfig{},
IBC: &IBCConfig{
Expand Down Expand Up @@ -214,3 +215,23 @@ func CreateTempConfig(cfg *Config) (*Config, error) {

return ParseConfig(tmpfile.Name()), nil
}

// INCOMPLETE(#858): enable default servicer config once the default config is adjusted based on user-defined config
// nolint:unused // Use the servicer default config once #858 is resolved: see above description
func defaultServicerConfig() *ServicerConfig {
return &ServicerConfig{
Enabled: true,
RelayMiningVolumeAccuracy: 0.2,
Services: map[string]*ServiceConfig{
// TODO(#831): Design how Chain/Service IDs should be described/defined.
"POKT-LocalNet": {
Url: "http://localhost",
TimeoutMsec: 5000,
BasicAuth: &BasicAuth{
UserName: "user",
Password: "password",
},
},
},
}
}
2 changes: 2 additions & 0 deletions runtime/configs/proto/persistence_config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ message PersistenceConfig {
string max_conn_lifetime = 8; // See pkg.go.dev/time#ParseDuration for reference
string max_conn_idle_time = 9; // See pkg.go.dev/time#ParseDuration for reference
string health_check_period = 10; // See pkg.go.dev/time#ParseDuration for reference
// TODO: `local_database_path` may need to be expanded to multiple stores depending on how usage evolves
string local_database_path = 11; // The path used to store local, i.e. off-chain and node-specific, data.
}
25 changes: 24 additions & 1 deletion runtime/configs/proto/servicer_config.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,34 @@ package configs;

option go_package = "github.com/pokt-network/pocket/runtime/configs";

// TODO: Reevaluate whether each utility actor should contain address/pubKey configs or if it should be shared
// ServicerConfig defines the configuration for the node acting as a servicer. Servicers earn rewards for providing Web3 access over a function of volume and quality
message ServicerConfig {
// Enabled defines whether or not the node is a servicer.
bool enabled = 1;
string public_key = 2;
string address = 3;
repeated string chains = 4;
map<string, ServiceConfig> services = 4;

// relay_mining_volume_accuracy is a parameter used to adjust the calculated number of service tokens for an application.
// It is introduced to minimize the chance of under-utilization of application's tokens, while removing the overhead of
// communication between servicers which would be necessary otherwise.
// See the following for more details:
// https://arxiv.org/abs/2305.10672
double relay_mining_volume_accuracy = 5;
}

// ServiceConfig holds configurations related to where/how the application/client can access the backing RPC service. It is analogous to "ChainConfig" in v0 but can support any RPC service.
message ServiceConfig {
string url = 1; // url specifies the URL at which the service is provided/requested
uint64 timeout_msec = 2; // timeout specifes the maximum amount of time, in milliseconds, to allow for the service to return a response
BasicAuth basic_auth = 3; // optional: basic authentication for HTTP services.
}

// BasicAuth stores authentication data for HTTP services
// When supplied, this data will be used as specified by the HTTP basic authentication scheme.
message BasicAuth {
string user_name = 1;
// IMPROVE: enforce encryption of plaintext password
string password = 2;
}
4 changes: 4 additions & 0 deletions runtime/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.44] - 2023-06-26

- Add a new ServiceConfig field to servicer config

## [0.0.0.43] - 2023-06-14

- Rename package import types to coreTypes for consitency and clarity
Expand Down
7 changes: 2 additions & 5 deletions runtime/manager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1816,11 +1816,8 @@ func TestNewManagerFromReaders(t *testing.T) {
Timeout: 30000,
UseCors: false,
},
Keybase: defaultCfg.Keybase,
Servicer: &configs.ServicerConfig{
Enabled: true,
Chains: []string{"0001"},
},
Keybase: defaultCfg.Keybase,
Servicer: &configs.ServicerConfig{Enabled: true},
Validator: &configs.ValidatorConfig{Enabled: true},
Fisherman: defaultCfg.Fisherman,
IBC: &configs.IBCConfig{Enabled: true},
Expand Down
5 changes: 5 additions & 0 deletions shared/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.0.0.60] - 2023-06-21

- Add a LocalContext type and place-holders for servicer token usage support to persistence
- Add a TODO comment for supporting different relay payload types in core types

## [0.0.0.59] - 2023-06-14

- Added validators for JSONRPC and REST payloads to shared types
Expand Down
12 changes: 11 additions & 1 deletion shared/core/types/proto/relay.proto
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ option go_package = "github.com/pokt-network/pocket/shared/core/types";

message Relay {
RelayMeta meta = 1;
// Every different chain/service may have its own custom payload (e.g. HTTP, JSON, GRPC, non-chain services)
oneof relay_payload {
JSONRPCPayload json_rpc_payload = 2;
RESTPayload rest_payload = 3;
Expand All @@ -16,6 +17,7 @@ message Relay {
}
}

// INCOMPLETE: add REST relay payload fields
message RESTPayload {
string contents = 1;
string http_path = 2;
Expand Down Expand Up @@ -50,7 +52,8 @@ message JSONRPCPayload {
message RelayMeta {
int64 block_height = 1;
string servicer_public_key = 2;
// TODO: Make Chain identifier type consistent in Session and Meta: use Identifiable for Chain in Session (or a string here to match the session)
// TODO(M5): Consider renaming `relay_chain` to `rpc_service` or something similar
// TODO: Make Chain/Service identifier type consistent in Session and Meta: use Identifiable for Chain/Service in Session (or a string here to match the session)
Identifiable relay_chain = 3;
Identifiable geo_zone = 4;
string signature = 5; // TECHDEBT: Consolidate with `Signature` proto used elsewhere in the future
Expand All @@ -73,3 +76,10 @@ message Identifiable {
string id = 1;
string name = 2;
}


// RelayReqRes contains a relay request and its response, used for persistence of relay service evidence
message RelayReqRes {
Relay relay = 1;
RelayResponse response = 2;
}
2 changes: 1 addition & 1 deletion shared/core/types/proto/session.proto
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import "actor.proto";
message Session {
string id = 1; // a universally unique ID for the session
int64 session_number = 2; // a monotonically increasing number representing the # on the chain
int64 session_height = 3; // the number of blocks (out of numBlocksPerSession) in this session
int64 session_height = 3; // the height at which the session starts
int64 num_session_blocks = 4; // the number of blocks the session is valid from
// CONSIDERATION: Should we add a `RelayChain` enum and use it across the board?
// CONSIDERATION: Should a single session support multiple relay chains?
Expand Down
31 changes: 30 additions & 1 deletion shared/modules/persistence_module.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package modules

//go:generate mockgen -destination=./mocks/persistence_module_mock.go github.com/pokt-network/pocket/shared/modules PersistenceModule,PersistenceRWContext,PersistenceReadContext,PersistenceWriteContext
//go:generate mockgen -destination=./mocks/persistence_module_mock.go github.com/pokt-network/pocket/shared/modules PersistenceModule,PersistenceRWContext,PersistenceReadContext,PersistenceWriteContext,PersistenceLocalContext

import (
"math/big"

"github.com/pokt-network/pocket/persistence/blockstore"
"github.com/pokt-network/pocket/persistence/indexer"
"github.com/pokt-network/pocket/runtime/genesis"
Expand All @@ -18,6 +20,9 @@ type PersistenceModule interface {

// Context operations
NewRWContext(height int64) (PersistenceRWContext, error)
// TODO(#406): removing height from "NewReadContext" input and passing it to specific methods seems a better choice.
// This could prevent confusion when retrieving the value of a parameter for a height less than the current height,
// e.g. when getting the App Token sessions multiplier for the starting height of a session.
NewReadContext(height int64) (PersistenceReadContext, error)
ReleaseWriteContext() error // The module can maintain many read contexts, but only one write context can exist at a time

Expand All @@ -35,6 +40,11 @@ type PersistenceModule interface {

// Debugging / development only
HandleDebugMessage(*messaging.DebugMessage) error

// GetLocalContext returns a local persistence context that can be used to store/retrieve node-specific, i.e. off-chain, data
// The module can maintain a single (i.e. a singleton) local context for both read and write operations: subsequent calls to GetLocalContext return
// the same local context.
GetLocalContext() (PersistenceLocalContext, error)
}

// Interface defining the context within which the node can operate with the persistence layer.
Expand Down Expand Up @@ -133,6 +143,8 @@ type PersistenceWriteContext interface {
// Flag Operations
InitFlags() error
SetFlag(paramName string, value any, enabled bool) error

RecordRelayService(applicationAddress string, key []byte, relay *coreTypes.Relay, response *coreTypes.RelayResponse) error
}

type PersistenceReadContext interface {
Expand Down Expand Up @@ -224,3 +236,20 @@ type PersistenceReadContext interface {
GetStringFlag(paramName string, height int64) (string, bool, error)
GetBytesFlag(paramName string, height int64) ([]byte, bool, error)
}

// PersistenceLocalContext defines the set of operations specific to local persistence.
//
// This context should be used for node-specific data, e.g. records of served relays.
// This is in contrast to PersistenceRWContext which should be used to store on-chain data.
type PersistenceLocalContext interface {
// StoreServicedRelay stores record of a serviced relay and its response in the local context.
// The stored service relays will be used to:
// a) check the number of tokens used per session, and
// b) prepare claim/proof messages once the session is over
// The "relayDigest" and "relayReqResBytes" parameters will be used as key and leaf contents in the constructed SMT, respectively.
StoreServicedRelay(session *coreTypes.Session, relayDigest, relayReqResBytes []byte) error
// GetSessionTokensUsed returns the number of tokens that have been used for the provided session.
// It returns the count of tokens used by the servicer instance
// for the application associated with the session
GetSessionTokensUsed(*coreTypes.Session) (*big.Int, error)
}
Loading

0 comments on commit 208ffbb

Please sign in to comment.