diff --git a/.circleci/config.yml b/.circleci/config.yml index 986a6253a..e04863961 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -46,8 +46,8 @@ jobs: key: pocket-core-build-{{ .Environment.CIRCLE_SHA1 }} # Run tests - run: - name: 'test' - command: go test -short -v -p 1 ./... + name: "test" + command: go test -short -v -p 1 ./... -timeout=15m no_output_timeout: 20m # Job to trigger the Pocket Core deployments CI with a specific branch trigger-pocket-core-deployments-branches: diff --git a/codec/codec.go b/codec/codec.go index d9342a88c..84febd38a 100644 --- a/codec/codec.go +++ b/codec/codec.go @@ -49,6 +49,7 @@ const ( UpgradeCodecUpdateKey = "CODEC" ValidatorSplitUpdateKey = "SPLIT" NonCustodialUpdateKey = "NCUST" + EnforceMaxChainsUpdateKey = "MAXCH" TxCacheEnhancementKey = "REDUP" MaxRelayProtKey = "MREL" ReplayBurnKey = "REPBR" @@ -291,6 +292,13 @@ func (cdc *Codec) IsOnNonCustodialUpgrade(height int64) bool { return (UpgradeFeatureMap[NonCustodialUpdateKey] != 0 && height == UpgradeFeatureMap[NonCustodialUpdateKey]) || TestMode <= -3 } +// IsAfterEnforceMaxChainsUpgrade Note: includes the actual upgrade height +func (cdc *Codec) IsAfterEnforceMaxChainsUpgrade(height int64) bool { + return (UpgradeFeatureMap[EnforceMaxChainsUpdateKey] != 0 && + height >= UpgradeFeatureMap[EnforceMaxChainsUpdateKey]) || + TestMode <= -3 +} + // IsAfterNamedFeatureActivationHeight Note: includes the actual upgrade height func (cdc *Codec) IsAfterNamedFeatureActivationHeight(height int64, key string) bool { return UpgradeFeatureMap[key] != 0 && height >= UpgradeFeatureMap[key] @@ -323,7 +331,7 @@ func (cdc *Codec) IsOnNamedFeatureActivationHeightWithTolerance( // SliceToExistingMap merge slice to existing map func SliceToExistingMap(arr []string, m map[string]int64) map[string]int64 { - var fmap = make(map[string]int64) + fmap := make(map[string]int64) for k, v := range m { fmap[k] = v } @@ -337,7 +345,7 @@ func SliceToExistingMap(arr []string, m map[string]int64) map[string]int64 { // SliceToMap converts slice to map func SliceToMap(arr []string) map[string]int64 { - var fmap = make(map[string]int64) + fmap := make(map[string]int64) for _, v := range arr { kv := strings.Split(v, ":") i, _ := strconv.ParseInt(kv[1], 10, 64) @@ -348,7 +356,7 @@ func SliceToMap(arr []string) map[string]int64 { // MapToSlice converts map to slice func MapToSlice(m map[string]int64) []string { - var fslice = make([]string, 0) + fslice := make([]string, 0) for k, v := range m { kv := fmt.Sprintf("%s:%d", k, v) fslice = append(fslice, kv) diff --git a/go.mod b/go.mod index c5b1beaec..4dce16b98 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/gofrs/uuid v4.0.0+incompatible // indirect github.com/golang/snappy v0.0.4 // indirect github.com/google/btree v1.0.0 // indirect - github.com/google/go-cmp v0.5.6 // indirect + github.com/google/go-cmp v0.5.8 // indirect github.com/gorilla/websocket v1.4.2 // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -74,7 +74,7 @@ require ( github.com/willf/bitset v1.1.10 // indirect go.etcd.io/bbolt v1.3.3 // indirect golang.org/x/net v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect + golang.org/x/sys v0.14.0 // indirect golang.org/x/term v0.1.0 // indirect golang.org/x/text v0.4.0 // indirect google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 // indirect diff --git a/go.sum b/go.sum index 2cb51eb54..652526f29 100644 --- a/go.sum +++ b/go.sum @@ -188,8 +188,8 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -594,8 +594,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -662,7 +662,6 @@ golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= diff --git a/x/pocketcore/keeper/claim.go b/x/pocketcore/keeper/claim.go index 9da4ccb5f..5ee12187d 100644 --- a/x/pocketcore/keeper/claim.go +++ b/x/pocketcore/keeper/claim.go @@ -3,19 +3,26 @@ package keeper import ( "encoding/hex" "fmt" - "github.com/pokt-network/pocket-core/codec" "time" + "github.com/tendermint/tendermint/rpc/client" + + "github.com/pokt-network/pocket-core/codec" "github.com/pokt-network/pocket-core/crypto" sdk "github.com/pokt-network/pocket-core/types" "github.com/pokt-network/pocket-core/x/auth" "github.com/pokt-network/pocket-core/x/auth/util" pc "github.com/pokt-network/pocket-core/x/pocketcore/types" - "github.com/tendermint/tendermint/rpc/client" ) // "SendClaimTx" - Automatically sends a claim of work/challenge based on relays or challenges stored. -func (k Keeper) SendClaimTx(ctx sdk.Ctx, keeper Keeper, n client.Client, node *pc.PocketNode, claimTx func(pk crypto.PrivateKey, cliCtx util.CLIContext, txBuilder auth.TxBuilder, header pc.SessionHeader, totalProofs int64, root pc.HashRange, evidenceType pc.EvidenceType) (*sdk.TxResponse, error)) { +func (k Keeper) SendClaimTx( + ctx sdk.Ctx, + keeper Keeper, + n client.Client, + node *pc.PocketNode, + claimTx func(pk crypto.PrivateKey, cliCtx util.CLIContext, txBuilder auth.TxBuilder, header pc.SessionHeader, totalProofs int64, root pc.HashRange, evidenceType pc.EvidenceType) (*sdk.TxResponse, error), +) { // get the private val key (main) account from the keybase address := node.GetAddress() // retrieve the iterator to go through each piece of evidence in storage @@ -51,7 +58,8 @@ func (k Keeper) SendClaimTx(ctx sdk.Ctx, keeper Keeper, n client.Client, node *p } // if the blockchain in the evidence is not supported then delete it because nodes don't get paid/challenged for unsupported blockchains if !k.IsPocketSupportedBlockchain(sessionCtx.WithBlockHeight(evidence.SessionHeader.SessionBlockHeight), evidence.SessionHeader.Chain) { - ctx.Logger().Info(fmt.Sprintf("claim for %s blockchain isn't pocket supported, so will not send. Deleting evidence\n", evidence.SessionHeader.Chain)) + ctx.Logger(). + Info(fmt.Sprintf("claim for %s blockchain isn't pocket supported, so will not send. Deleting evidence\n", evidence.SessionHeader.Chain)) if err := pc.DeleteEvidence(evidence.SessionHeader, evidenceType, node.EvidenceStore); err != nil { ctx.Logger().Debug(err.Error()) } @@ -70,10 +78,15 @@ func (k Keeper) SendClaimTx(ctx sdk.Ctx, keeper Keeper, n client.Client, node *p } app, found := k.GetAppFromPublicKey(sessionCtx, evidence.ApplicationPubKey) if !found { - ctx.Logger().Error(fmt.Sprintf("an error occurred creating the claim transaction with app %s not found with evidence %v", evidence.ApplicationPubKey, evidence)) + ctx.Logger(). + Error(fmt.Sprintf("an error occurred creating the claim transaction with app %s not found with evidence %v", evidence.ApplicationPubKey, evidence)) } // generate the merkle root for this evidence - root := evidence.GenerateMerkleRoot(evidence.SessionHeader.SessionBlockHeight, pc.MaxPossibleRelays(app, k.SessionNodeCount(sessionCtx)).Int64(), node.EvidenceStore) + root := evidence.GenerateMerkleRoot( + evidence.SessionHeader.SessionBlockHeight, + pc.MaxPossibleRelays(app, k.SessionNodeCount(sessionCtx)).Int64(), + node.EvidenceStore, + ) claimTxTotalTime := float64(time.Since(now).Milliseconds()) go func() { pc.GlobalServiceMetric().AddClaimTiming(evidence.SessionHeader.Chain, claimTxTotalTime, &address) @@ -131,6 +144,12 @@ func (k Keeper) ValidateClaim(ctx sdk.Ctx, claim pc.MsgClaim) (err sdk.Error) { return pc.NewOverServiceError(pc.ModuleName) } } + // Ensure that the app is not staked to more than the permitted number of chains + lenAppChains := int64(len(app.GetChains())) + if pc.ModuleCdc.IsAfterEnforceMaxChainsUpgrade(ctx.BlockHeight()) && + lenAppChains > k.appKeeper.MaxChains(sessionContext) { + return pc.NewChainsOverLimitError(pc.ModuleName, lenAppChains, k.appKeeper.MaxChains(sessionContext)) + } // get the session node count for the time of the session sessionNodeCount := int(k.SessionNodeCount(sessionContext)) // check cache @@ -148,7 +167,8 @@ func (k Keeper) ValidateClaim(ctx sdk.Ctx, claim pc.MsgClaim) (err sdk.Error) { // create a new session to validate session, err = pc.NewSession(sessionContext, sessionEndCtx, k.posKeeper, claim.SessionHeader, hex.EncodeToString(hash), sessionNodeCount) if err != nil { - ctx.Logger().Error(fmt.Errorf("could not generate session with public key: %s, for chain: %s", app.GetPublicKey().RawString(), claim.SessionHeader.Chain).Error()) + ctx.Logger(). + Error(fmt.Errorf("could not generate session with public key: %s, for chain: %s", app.GetPublicKey().RawString(), claim.SessionHeader.Chain).Error()) return err } } @@ -316,7 +336,7 @@ func (k Keeper) ClaimIsMature(ctx sdk.Ctx, sessionBlockHeight int64) bool { // "DeleteExpiredClaims" - Deletes the expired (claim expiration > # of session passed since claim genesis) claims func (k Keeper) DeleteExpiredClaims(ctx sdk.Ctx) { - var msg = pc.MsgClaim{} + msg := pc.MsgClaim{} store := ctx.KVStore(k.storeKey) iterator, _ := sdk.KVStorePrefixIterator(store, pc.ClaimKey) defer iterator.Close() diff --git a/x/pocketcore/types/errors.go b/x/pocketcore/types/errors.go index 2b2aa9641..1b702fb3d 100644 --- a/x/pocketcore/types/errors.go +++ b/x/pocketcore/types/errors.go @@ -94,6 +94,7 @@ const ( CodeInvalidExpirationHeightErr = 88 CodeInvalidMerkleRangeError = 89 CodeEvidenceSealed = 90 + CodeChainsOverLimitError = 91 ) var ( @@ -186,6 +187,7 @@ var ( InvalidExpirationHeightErr = errors.New("the expiration height included in the claim message is invalid (should not be set)") InvalidMerkleRangeError = errors.New("the merkle hash range is invalid") SealedEvidenceError = errors.New("the evidence is sealed, either max relays reached or claim already submitted") + ChainsOverLimitError = errors.New("the number of staked chains is over the limit") ) func NewSealedEvidenceError(codespace sdk.CodespaceType) sdk.Error { @@ -195,6 +197,7 @@ func NewSealedEvidenceError(codespace sdk.CodespaceType) sdk.Error { func NewUnsupportedBlockchainError(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeUnsupportedBlockchainError, UnsupportedBlockchainError.Error()) } + func NewNodeNotInSessionError(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeNodeNotInSessionError, NodeNotInSessionError.Error()) } @@ -250,9 +253,11 @@ func NewInvalidRootError(codespace sdk.CodespaceType) sdk.Error { func NewInvalidHashLengthError(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidHashLengthError, InvalidHashLengthError.Error()) } + func NewInvalidNetIDLengthError(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidNetworkIDError, InvalidNetworkIDLengthError.Error()) } + func NewInvalidAppPubKeyError(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidAppPubKeyError, InvalidAppPubKeyError.Error()) } @@ -460,6 +465,7 @@ func NewEmptyProofsError(codespace sdk.CodespaceType) sdk.Error { func NewEmptyBlockIDError(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeEmptyBlockIDError, EmptyBlockIDError.Error()) } + func NewEmptyChainError(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeEmptyChainError, EmptyNonNativeChainError.Error()) } @@ -475,3 +481,7 @@ func NewInvalidTokenError(codespace sdk.CodespaceType, err error) sdk.Error { func NewInvalidPKError(codespace sdk.CodespaceType) sdk.Error { return sdk.NewError(codespace, CodeInvalidPkFileErr, InvalidPkFileErr.Error()) } + +func NewChainsOverLimitError(codespace sdk.CodespaceType, gotChains, maxChains int64) sdk.Error { + return sdk.NewError(codespace, CodeChainsOverLimitError, fmt.Sprintf("%s: got %d, max %d", ChainsOverLimitError.Error(), gotChains, maxChains)) +} diff --git a/x/pocketcore/types/expectedKeepers.go b/x/pocketcore/types/expectedKeepers.go index 834401eae..ab82056c5 100644 --- a/x/pocketcore/types/expectedKeepers.go +++ b/x/pocketcore/types/expectedKeepers.go @@ -26,6 +26,7 @@ type PosKeeper interface { BlocksPerSession(ctx sdk.Ctx) (res int64) StakeDenom(ctx sdk.Ctx) (res string) GetValidatorsByChain(ctx sdk.Ctx, networkID string) (validators []sdk.Address, total int) + MaxChains(ctx sdk.Ctx) (maxChains int64) } type AppsKeeper interface { @@ -34,6 +35,7 @@ type AppsKeeper interface { AllApplications(ctx sdk.Ctx) (applications []appexported.ApplicationI) TotalTokens(ctx sdk.Ctx) sdk.BigInt JailApplication(ctx sdk.Ctx, addr sdk.Address) + MaxChains(ctx sdk.Ctx) (maxChains int64) } type PocketKeeper interface { diff --git a/x/pocketcore/types/service.go b/x/pocketcore/types/service.go index c28d09521..0a47dd5fa 100644 --- a/x/pocketcore/types/service.go +++ b/x/pocketcore/types/service.go @@ -5,14 +5,15 @@ import ( "encoding/hex" "encoding/json" "fmt" - "github.com/pokt-network/pocket-core/crypto" - sdk "github.com/pokt-network/pocket-core/types" - "github.com/pokt-network/pocket-core/x/nodes/exported" "io/ioutil" "log" "net/http" "strings" "time" + + "github.com/pokt-network/pocket-core/crypto" + sdk "github.com/pokt-network/pocket-core/types" + "github.com/pokt-network/pocket-core/x/nodes/exported" ) const DEFAULTHTTPMETHOD = "POST" @@ -25,7 +26,15 @@ type Relay struct { } // "Validate" - Checks the validity of a relay request using store data -func (r *Relay) Validate(ctx sdk.Ctx, posKeeper PosKeeper, appsKeeper AppsKeeper, pocketKeeper PocketKeeper, hb *HostedBlockchains, sessionBlockHeight int64, node *PocketNode) (maxPossibleRelays sdk.BigInt, err sdk.Error) { +func (r *Relay) Validate( + ctx sdk.Ctx, + posKeeper PosKeeper, + appsKeeper AppsKeeper, + pocketKeeper PocketKeeper, + hb *HostedBlockchains, + sessionBlockHeight int64, + node *PocketNode, +) (maxPossibleRelays sdk.BigInt, err sdk.Error) { // validate payload if err := r.Payload.Validate(); err != nil { return sdk.ZeroInt(), NewEmptyPayloadDataError(ModuleName) @@ -56,6 +65,12 @@ func (r *Relay) Validate(ctx sdk.Ctx, posKeeper PosKeeper, appsKeeper AppsKeeper if !found { return sdk.ZeroInt(), NewAppNotFoundError(ModuleName) } + // Ensure that the app is not staked to more than the permitted number of chains + numAppChains := int64(len(app.GetChains())) + if ModuleCdc.IsAfterEnforceMaxChainsUpgrade(ctx.BlockHeight()) && + numAppChains > appsKeeper.MaxChains(sessionCtx) { + return sdk.ZeroInt(), NewChainsOverLimitError(ModuleName, numAppChains, appsKeeper.MaxChains(ctx)) + } // get session node count from that session height sessionNodeCount := pocketKeeper.SessionNodeCount(sessionCtx) // get max possible relays @@ -96,8 +111,7 @@ func (r *Relay) Validate(ctx sdk.Ctx, posKeeper PosKeeper, appsKeeper AppsKeeper // With session rollover, the height of `ctx` may already be in the next // session of the relay's session. In such a case, we need to pass the // correct context of the session end instead of `ctx`. - sessionEndHeight := - sessionBlockHeight + posKeeper.BlocksPerSession(sessionCtx) - 1 + sessionEndHeight := sessionBlockHeight + posKeeper.BlocksPerSession(sessionCtx) - 1 var sesssionEndCtx sdk.Ctx if ctx.BlockHeight() > sessionEndHeight { if sesssionEndCtx, err = ctx.PrevCtx(sessionEndHeight); err != nil { @@ -160,7 +174,7 @@ func (r Relay) Execute(hostedBlockchains *HostedBlockchains, address *sdk.Addres // "Bytes" - Returns the bytes representation of the Relay func (r Relay) Bytes() []byte { - //Anonymous Struct used because of #742 empty proof object being marshalled + // Anonymous Struct used because of #742 empty proof object being marshalled relay := struct { Payload Payload `json:"payload"` // the data payload of the request Meta RelayMeta `json:"meta"` // metadata for the relay request @@ -244,7 +258,8 @@ type RelayMeta struct { // "Validate" - Validates the relay meta object func (m RelayMeta) Validate(ctx sdk.Ctx) sdk.Error { // ensures the block height is within the acceptable range - if ctx.BlockHeight()+int64(GlobalPocketConfig.ClientBlockSyncAllowance) < m.BlockHeight || ctx.BlockHeight()-int64(GlobalPocketConfig.ClientBlockSyncAllowance) > m.BlockHeight { + if ctx.BlockHeight()+int64(GlobalPocketConfig.ClientBlockSyncAllowance) < m.BlockHeight || + ctx.BlockHeight()-int64(GlobalPocketConfig.ClientBlockSyncAllowance) > m.BlockHeight { return NewOutOfSyncRequestError(ModuleName) } return nil diff --git a/x/pocketcore/types/service_test.go b/x/pocketcore/types/service_test.go index 80edf653e..79d7950e6 100644 --- a/x/pocketcore/types/service_test.go +++ b/x/pocketcore/types/service_test.go @@ -24,8 +24,8 @@ func TestRelay_Validate(t *testing.T) { // TODO add overservice, and not unique appPubKey := appPrivateKey.PublicKey().RawString() npk := getRandomPubKey() nodePubKey := npk.RawString() - ethereum := hex.EncodeToString([]byte{01}) - bitcoin := hex.EncodeToString([]byte{02}) + ethereum := hex.EncodeToString([]byte{0o1}) + bitcoin := hex.EncodeToString([]byte{0o2}) p := Payload{ Data: "{\"jsonrpc\":\"2.0\",\"method\":\"web3_clientVersion\",\"params\":[],\"id\":67}", Method: "", @@ -145,7 +145,7 @@ func TestRelay_Execute(t *testing.T) { npk := getRandomPubKey() nodeAddr := sdk.Address(npk.Address()) nodePubKey := npk.RawString() - ethereum := hex.EncodeToString([]byte{01}) + ethereum := hex.EncodeToString([]byte{0o1}) p := Payload{ Data: "foo", Method: "POST", @@ -194,7 +194,7 @@ func TestRelay_HandleProof(t *testing.T) { appPubKey := appPrivateKey.PublicKey().RawString() npk := getRandomPubKey() nodePubKey := npk.RawString() - ethereum := hex.EncodeToString([]byte{01}) + ethereum := hex.EncodeToString([]byte{0o1}) p := Payload{ Data: "foo", Method: "POST", @@ -321,6 +321,10 @@ func (m MockAppsKeeper) JailApplication(ctx sdk.Ctx, addr sdk.Address) { panic("implement me") } +func (m MockAppsKeeper) MaxChains(ctx sdk.Ctx) (res int64) { + return 15 +} + type MockPosKeeper struct { Validators []exported.ValidatorI } @@ -397,3 +401,7 @@ func (m MockPosKeeper) BlocksPerSession(ctx sdk.Ctx) (res int64) { func (m MockPosKeeper) StakeDenom(ctx sdk.Ctx) (res string) { panic("implement me") } + +func (m MockPosKeeper) MaxChains(ctx sdk.Ctx) (res int64) { + return 15 +} diff --git a/x/pocketcore/types/session.go b/x/pocketcore/types/session.go index 788885528..d2c4fde13 100644 --- a/x/pocketcore/types/session.go +++ b/x/pocketcore/types/session.go @@ -4,10 +4,11 @@ import ( "encoding/hex" "encoding/json" "fmt" + "log" + sdk "github.com/pokt-network/pocket-core/types" appexported "github.com/pokt-network/pocket-core/x/apps/exported" "github.com/pokt-network/pocket-core/x/nodes/exported" - "log" ) // "Session" - The relationship between an application and the pocket network @@ -17,11 +18,17 @@ func (s Session) IsSealable() bool { } func (s Session) HashString() string { - return s.HashString() + return s.SessionHeader.HashString() } // "NewSession" - create a new session from seed data -func NewSession(sessionCtx, ctx sdk.Ctx, keeper PosKeeper, sessionHeader SessionHeader, blockHash string, sessionNodesCount int) (Session, sdk.Error) { +func NewSession( + sessionCtx, ctx sdk.Ctx, + keeper PosKeeper, + sessionHeader SessionHeader, + blockHash string, + sessionNodesCount int, +) (Session, sdk.Error) { // first generate session key sessionKey, err := NewSessionKey(sessionHeader.ApplicationPubKey, sessionHeader.Chain, blockHash) if err != nil { @@ -101,7 +108,17 @@ func (s Session) Key() ([]byte, error) { type SessionNodes []sdk.Address // "NewSessionNodes" - Generates nodes for the session -func NewSessionNodes(sessionCtx, ctx sdk.Ctx, keeper PosKeeper, chain string, sessionKey SessionKey, sessionNodesCount int) (sessionNodes SessionNodes, err sdk.Error) { +func NewSessionNodes( + sessionCtx, ctx sdk.Ctx, + keeper PosKeeper, + chain string, + sessionKey SessionKey, + sessionNodesCount int, +) (sessionNodes SessionNodes, err sdk.Error) { + // retrieve the enforce max chains flag's value from the codec + isEnforceMaxChains := ModuleCdc.IsAfterEnforceMaxChainsUpgrade(ctx.BlockHeight()) + // retrieve the max chains value from the sessionCtx + nodeMaxChains := keeper.MaxChains(sessionCtx) // all nodesAddrs at session genesis nodesAddrs, totalNodes := keeper.GetValidatorsByChain(sessionCtx, chain) // validate nodesAddrs @@ -110,11 +127,11 @@ func NewSessionNodes(sessionCtx, ctx sdk.Ctx, keeper PosKeeper, chain string, se } sessionNodes = make(SessionNodes, sessionNodesCount) var node exported.ValidatorI - //unique address map to avoid re-checking a pseudorandomly selected servicer + // unique address map to avoid re-checking a pseudorandomly selected servicer m := make(map[string]struct{}) // only select the nodesAddrs if not jailed for i, numOfNodes := 0, 0; ; i++ { - //if this is true we already checked all nodes we got on getValidatorsBychain + // if this is true we already checked all nodes we got on getValidatorsBychain if len(m) >= totalNodes { return nil, NewInsufficientNodesError(ModuleName) } @@ -124,17 +141,22 @@ func NewSessionNodes(sessionCtx, ctx sdk.Ctx, keeper PosKeeper, chain string, se sessionKey = Hash(sessionKey) // get the node from the array n := nodesAddrs[index.Int64()] - //if we already have seen this address we continue as it's either on the list or discarded + // if we already have seen this address we continue as it's either on the list or discarded if _, ok := m[n.String()]; ok { continue } - //add the node address to the map + // add the node address to the map m[n.String()] = struct{}{} // cross check the node from the `new` or `end` world state node = keeper.Validator(ctx, n) - // if not found or jailed, don't add to session and continue - if node == nil || node.IsJailed() || !NodeHasChain(chain, node) || sessionNodes.Contains(node.GetAddress()) { + lenNodeChains := int64(len(node.GetChains())) + // if not found or jailed or is overstaked to chains + if node == nil || + (isEnforceMaxChains && lenNodeChains > nodeMaxChains) || + node.IsJailed() || + !NodeHasChain(chain, node) || + sessionNodes.Contains(node.GetAddress()) { continue } // else add the node to the session @@ -267,8 +289,8 @@ func BlockHash(ctx sdk.Context) string { // "MaxPossibleRelays" - Returns the maximum possible amount of relays for an App on a sessions func MaxPossibleRelays(app appexported.ApplicationI, sessionNodeCount int64) sdk.BigInt { - //GetMaxRelays Max value is bound to math.MaxUint64, - //current worse case is 1 chain and 5 nodes per session with a result of 3689348814741910323 which can be used safely as int64 + // GetMaxRelays Max value is bound to math.MaxUint64, + // current worse case is 1 chain and 5 nodes per session with a result of 3689348814741910323 which can be used safely as int64 return app.GetMaxRelays().ToDec().Quo(sdk.NewDec(int64(len(app.GetChains())))).Quo(sdk.NewDec(sessionNodeCount)).RoundInt() } diff --git a/x/pocketcore/types/session_test.go b/x/pocketcore/types/session_test.go index 6f7fb2221..f01124312 100644 --- a/x/pocketcore/types/session_test.go +++ b/x/pocketcore/types/session_test.go @@ -2,8 +2,16 @@ package types import ( "encoding/hex" - "github.com/stretchr/testify/assert" "testing" + "time" + + "github.com/stretchr/testify/assert" + + "github.com/pokt-network/pocket-core/codec" + "github.com/pokt-network/pocket-core/crypto" + sdk "github.com/pokt-network/pocket-core/types" + "github.com/pokt-network/pocket-core/x/nodes/exported" + nodesTypes "github.com/pokt-network/pocket-core/x/nodes/types" ) func TestNewSessionKey(t *testing.T) { @@ -35,216 +43,255 @@ func TestSessionKey_Validate(t *testing.T) { assert.Nil(t, realKey.Validate()) } -//func TestNewSessionNodes(t *testing.T) { -// fakeSessionKey, err := hex.DecodeString("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey1, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab81") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey2, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab82") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey3, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab83") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey4, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab84") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey5, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab85") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey6, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab86") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey7, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab87") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey8, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab88") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey9, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab89") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey10, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab8A") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey11, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab8B") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// fakePubKey12, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab8C") -// if err != nil { -// t.Fatalf(err.Error()) -// } -// ethereum := hex.EncodeToString([]byte{01}) -// var allNodes []exported.ValidatorI -// node12 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey12.Address()), -// PublicKey: fakePubKey12, -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node1 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey1.Address()), -// PublicKey: (fakePubKey1), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node2 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey2.Address()), -// PublicKey: (fakePubKey2), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node3 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey3.Address()), -// PublicKey: (fakePubKey3), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node4 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey4.Address()), -// PublicKey: (fakePubKey4), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node5 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey5.Address()), -// PublicKey: (fakePubKey5), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node6 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey6.Address()), -// PublicKey: (fakePubKey6), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node7 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey7.Address()), -// PublicKey: (fakePubKey7), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node8 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey8.Address()), -// PublicKey: (fakePubKey8), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node9 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey9.Address()), -// PublicKey: (fakePubKey9), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node10 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey10.Address()), -// PublicKey: (fakePubKey10), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// node11 := nodesTypes.Validator{ -// Address: sdk.Address(fakePubKey11.Address()), -// PublicKey: (fakePubKey11), -// Jailed: false, -// Status: sdk.Staked, -// Chains: []string{ethereum}, -// ServiceUrl: "https://www.google.com:443", -// StakedTokens: sdk.NewInt(100000), -// UnstakingCompletionTime: time.Time{}, -// } -// allNodes = make([]exported.ValidatorI, 12) -// allNodes[0] = node12 -// allNodes[1] = node1 -// allNodes[2] = node2 -// allNodes[3] = node3 -// allNodes[4] = node4 -// allNodes[5] = node5 -// allNodes[6] = node6 -// allNodes[7] = node7 -// allNodes[8] = node8 -// allNodes[9] = node9 -// allNodes[10] = node10 -// allNodes[11] = node11 -// k := MockPosKeeper{Validators: allNodes} -// sessionNodes, err := NewSessionNodes(newContext(t, false).WithAppVersion("0.0.0"), newContext(t, false).WithAppVersion("0.0.0"), k, ethereum, fakeSessionKey, 5) -// assert.Nil(t, err) -// assert.Len(t, sessionNodes, 5) -// assert.Contains(t, sessionNodes, allNodes[0].(nodesTypes.Validator)) -// assert.Contains(t, sessionNodes, allNodes[1].(nodesTypes.Validator)) -// assert.NotContains(t, sessionNodes, allNodes[2].(nodesTypes.Validator)) -// assert.NotContains(t, sessionNodes, allNodes[3].(nodesTypes.Validator)) -// assert.Contains(t, sessionNodes, allNodes[4].(nodesTypes.Validator)) -// assert.NotContains(t, sessionNodes, allNodes[5].(nodesTypes.Validator)) -// assert.NotContains(t, sessionNodes, allNodes[6].(nodesTypes.Validator)) -// assert.Contains(t, sessionNodes, allNodes[7].(nodesTypes.Validator)) -// assert.Contains(t, sessionNodes, allNodes[8].(nodesTypes.Validator)) -// assert.NotContains(t, sessionNodes, allNodes[9].(nodesTypes.Validator)) -// assert.NotContains(t, sessionNodes, allNodes[10].(nodesTypes.Validator)) -// assert.NotContains(t, sessionNodes, allNodes[11].(nodesTypes.Validator)) -// assert.True(t, sessionNodes.Contains(node12)) -// assert.True(t, sessionNodes.Contains(node8)) -// assert.True(t, sessionNodes.Contains(node7)) -// assert.True(t, sessionNodes.Contains(node4)) -// assert.True(t, sessionNodes.Contains(node1)) -// assert.False(t, sessionNodes.Contains(node2)) -// assert.Nil(t, sessionNodes.Validate(5)) -// assert.NotNil(t, SessionNodes(make([]exported.ValidatorI, 5)).Validate(5)) -//} +func TestNewSessionNodes(t *testing.T) { + fakeSessionKey, err := hex.DecodeString("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab80") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey1, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab81") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey2, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab82") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey3, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab83") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey4, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab84") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey5, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab85") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey6, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab86") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey7, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab87") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey8, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab88") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey9, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab89") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey10, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab8A") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey11, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab8B") + if err != nil { + t.Fatalf(err.Error()) + } + fakePubKey12, err := crypto.NewPublicKey("36f028580bb02cc8272a9a020f4200e346e276ae664e45ee80745574e2f5ab8C") + if err != nil { + t.Fatalf(err.Error()) + } + ethereum := hex.EncodeToString([]byte{01}) + var allNodes []exported.ValidatorI + node12 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey12.Address()), + PublicKey: fakePubKey12, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + ethereum, + }, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node1 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey1.Address()), + PublicKey: fakePubKey1, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node2 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey2.Address()), + PublicKey: fakePubKey2, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node3 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey3.Address()), + PublicKey: fakePubKey3, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node4 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey4.Address()), + PublicKey: fakePubKey4, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node5 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey5.Address()), + PublicKey: fakePubKey5, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node6 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey6.Address()), + PublicKey: fakePubKey6, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node7 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey7.Address()), + PublicKey: fakePubKey7, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node8 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey8.Address()), + PublicKey: fakePubKey8, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node9 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey9.Address()), + PublicKey: fakePubKey9, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node10 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey10.Address()), + PublicKey: fakePubKey10, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + node11 := nodesTypes.Validator{ + Address: sdk.Address(fakePubKey11.Address()), + PublicKey: fakePubKey11, + Jailed: false, + Status: sdk.Staked, + Chains: []string{ethereum}, + ServiceURL: "https://www.google.com:443", + StakedTokens: sdk.NewInt(100000), + UnstakingCompletionTime: time.Time{}, + } + allNodes = make([]exported.ValidatorI, 12) + allNodes[0] = node12 + allNodes[1] = node1 + allNodes[2] = node2 + allNodes[3] = node3 + allNodes[4] = node4 + allNodes[5] = node5 + allNodes[6] = node6 + allNodes[7] = node7 + allNodes[8] = node8 + allNodes[9] = node9 + allNodes[10] = node10 + allNodes[11] = node11 + k := MockPosKeeper{Validators: allNodes} + ctx := newContext(t, false) + sessionCtx := newContext(t, false) + sessionNodes, err := NewSessionNodes( + sessionCtx, + ctx, + k, + ethereum, + fakeSessionKey, + 5, + ) + assert.Nil(t, err) + assert.Len(t, sessionNodes, 5) + assert.True(t, sessionNodes.Contains(node12.Address)) + assert.True(t, sessionNodes.Contains(node9.Address)) + assert.True(t, sessionNodes.Contains(node6.Address)) + assert.True(t, sessionNodes.Contains(node2.Address)) + assert.True(t, sessionNodes.Contains(node1.Address)) + assert.Nil(t, sessionNodes.Validate(5)) + assert.NotNil(t, SessionNodes(make([]sdk.Address, 5)).Validate(5)) + + // Test for after EnforceMaxChainsUpdateKey is activated + maxChainsUpgradeHeight := int64(10) + codec.UpgradeFeatureMap[codec.EnforceMaxChainsUpdateKey] = maxChainsUpgradeHeight + + ctx = ctx.WithBlockHeight(maxChainsUpgradeHeight) + sessionCtx = sessionCtx.WithBlockHeight(maxChainsUpgradeHeight) + + sessionNodes, err = NewSessionNodes( + sessionCtx, + ctx, + k, + ethereum, + fakeSessionKey, + 5, + ) + assert.Nil(t, err) + assert.Len(t, sessionNodes, 5) + assert.False(t, sessionNodes.Contains(node12.Address)) + assert.True(t, sessionNodes.Contains(node3.Address)) + assert.True(t, sessionNodes.Contains(node9.Address)) + assert.True(t, sessionNodes.Contains(node6.Address)) + assert.True(t, sessionNodes.Contains(node2.Address)) + assert.True(t, sessionNodes.Contains(node1.Address)) + assert.Nil(t, sessionNodes.Validate(5)) + assert.NotNil(t, SessionNodes(make([]sdk.Address, 5)).Validate(5)) +}