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
117 changes: 115 additions & 2 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 @@ -2383,6 +2384,118 @@ func TestChainSimulator_UnStakeOneActiveNodeAndCheckAPIAuctionList(t *testing.T)
require.Equal(t, 1, numUnQualified)
}

// 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)
numQualified, numUnQualified := getNumQualifiedAndUnqualified(t, metachainNode)
require.Equal(t, 0, numQualified)
require.Equal(t, 0, numUnQualified)

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

numQualified, numUnQualified = getNumQualifiedAndUnqualified(t, metachainNode)
require.Equal(t, 0, numQualified)
require.Equal(t, 0, numUnQualified)

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

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

numQualified, numUnQualified = getNumQualifiedAndUnqualified(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, numQualified)
require.Equal(t, 0, numUnQualified)
mariusmihaic marked this conversation as resolved.
Show resolved Hide resolved
}

func stakeNodes(t *testing.T, cs chainSimulatorIntegrationTests.ChainSimulator, numTxs int) {
mariusmihaic marked this conversation as resolved.
Show resolved Hide resolved
txs := make([]*transaction.Transaction, numTxs)
for i := 0; i < numTxs; i++ {
privateKey, blsKeys, err := chainSimulator.GenerateBlsPrivateKeys(1)
require.Nil(t, err)
err = cs.AddValidatorKeys(privateKey)
require.Nil(t, err)

mintValue := big.NewInt(0).Add(staking.MinimumStakeValue, staking.OneEGLD)
validatorOwner, err := cs.GenerateAndMintWalletAddress(core.AllShardId, mintValue)
require.Nil(t, err)

txDataField := fmt.Sprintf("stake@01@%s@%s", blsKeys[0], staking.MockBLSSignature)
txs[i] = staking.GenerateTransaction(validatorOwner.Bytes, 0, vm.ValidatorSCAddress, staking.MinimumStakeValue, txDataField, staking.GasLimitForStakeOperation)
}
stakeTxs, err := cs.SendTxsAndGenerateBlocksTilAreExecuted(txs, staking.MaxNumOfBlockToGenerateWhenExecutingTx)
require.Nil(t, err)
require.NotNil(t, stakeTxs)
require.Len(t, stakeTxs, numTxs)

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

func stakeOneNode(t *testing.T, cs chainSimulatorIntegrationTests.ChainSimulator) {
privateKey, blsKeys, err := chainSimulator.GenerateBlsPrivateKeys(1)
require.Nil(t, err)
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 @@ -399,11 +400,25 @@ func testBLSKeyIsInAuction(
currentEpoch := metachainNode.GetCoreComponents().EnableEpochsHandler().GetCurrentEpoch()
if metachainNode.GetCoreComponents().EnableEpochsHandler().GetActivationEpoch(common.StakingV4Step2Flag) == currentEpoch {
// starting from phase 2, we have the shuffled out nodes from the previous epoch in the action list
actionListSize += 8
// if there is no lowWaitingList condition
shuffledToAuctionValPubKeys, err := metachainNode.GetProcessComponents().NodesCoordinator().GetShuffledOutToAuctionValidatorsPublicKeys(currentEpoch)
require.Nil(t, err)

if len(shuffledToAuctionValPubKeys) > 0 {
// there are 2 nodes per shard shuffled out so 2 * 4 = 8
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
shuffledToAuctionValPubKeys, err := metachainNode.GetProcessComponents().NodesCoordinator().GetShuffledOutToAuctionValidatorsPublicKeys(currentEpoch)
require.Nil(t, err)

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
31 changes: 28 additions & 3 deletions sharding/nodesCoordinator/indexHashedNodesCoordinator.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ import (
"github.com/multiversx/mx-chain-core-go/data/endProcess"
"github.com/multiversx/mx-chain-core-go/hashing"
"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/epochStart"
"github.com/multiversx/mx-chain-go/state"
"github.com/multiversx/mx-chain-go/storage"
logger "github.com/multiversx/mx-chain-logger-go"
)

var _ NodesCoordinator = (*indexHashedNodesCoordinator)(nil)
Expand Down Expand Up @@ -68,6 +69,7 @@ type epochNodesConfig struct {
newList []Validator
auctionList []Validator
mutNodesMaps sync.RWMutex
lowWaitingList bool
}

type indexHashedNodesCoordinator struct {
Expand Down Expand Up @@ -122,6 +124,7 @@ func NewIndexHashedNodesCoordinator(arguments ArgNodesCoordinator) (*indexHashed
shuffledOutMap: make(map[uint32][]Validator),
newList: make([]Validator, 0),
auctionList: make([]Validator, 0),
lowWaitingList: false,
}

// todo: if not genesis, use previous randomness from start of epoch meta block
Expand Down Expand Up @@ -156,7 +159,7 @@ func NewIndexHashedNodesCoordinator(arguments ArgNodesCoordinator) (*indexHashed
ihnc.loadingFromDisk.Store(false)

ihnc.nodesCoordinatorHelper = ihnc
err = ihnc.setNodesPerShards(arguments.EligibleNodes, arguments.WaitingNodes, nil, nil, arguments.Epoch)
err = ihnc.setNodesPerShards(arguments.EligibleNodes, arguments.WaitingNodes, nil, nil, arguments.Epoch, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -257,6 +260,7 @@ func (ihnc *indexHashedNodesCoordinator) setNodesPerShards(
leaving map[uint32][]Validator,
shuffledOut map[uint32][]Validator,
epoch uint32,
lowWaitingList bool,
) error {
ihnc.mutNodesConfig.Lock()
defer ihnc.mutNodesConfig.Unlock()
Expand Down Expand Up @@ -296,6 +300,7 @@ func (ihnc *indexHashedNodesCoordinator) setNodesPerShards(
nodesConfig.waitingMap = waiting
nodesConfig.leavingMap = leaving
nodesConfig.shuffledOutMap = shuffledOut
nodesConfig.lowWaitingList = lowWaitingList
nodesConfig.shardID, isCurrentNodeValidator = ihnc.computeShardForSelfPublicKey(nodesConfig)
nodesConfig.selectors, err = ihnc.createSelectors(nodesConfig)
if err != nil {
Expand Down Expand Up @@ -546,6 +551,26 @@ func (ihnc *indexHashedNodesCoordinator) GetAllShuffledOutValidatorsPublicKeys(e
return validatorsPubKeys, nil
}

// GetShuffledOutToAuctionValidatorsPublicKeys will return shuffled out to auction validators public keys
func (ihnc *indexHashedNodesCoordinator) GetShuffledOutToAuctionValidatorsPublicKeys(epoch uint32) (map[uint32][][]byte, error) {
validatorsPubKeys := make(map[uint32][][]byte)

ihnc.mutNodesConfig.RLock()
nodesConfig, ok := ihnc.nodesConfig[epoch]
ihnc.mutNodesConfig.RUnlock()

if !ok {
return nil, fmt.Errorf("%w epoch=%v", ErrEpochNodesConfigDoesNotExist, epoch)
}

if nodesConfig.lowWaitingList {
// in case of low waiting list the nodes do not go through auction but directly to waiting
return validatorsPubKeys, nil
}

return ihnc.GetAllShuffledOutValidatorsPublicKeys(epoch)
}

// GetValidatorsIndexes will return validators indexes for a block
func (ihnc *indexHashedNodesCoordinator) GetValidatorsIndexes(
publicKeys []string,
Expand Down Expand Up @@ -662,7 +687,7 @@ func (ihnc *indexHashedNodesCoordinator) EpochStartPrepare(metaHdr data.HeaderHa
resUpdateNodes.Leaving,
)

err = ihnc.setNodesPerShards(resUpdateNodes.Eligible, resUpdateNodes.Waiting, leavingNodesMap, resUpdateNodes.ShuffledOut, newEpoch)
err = ihnc.setNodesPerShards(resUpdateNodes.Eligible, resUpdateNodes.Waiting, leavingNodesMap, resUpdateNodes.ShuffledOut, newEpoch, resUpdateNodes.LowWaitingList)
if err != nil {
log.Error("set nodes per shard failed", "error", err.Error())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func (ihnc *indexHashedNodesCoordinator) SetNodesConfigFromValidatorsInfo(epoch
resUpdateNodes.Leaving,
)

err = ihnc.setNodesPerShards(resUpdateNodes.Eligible, resUpdateNodes.Waiting, leavingNodesMap, resUpdateNodes.ShuffledOut, epoch)
err = ihnc.setNodesPerShards(resUpdateNodes.Eligible, resUpdateNodes.Waiting, leavingNodesMap, resUpdateNodes.ShuffledOut, epoch, resUpdateNodes.LowWaitingList)
if err != nil {
return err
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ func (ihnc *indexHashedNodesCoordinator) nodesCoordinatorToRegistryWithAuction()

func epochNodesConfigToEpochValidatorsWithAuction(config *epochNodesConfig) *EpochValidatorsWithAuction {
result := &EpochValidatorsWithAuction{
Eligible: make(map[string]Validators, len(config.eligibleMap)),
Waiting: make(map[string]Validators, len(config.waitingMap)),
Leaving: make(map[string]Validators, len(config.leavingMap)),
ShuffledOut: make(map[string]Validators, len(config.shuffledOutMap)),
Eligible: make(map[string]Validators, len(config.eligibleMap)),
Waiting: make(map[string]Validators, len(config.waitingMap)),
Leaving: make(map[string]Validators, len(config.leavingMap)),
ShuffledOut: make(map[string]Validators, len(config.shuffledOutMap)),
LowWaitingList: config.lowWaitingList,
}

for k, v := range config.eligibleMap {
Expand Down
Loading
Loading