Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Session roll over fix #1536

Merged
merged 6 commits into from
Apr 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ func UpdateConfig(datadir string) {
GlobalConfig.PocketConfig.RPCTimeout = sdk.DefaultRPCTimeout
GlobalConfig.PocketConfig.IavlCacheSize = sdk.DefaultIavlCacheSize
GlobalConfig.PocketConfig.LeanPocket = sdk.DefaultLeanPocket
GlobalConfig.PocketConfig.ClientSessionSyncAllowance = sdk.DefaultSessionSyncAllowance

// Backup and Save the File
var jsonFile *os.File
Expand Down
1 change: 1 addition & 0 deletions doc/guides/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ Pocket Core provides a configuration file found in `<datadir>/config/config.json
- **"rpc_port"**: The port of Pocket Core's RPC
- **"client_block_sync_allowance"**: The +/- allowance in blocks for a relay request \(security mechanism that can
help filter misconfigured clients\)
- **"client_session_sync_allowance"**: The +/- allowance in amount of sessions for a relay request \(mechanism to allow for servicing stale sessions for whenever session height changes.)
- **"max_evidence_cache_entries"**: Maximum number of relay evidence stored in cache memory
- **"max_session_cache_entries"**: Maximum number of sessions stored in cache memory
- **"json_sort_relay_responses"**: Detect and sort if relay response is in json \(can help response comparisons if
Expand Down
121 changes: 62 additions & 59 deletions types/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,36 +18,37 @@ type SDKConfig struct {
}

type PocketConfig struct {
DataDir string `json:"data_dir"`
GenesisName string `json:"genesis_file"`
ChainsName string `json:"chains_name"`
EvidenceDBName string `json:"evidence_db_name"`
TendermintURI string `json:"tendermint_uri"`
KeybaseName string `json:"keybase_name"`
RPCPort string `json:"rpc_port"`
ClientBlockSyncAllowance int `json:"client_block_sync_allowance"`
MaxEvidenceCacheEntires int `json:"max_evidence_cache_entries"`
MaxSessionCacheEntries int `json:"max_session_cache_entries"`
JSONSortRelayResponses bool `json:"json_sort_relay_responses"`
RemoteCLIURL string `json:"remote_cli_url"`
UserAgent string `json:"user_agent"`
ValidatorCacheSize int64 `json:"validator_cache_size"`
ApplicationCacheSize int64 `json:"application_cache_size"`
RPCTimeout int64 `json:"rpc_timeout"`
PrometheusAddr string `json:"pocket_prometheus_port"`
PrometheusMaxOpenfiles int `json:"prometheus_max_open_files"`
MaxClaimAgeForProofRetry int `json:"max_claim_age_for_proof_retry"`
ProofPrevalidation bool `json:"proof_prevalidation"`
CtxCacheSize int `json:"ctx_cache_size"`
ABCILogging bool `json:"abci_logging"`
RelayErrors bool `json:"show_relay_errors"`
DisableTxEvents bool `json:"disable_tx_events"`
Cache bool `json:"-"`
IavlCacheSize int64 `json:"iavl_cache_size"`
ChainsHotReload bool `json:"chains_hot_reload"`
GenerateTokenOnStart bool `json:"generate_token_on_start"`
LeanPocket bool `json:"lean_pocket"`
LeanPocketUserKeyFileName string `json:"lean_pocket_user_key_file"`
DataDir string `json:"data_dir"`
GenesisName string `json:"genesis_file"`
ChainsName string `json:"chains_name"`
EvidenceDBName string `json:"evidence_db_name"`
TendermintURI string `json:"tendermint_uri"`
KeybaseName string `json:"keybase_name"`
RPCPort string `json:"rpc_port"`
ClientBlockSyncAllowance int `json:"client_block_sync_allowance"`
ClientSessionSyncAllowance int64 `json:"client_session_sync_allowance"`
nodiesBlade marked this conversation as resolved.
Show resolved Hide resolved
MaxEvidenceCacheEntires int `json:"max_evidence_cache_entries"`
MaxSessionCacheEntries int `json:"max_session_cache_entries"`
JSONSortRelayResponses bool `json:"json_sort_relay_responses"`
RemoteCLIURL string `json:"remote_cli_url"`
UserAgent string `json:"user_agent"`
ValidatorCacheSize int64 `json:"validator_cache_size"`
ApplicationCacheSize int64 `json:"application_cache_size"`
RPCTimeout int64 `json:"rpc_timeout"`
PrometheusAddr string `json:"pocket_prometheus_port"`
PrometheusMaxOpenfiles int `json:"prometheus_max_open_files"`
MaxClaimAgeForProofRetry int `json:"max_claim_age_for_proof_retry"`
ProofPrevalidation bool `json:"proof_prevalidation"`
CtxCacheSize int `json:"ctx_cache_size"`
ABCILogging bool `json:"abci_logging"`
RelayErrors bool `json:"show_relay_errors"`
DisableTxEvents bool `json:"disable_tx_events"`
Cache bool `json:"-"`
IavlCacheSize int64 `json:"iavl_cache_size"`
ChainsHotReload bool `json:"chains_hot_reload"`
GenerateTokenOnStart bool `json:"generate_token_on_start"`
LeanPocket bool `json:"lean_pocket"`
LeanPocketUserKeyFileName string `json:"lean_pocket_user_key_file"`
}

func (c PocketConfig) GetLeanPocketUserKeyFilePath() string {
Expand Down Expand Up @@ -82,6 +83,7 @@ const (
DefaultMaxEvidenceCacheEntries = 500
DefaultListenAddr = "tcp://0.0.0.0:"
DefaultClientBlockSyncAllowance = 10
DefaultSessionSyncAllowance = 1 // 1 session (irrespective of num blocks per session)
DefaultJSONSortRelayResponses = true
DefaultTxIndexer = "kv"
DefaultRPCDisableTransactionEvents = true
Expand Down Expand Up @@ -117,35 +119,36 @@ func DefaultConfig(dataDir string) Config {
c := Config{
TendermintConfig: *config.DefaultConfig(),
PocketConfig: PocketConfig{
DataDir: dataDir,
GenesisName: DefaultGenesisName,
ChainsName: DefaultChainsName,
EvidenceDBName: DefaultEvidenceDBName,
TendermintURI: DefaultTMURI,
KeybaseName: DefaultKeybaseName,
RPCPort: DefaultRPCPort,
ClientBlockSyncAllowance: DefaultClientBlockSyncAllowance,
MaxEvidenceCacheEntires: DefaultMaxEvidenceCacheEntries,
MaxSessionCacheEntries: DefaultMaxSessionCacheEntries,
JSONSortRelayResponses: DefaultJSONSortRelayResponses,
RemoteCLIURL: DefaultRemoteCLIURL,
UserAgent: DefaultUserAgent,
ValidatorCacheSize: DefaultValidatorCacheSize,
ApplicationCacheSize: DefaultApplicationCacheSize,
RPCTimeout: DefaultRPCTimeout,
PrometheusAddr: DefaultPocketPrometheusListenAddr,
PrometheusMaxOpenfiles: DefaultPrometheusMaxOpenFile,
MaxClaimAgeForProofRetry: DefaultMaxClaimProofRetryAge,
ProofPrevalidation: DefaultProofPrevalidation,
CtxCacheSize: DefaultCtxCacheSize,
ABCILogging: DefaultABCILogging,
RelayErrors: DefaultRelayErrors,
DisableTxEvents: DefaultRPCDisableTransactionEvents,
IavlCacheSize: DefaultIavlCacheSize,
ChainsHotReload: DefaultChainHotReload,
GenerateTokenOnStart: DefaultGenerateTokenOnStart,
LeanPocket: DefaultLeanPocket,
LeanPocketUserKeyFileName: DefaultLeanPocketUserKeyFileName,
DataDir: dataDir,
GenesisName: DefaultGenesisName,
ChainsName: DefaultChainsName,
EvidenceDBName: DefaultEvidenceDBName,
TendermintURI: DefaultTMURI,
KeybaseName: DefaultKeybaseName,
RPCPort: DefaultRPCPort,
ClientBlockSyncAllowance: DefaultClientBlockSyncAllowance,
ClientSessionSyncAllowance: DefaultSessionSyncAllowance,
MaxEvidenceCacheEntires: DefaultMaxEvidenceCacheEntries,
MaxSessionCacheEntries: DefaultMaxSessionCacheEntries,
JSONSortRelayResponses: DefaultJSONSortRelayResponses,
RemoteCLIURL: DefaultRemoteCLIURL,
UserAgent: DefaultUserAgent,
ValidatorCacheSize: DefaultValidatorCacheSize,
ApplicationCacheSize: DefaultApplicationCacheSize,
RPCTimeout: DefaultRPCTimeout,
PrometheusAddr: DefaultPocketPrometheusListenAddr,
PrometheusMaxOpenfiles: DefaultPrometheusMaxOpenFile,
MaxClaimAgeForProofRetry: DefaultMaxClaimProofRetryAge,
ProofPrevalidation: DefaultProofPrevalidation,
CtxCacheSize: DefaultCtxCacheSize,
ABCILogging: DefaultABCILogging,
RelayErrors: DefaultRelayErrors,
DisableTxEvents: DefaultRPCDisableTransactionEvents,
IavlCacheSize: DefaultIavlCacheSize,
ChainsHotReload: DefaultChainHotReload,
GenerateTokenOnStart: DefaultGenerateTokenOnStart,
LeanPocket: DefaultLeanPocket,
LeanPocketUserKeyFileName: DefaultLeanPocketUserKeyFileName,
},
}
c.TendermintConfig.LevelDBOptions = config.DefaultLevelDBOpts()
Expand Down
4 changes: 4 additions & 0 deletions types/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ func GetCacheKey(height int, value string) (key string) {
return key
}

func IsBetween(target, minInclusive, maxInclusive int64) bool {
return minInclusive <= target && target <= maxInclusive
}

// SortedJSON takes any JSON and returns it sorted by keys. Also, all white-spaces
// are removed.
// This method can be used to canonicalize JSON to be returned by GetSignBytes,
Expand Down
10 changes: 8 additions & 2 deletions x/pocketcore/keeper/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ import (
// HandleRelay handles an api (read/write) request to a non-native (external) blockchain
nodiesBlade marked this conversation as resolved.
Show resolved Hide resolved
func (k Keeper) HandleRelay(ctx sdk.Ctx, relay pc.Relay) (*pc.RelayResponse, sdk.Error) {
relayTimeStart := time.Now()
// get the latest session block height because this relay will correspond with the latest session
sessionBlockHeight := k.GetLatestSessionBlockHeight(ctx)

sessionBlockHeight := relay.Proof.SessionBlockHeight
nodiesBlade marked this conversation as resolved.
Show resolved Hide resolved

if !k.IsProofSessionHeightWithinTolerance(ctx, sessionBlockHeight) {
// For legacy support, we are intentionally returning the invalid block height error.
return nil, pc.NewInvalidBlockHeightError(pc.ModuleName)
nodiesBlade marked this conversation as resolved.
Show resolved Hide resolved
}

var node *pc.PocketNode
// There is reference to node address so that way we don't have to recreate address twice for pre-leanpokt
var nodeAddress sdk.Address
Expand Down
103 changes: 96 additions & 7 deletions x/pocketcore/keeper/service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,15 +119,20 @@ func TestKeeper_HandleRelay(t *testing.T) {
ctx, keeper, kvkeys, clientPrivateKey, appPrivateKey, nodePubKey, chain :=
setupHandleRelayTest(t)

SessionRangesToTest := 2
// Eliminate the impact of ClientBlockSyncAllowance
// Store the original allowances to clean up at the end of this test
originalClientBlockSyncAllowance := types.GlobalPocketConfig.ClientBlockSyncAllowance
originalClientSessionSyncAllowance := types.GlobalPocketConfig.ClientSessionSyncAllowance

// Eliminate the impact of ClientBlockSyncAllowance to avoid any relay meta validation errors (OutOfSyncError)
types.GlobalPocketConfig.ClientBlockSyncAllowance = 10000

nodeBlockHeight := ctx.BlockHeight()
blocksPerSesssion := keeper.BlocksPerSession(ctx)
latestSessionHeight := keeper.GetLatestSessionBlockHeight(ctx)

t.Cleanup(func() {
types.GlobalPocketConfig.ClientBlockSyncAllowance = originalClientBlockSyncAllowance
types.GlobalPocketConfig.ClientSessionSyncAllowance = originalClientSessionSyncAllowance
gock.Off() // Flush pending mocks after test execution
})

Expand All @@ -137,8 +142,11 @@ func TestKeeper_HandleRelay(t *testing.T) {
mockCtx.On("BlockHeight").Return(ctx.BlockHeight())
mockCtx.On("Logger").Return(ctx.Logger())
mockCtx.On("PrevCtx", nodeBlockHeight).Return(ctx, nil)

allSessionRangesTests := 4 // The range of block heights we will mock

// Set up mocks for heights we'll query later.
for i := int64(1); i <= blocksPerSesssion*int64(SessionRangesToTest); i++ {
for i := int64(1); i <= blocksPerSesssion*int64(allSessionRangesTests); i++ {
mockCtx.On("PrevCtx", nodeBlockHeight-i).Return(ctx, nil)
mockCtx.On("PrevCtx", nodeBlockHeight+i).Return(ctx, nil)
}
Expand All @@ -154,14 +162,16 @@ func TestKeeper_HandleRelay(t *testing.T) {
nodePubKey,
chain,
)
assert.Nil(t, err, err)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.NotEmpty(t, resp)
assert.Equal(t, resp.Response, "bar")

// Client is behind or advanced beyond Node's height
// --> CodeInvalidBlockHeightError
for i := 1; i <= SessionRangesToTest; i++ {
// TC 1:
// Client is behind or advanced beyond Node's height with ClientSessionSyncAllowance 0
// --> CodeInvalidBlockHeightError
types.GlobalPocketConfig.ClientSessionSyncAllowance = 0
for i := 1; i <= allSessionRangesTests; i++ {
resp, err = testRelayAt(
t,
mockCtx,
Expand Down Expand Up @@ -191,4 +201,83 @@ func TestKeeper_HandleRelay(t *testing.T) {
assert.Equal(t, err.Codespace(), sdk.CodespaceType(types.ModuleName))
assert.Equal(t, err.Code(), sdk.CodeType(types.CodeInvalidBlockHeightError))
}

// TC2:
// Test a relay while one session behind and forward,
// while ClientSessionSyncAllowance = 1
// --> Success on one session behind
// --> InvalidBlockHeightError on one session forward
sessionRangeTc := 1
types.GlobalPocketConfig.ClientSessionSyncAllowance = int64(sessionRangeTc)

// First test the minimum boundary
nodiesBlade marked this conversation as resolved.
Show resolved Hide resolved
resp, err = testRelayAt(
t,
mockCtx,
keeper,
latestSessionHeight-blocksPerSesssion*int64(sessionRangeTc),
clientPrivateKey,
appPrivateKey,
nodePubKey,
chain,
)
assert.Nil(t, err)
assert.NotNil(t, resp)
assert.NotEmpty(t, resp)
assert.Equal(t, resp.Response, "bar")

// Second test the maximum boundary - Error
resp, err = testRelayAt(
t,
mockCtx,
keeper,
latestSessionHeight+blocksPerSesssion*int64(sessionRangeTc),
clientPrivateKey,
appPrivateKey,
nodePubKey,
chain,
)
assert.Nil(t, resp)
assert.NotNil(t, err)
assert.Equal(t, err.Codespace(), sdk.CodespaceType(types.ModuleName))
assert.Equal(t, err.Code(), sdk.CodeType(types.CodeInvalidBlockHeightError))

// TC2:
// Test a relay while two sessions behind and forward,
// while ClientSessionSyncAllowance = 1
// --> InvalidBlockHeightError on two sessions behind and forwards
sessionRangeTc = 2
types.GlobalPocketConfig.ClientSessionSyncAllowance = 1

// First test two sessions back - Error
resp, err = testRelayAt(
nodiesBlade marked this conversation as resolved.
Show resolved Hide resolved
t,
mockCtx,
keeper,
latestSessionHeight-blocksPerSesssion*int64(sessionRangeTc),
clientPrivateKey,
appPrivateKey,
nodePubKey,
chain,
)
assert.Nil(t, resp)
assert.NotNil(t, err)
assert.Equal(t, err.Codespace(), sdk.CodespaceType(types.ModuleName))
assert.Equal(t, err.Code(), sdk.CodeType(types.CodeInvalidBlockHeightError))

// Second test two sessions forward - Error
resp, err = testRelayAt(
t,
mockCtx,
keeper,
latestSessionHeight+blocksPerSesssion*int64(sessionRangeTc),
clientPrivateKey,
appPrivateKey,
nodePubKey,
chain,
)
assert.Nil(t, resp)
assert.NotNil(t, err)
assert.Equal(t, err.Codespace(), sdk.CodespaceType(types.ModuleName))
assert.Equal(t, err.Code(), sdk.CodeType(types.CodeInvalidBlockHeightError))
}
12 changes: 12 additions & 0 deletions x/pocketcore/keeper/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ func (k Keeper) IsSessionBlock(ctx sdk.Ctx) bool {
return ctx.BlockHeight()%k.posKeeper.BlocksPerSession(ctx) == 1
}

// IsProofSessionHeightWithinTolerance checks if the relaySessionBlockHeight is bounded by (latestSessionBlockHeight - tolerance ) <= x <= latestSessionHeight
func (k Keeper) IsProofSessionHeightWithinTolerance(ctx sdk.Ctx, relaySessionBlockHeight int64) bool {
// Session block height can never be zero.
if relaySessionBlockHeight <= 0 {
return false
}
latestSessionHeight := k.GetLatestSessionBlockHeight(ctx)
tolerance := types.GlobalPocketConfig.ClientSessionSyncAllowance * k.posKeeper.BlocksPerSession(ctx)
minHeight := latestSessionHeight - tolerance
return sdk.IsBetween(relaySessionBlockHeight, minHeight, latestSessionHeight)
}

// "GetLatestSessionBlockHeight" - Returns the latest session block height (first block of the session, (see blocksPerSession))
func (k Keeper) GetLatestSessionBlockHeight(ctx sdk.Ctx) (sessionBlockHeight int64) {
// get the latest block height
Expand Down