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

highlight nodes shuffled out to auction list #6166

Merged
merged 10 commits into from
May 17, 2024
5 changes: 5 additions & 0 deletions epochStart/bootstrap/disabled/disabledNodesCoordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func (n *nodesCoordinator) GetAllShuffledOutValidatorsPublicKeys(_ uint32) (map[
return nil, nil
}

// GetShuffledOutToAuctionValidatorsPublicKeys -
func (n *nodesCoordinator) GetShuffledOutToAuctionValidatorsPublicKeys(_ uint32) (map[uint32][][]byte, error) {
return nil, nil
}

// GetConsensusValidatorsPublicKeys -
func (n *nodesCoordinator) GetConsensusValidatorsPublicKeys(_ []byte, _ uint64, _ uint32, _ uint32) ([]string, error) {
return nil, nil
Expand Down
21 changes: 11 additions & 10 deletions integrationTests/chainSimulator/staking/stake/simpleStake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (

"github.com/multiversx/mx-chain-core-go/core"
"github.com/multiversx/mx-chain-core-go/data/transaction"
"github.com/stretchr/testify/require"

"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/config"
"github.com/multiversx/mx-chain-go/integrationTests/chainSimulator/staking"
Expand All @@ -17,7 +19,6 @@ import (
"github.com/multiversx/mx-chain-go/node/chainSimulator/configs"
"github.com/multiversx/mx-chain-go/node/chainSimulator/process"
"github.com/multiversx/mx-chain-go/vm"
"github.com/stretchr/testify/require"
)

// Test scenarios
Expand Down Expand Up @@ -261,30 +262,30 @@ func TestChainSimulator_StakingV4Step2APICalls(t *testing.T) {
err = cs.GenerateBlocks(2)
require.Nil(t, err)

numQualified, numUnQualified := getNumQualifiedAndUnqualified(t, metachainNode)
require.Equal(t, 8, numQualified)
require.Equal(t, 1, numUnQualified)
qualified, unQualified := getQualifiedAndUnqualifiedNodes(t, metachainNode)
require.Equal(t, 8, len(qualified))
require.Equal(t, 1, len(unQualified))
}
}

func getNumQualifiedAndUnqualified(t *testing.T, metachainNode process.NodeHandler) (int, int) {
func getQualifiedAndUnqualifiedNodes(t *testing.T, metachainNode process.NodeHandler) ([]string, []string) {
err := metachainNode.GetProcessComponents().ValidatorsProvider().ForceUpdate()
require.Nil(t, err)
auctionList, err := metachainNode.GetProcessComponents().ValidatorsProvider().GetAuctionList()
require.Nil(t, err)

numQualified := 0
numUnQualified := 0
qualified := make([]string, 0)
unQualified := make([]string, 0)

for _, auctionOwnerData := range auctionList {
for _, auctionNode := range auctionOwnerData.Nodes {
if auctionNode.Qualified {
numQualified++
qualified = append(qualified, auctionNode.BlsKey)
} else {
numUnQualified++
unQualified = append(unQualified, auctionNode.BlsKey)
}
}
}

return numQualified, numUnQualified
return qualified, unQualified
}
159 changes: 142 additions & 17 deletions integrationTests/chainSimulator/staking/stake/stakeAndUnStake_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import (
coreAPI "github.com/multiversx/mx-chain-core-go/data/api"
"github.com/multiversx/mx-chain-core-go/data/transaction"
"github.com/multiversx/mx-chain-core-go/data/validator"
logger "github.com/multiversx/mx-chain-logger-go"
"github.com/stretchr/testify/require"

"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/config"
chainSimulatorIntegrationTests "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator"
Expand All @@ -23,8 +26,6 @@ import (
chainSimulatorProcess "github.com/multiversx/mx-chain-go/node/chainSimulator/process"
"github.com/multiversx/mx-chain-go/process"
"github.com/multiversx/mx-chain-go/vm"
logger "github.com/multiversx/mx-chain-logger-go"
"github.com/stretchr/testify/require"
)

const (
Expand Down Expand Up @@ -2366,24 +2367,153 @@ func TestChainSimulator_UnStakeOneActiveNodeAndCheckAPIAuctionList(t *testing.T)

metachainNode := cs.GetNodeHandler(core.MetachainShardId)

numQualified, numUnQualified := getNumQualifiedAndUnqualified(t, metachainNode)
require.Equal(t, 8, numQualified)
require.Equal(t, 0, numUnQualified)
qualified, unQualified := getQualifiedAndUnqualifiedNodes(t, metachainNode)
require.Equal(t, 8, len(qualified))
require.Equal(t, 0, len(unQualified))

stakeOneNode(t, cs)

numQualified, numUnQualified = getNumQualifiedAndUnqualified(t, metachainNode)
require.Equal(t, 8, numQualified)
require.Equal(t, 1, numUnQualified)
qualified, unQualified = getQualifiedAndUnqualifiedNodes(t, metachainNode)
require.Equal(t, 8, len(qualified))
require.Equal(t, 1, len(unQualified))

unStakeOneActiveNode(t, cs)

numQualified, numUnQualified = getNumQualifiedAndUnqualified(t, metachainNode)
require.Equal(t, 8, numQualified)
require.Equal(t, 1, numUnQualified)
qualified, unQualified = getQualifiedAndUnqualifiedNodes(t, metachainNode)
require.Equal(t, 8, len(qualified))
require.Equal(t, 1, len(unQualified))
}

// Nodes configuration at genesis consisting of a total of 40 nodes, distributed on 3 shards + meta:
// - 4 eligible nodes/shard
// - 4 waiting nodes/shard
// - 2 nodes to shuffle per shard
// - max num nodes config for stakingV4 step3 = 32 (being downsized from previously 40 nodes)
// - with this config, we should always select max 8 nodes from auction list if there are > 40 nodes in the network
// This test will run with only 32 nodes and check that there are no nodes in the auction list,
// because of the lowWaitingList condition being triggered when in staking v4
func TestChainSimulator_EdgeCaseLowWaitingList(t *testing.T) {
if testing.Short() {
t.Skip("this is not a short test")
}

startTime := time.Now().Unix()
roundDurationInMillis := uint64(6000)
roundsPerEpoch := core.OptionalUint64{
HasValue: true,
Value: 20,
}

stakingV4Step1Epoch := uint32(2)
stakingV4Step2Epoch := uint32(3)
stakingV4Step3Epoch := uint32(4)

numOfShards := uint32(3)
cs, err := chainSimulator.NewChainSimulator(chainSimulator.ArgsChainSimulator{
BypassTxSignatureCheck: false,
TempDir: t.TempDir(),
PathToInitialConfig: defaultPathToInitialConfig,
NumOfShards: numOfShards,
GenesisTimestamp: startTime,
RoundDurationInMillis: roundDurationInMillis,
RoundsPerEpoch: roundsPerEpoch,
ApiInterface: api.NewNoApiInterface(),
MinNodesPerShard: 4,
MetaChainMinNodes: 4,
NumNodesWaitingListMeta: 2,
NumNodesWaitingListShard: 2,
AlterConfigsFunction: func(cfg *config.Configs) {
cfg.EpochConfig.EnableEpochs.StakingV4Step1EnableEpoch = stakingV4Step1Epoch
cfg.EpochConfig.EnableEpochs.StakingV4Step2EnableEpoch = stakingV4Step2Epoch
cfg.EpochConfig.EnableEpochs.StakingV4Step3EnableEpoch = stakingV4Step3Epoch

cfg.EpochConfig.EnableEpochs.MaxNodesChangeEnableEpoch[1].MaxNumNodes = 40
cfg.EpochConfig.EnableEpochs.MaxNodesChangeEnableEpoch[1].NodesToShufflePerShard = 2

cfg.EpochConfig.EnableEpochs.MaxNodesChangeEnableEpoch[2].EpochEnable = stakingV4Step3Epoch
cfg.EpochConfig.EnableEpochs.MaxNodesChangeEnableEpoch[2].MaxNumNodes = 32
cfg.EpochConfig.EnableEpochs.MaxNodesChangeEnableEpoch[2].NodesToShufflePerShard = 2
},
})
require.Nil(t, err)
require.NotNil(t, cs)

defer cs.Close()

epochToCheck := int32(stakingV4Step3Epoch + 1)
err = cs.GenerateBlocksUntilEpochIsReached(epochToCheck)
require.Nil(t, err)

metachainNode := cs.GetNodeHandler(core.MetachainShardId)
qualified, unQualified := getQualifiedAndUnqualifiedNodes(t, metachainNode)
require.Equal(t, 0, len(qualified))
require.Equal(t, 0, len(unQualified))

// we always have 0 in auction list because of the lowWaitingList condition
epochToCheck += 1
err = cs.GenerateBlocksUntilEpochIsReached(epochToCheck)
require.Nil(t, err)

qualified, unQualified = getQualifiedAndUnqualifiedNodes(t, metachainNode)
require.Equal(t, 0, len(qualified))
require.Equal(t, 0, len(unQualified))

// stake 16 mode nodes, these will go to auction list
stakeNodes(t, cs, 17)

epochToCheck += 1
err = cs.GenerateBlocksUntilEpochIsReached(epochToCheck)
require.Nil(t, err)

qualified, unQualified = getQualifiedAndUnqualifiedNodes(t, metachainNode)
// all the previously registered will be selected, as we have 24 nodes in eligible+waiting, 8 will shuffle out,
// but this time there will be not be lowWaitingList, as there are enough in auction, so we will end up with
// 24-8 = 16 nodes remaining + 16 from auction, to fill up all 32 positions
require.Equal(t, 16, len(qualified))
require.Equal(t, 1, len(unQualified))

shuffledOutNodesKeys, err := metachainNode.GetProcessComponents().NodesCoordinator().GetShuffledOutToAuctionValidatorsPublicKeys(uint32(epochToCheck))
require.Nil(t, err)

checkKeysNotInMap(t, shuffledOutNodesKeys, qualified)
checkKeysNotInMap(t, shuffledOutNodesKeys, unQualified)
}

func checkKeysNotInMap(t *testing.T, m map[uint32][][]byte, keys []string) {
for _, key := range keys {
for _, v := range m {
for _, k := range v {
mapKey := hex.EncodeToString(k)
require.NotEqual(t, key, mapKey)
}
}
}
}

func stakeNodes(t *testing.T, cs chainSimulatorIntegrationTests.ChainSimulator, numNodesToStake int) {
txs := make([]*transaction.Transaction, numNodesToStake)
for i := 0; i < numNodesToStake; i++ {
txs[i] = createStakeTransaction(t, cs)
}

stakeTxs, err := cs.SendTxsAndGenerateBlocksTilAreExecuted(txs, staking.MaxNumOfBlockToGenerateWhenExecutingTx)
require.Nil(t, err)
require.NotNil(t, stakeTxs)
require.Len(t, stakeTxs, numNodesToStake)

require.Nil(t, cs.GenerateBlocks(1))
}

func stakeOneNode(t *testing.T, cs chainSimulatorIntegrationTests.ChainSimulator) {
txStake := createStakeTransaction(t, cs)
stakeTx, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(txStake, staking.MaxNumOfBlockToGenerateWhenExecutingTx)
require.Nil(t, err)
require.NotNil(t, stakeTx)

require.Nil(t, cs.GenerateBlocks(1))
}

func createStakeTransaction(t *testing.T, cs chainSimulatorIntegrationTests.ChainSimulator) *transaction.Transaction {
privateKey, blsKeys, err := chainSimulator.GenerateBlsPrivateKeys(1)
require.Nil(t, err)
err = cs.AddValidatorKeys(privateKey)
Expand All @@ -2394,12 +2524,7 @@ func stakeOneNode(t *testing.T, cs chainSimulatorIntegrationTests.ChainSimulator
require.Nil(t, err)

txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature)
txStake := staking.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, staking.MinimumStakeValue, txDataField, staking.GasLimitForStakeOperation)
stakeTx, err := cs.SendTxAndGenerateBlockTilTxIsExecuted(txStake, staking.MaxNumOfBlockToGenerateWhenExecutingTx)
require.Nil(t, err)
require.NotNil(t, stakeTx)

require.Nil(t, cs.GenerateBlocks(1))
return staking.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, staking.MinimumStakeValue, txDataField, staking.GasLimitForStakeOperation)
}

func unStakeOneActiveNode(t *testing.T, cs chainSimulatorIntegrationTests.ChainSimulator) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ import (
"github.com/multiversx/mx-chain-crypto-go/signing"
"github.com/multiversx/mx-chain-crypto-go/signing/mcl"
mclsig "github.com/multiversx/mx-chain-crypto-go/signing/mcl/singlesig"
logger "github.com/multiversx/mx-chain-logger-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/config"
chainSimulatorIntegrationTests "github.com/multiversx/mx-chain-go/integrationTests/chainSimulator"
Expand All @@ -25,9 +29,6 @@ import (
chainSimulatorProcess "github.com/multiversx/mx-chain-go/node/chainSimulator/process"
"github.com/multiversx/mx-chain-go/process"
"github.com/multiversx/mx-chain-go/vm"
logger "github.com/multiversx/mx-chain-logger-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

var log = logger.GetOrCreate("stakingProvider")
Expand Down Expand Up @@ -397,13 +398,19 @@ func testBLSKeyIsInAuction(
require.Nil(t, err)

currentEpoch := metachainNode.GetCoreComponents().EnableEpochsHandler().GetCurrentEpoch()
if metachainNode.GetCoreComponents().EnableEpochsHandler().GetActivationEpoch(common.StakingV4Step2Flag) == currentEpoch {

shuffledToAuctionValPubKeys, err := metachainNode.GetProcessComponents().NodesCoordinator().GetShuffledOutToAuctionValidatorsPublicKeys(currentEpoch)
require.Nil(t, err)

stakingV4Step2Epoch := metachainNode.GetCoreComponents().EnableEpochsHandler().GetActivationEpoch(common.StakingV4Step2Flag)
stakingV4Step3Epoch := metachainNode.GetCoreComponents().EnableEpochsHandler().GetActivationEpoch(common.StakingV4Step3Flag)
if stakingV4Step2Epoch == currentEpoch || stakingV4Step3Epoch == currentEpoch {
// starting from phase 2, we have the shuffled out nodes from the previous epoch in the action list
actionListSize += 8
}
if metachainNode.GetCoreComponents().EnableEpochsHandler().GetActivationEpoch(common.StakingV4Step3Flag) <= currentEpoch {
// starting from phase 3, we have the shuffled out nodes from the previous epoch in the action list
actionListSize += 4
// if there is no lowWaitingList condition
if len(shuffledToAuctionValPubKeys) > 0 {
// there are 2 nodes per shard shuffled out so 2 * 4 = 8
actionListSize += 8
}
}

require.Equal(t, actionListSize, len(auctionList))
Expand Down
5 changes: 3 additions & 2 deletions process/peer/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
"github.com/multiversx/mx-chain-core-go/data"
"github.com/multiversx/mx-chain-core-go/data/block"
"github.com/multiversx/mx-chain-core-go/marshal"
logger "github.com/multiversx/mx-chain-logger-go"

"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/common/errChan"
"github.com/multiversx/mx-chain-go/common/validatorInfo"
Expand All @@ -23,7 +25,6 @@ import (
"github.com/multiversx/mx-chain-go/state"
"github.com/multiversx/mx-chain-go/state/accounts"
"github.com/multiversx/mx-chain-go/state/parsers"
logger "github.com/multiversx/mx-chain-logger-go"
)

var log = logger.GetOrCreate("process/peer")
Expand Down Expand Up @@ -197,7 +198,7 @@ func (vs *validatorStatistics) saveNodesCoordinatorUpdates(epoch uint32) (bool,
nodeForcedToRemain = nodeForcedToRemain || tmpNodeForcedToRemain

if vs.enableEpochsHandler.IsFlagEnabled(common.StakingV4Step2Flag) {
nodesMap, err = vs.nodesCoordinator.GetAllShuffledOutValidatorsPublicKeys(epoch)
nodesMap, err = vs.nodesCoordinator.GetShuffledOutToAuctionValidatorsPublicKeys(epoch)
if err != nil {
return false, err
}
Expand Down
14 changes: 11 additions & 3 deletions process/peer/process_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ import (
"github.com/multiversx/mx-chain-core-go/core/keyValStorage"
"github.com/multiversx/mx-chain-core-go/data"
"github.com/multiversx/mx-chain-core-go/data/block"
vmcommon "github.com/multiversx/mx-chain-vm-common-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/config"
"github.com/multiversx/mx-chain-go/dataRetriever"
Expand All @@ -33,9 +37,6 @@ import (
"github.com/multiversx/mx-chain-go/testscommon/shardingMocks"
stateMock "github.com/multiversx/mx-chain-go/testscommon/state"
storageStubs "github.com/multiversx/mx-chain-go/testscommon/storage"
vmcommon "github.com/multiversx/mx-chain-vm-common-go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)

const (
Expand Down Expand Up @@ -2719,6 +2720,13 @@ func TestValidatorStatisticsProcessor_SaveNodesCoordinatorUpdatesWithStakingV4(t
}
return mapNodes, nil
},
GetShuffledOutToAuctionValidatorsPublicKeysCalled: func(epoch uint32) (map[uint32][][]byte, error) {
mapNodes := map[uint32][][]byte{
0: {pk1},
core.MetachainShardId: {pk2},
}
return mapNodes, nil
},
}
stakingV4Step2EnableEpochCalledCt := 0
arguments.EnableEpochsHandler = &enableEpochsHandlerMock.EnableEpochsHandlerStub{
Expand Down
1 change: 1 addition & 0 deletions sharding/nodesCoordinator/dtos.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ type ResUpdateNodes struct {
ShuffledOut map[uint32][]Validator
Leaving []Validator
StillRemaining []Validator
LowWaitingList bool
}
2 changes: 2 additions & 0 deletions sharding/nodesCoordinator/hashValidatorShuffler.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/multiversx/mx-chain-core-go/core/atomic"
"github.com/multiversx/mx-chain-core-go/core/check"
"github.com/multiversx/mx-chain-core-go/hashing/sha256"

"github.com/multiversx/mx-chain-go/common"
"github.com/multiversx/mx-chain-go/config"
"github.com/multiversx/mx-chain-go/epochStart"
Expand Down Expand Up @@ -350,6 +351,7 @@ func shuffleNodes(arg shuffleNodesArg) (*ResUpdateNodes, error) {
ShuffledOut: shuffledOutMap,
Leaving: actualLeaving,
StillRemaining: stillRemainingInLeaving,
LowWaitingList: lowWaitingList,
}, nil
}

Expand Down
Loading
Loading