Skip to content

Commit

Permalink
Merge pull request #5858 from multiversx/enable-stake-delegation-all-…
Browse files Browse the repository at this point in the history
…times

do not activate more nodes on stake if too many nodes
  • Loading branch information
mariusmihaic authored Jan 30, 2024
2 parents da8e4ff + 7cc9bc9 commit fa7b722
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 17 deletions.
4 changes: 2 additions & 2 deletions cmd/node/config/systemSmartContractsConfig.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@
MaxNumberOfNodesForStake = 36
UnJailValue = "2500000000000000000" #0.1% of genesis node price
ActivateBLSPubKeyMessageVerification = false
StakeLimitPercentage = 0.01 #fraction of value 0.01 - 1%
NodeLimitPercentage = 0.005 #fraction of value 0.005 - 0.5%
StakeLimitPercentage = 1.0 #fraction of value 1 - 100%, for the time being no stake limit
NodeLimitPercentage = 0.1 #fraction of value 0.1 - 10%

[ESDTSystemSCConfig]
BaseIssuingCost = "5000000000000000000" #5 eGLD
Expand Down
28 changes: 28 additions & 0 deletions vm/systemSmartContracts/delegation.go
Original file line number Diff line number Diff line change
Expand Up @@ -1215,6 +1215,13 @@ func (d *delegation) stakeNodes(args *vmcommon.ContractCallInput) vmcommon.Retur
return vmOutput.ReturnCode
}

allLogs := d.eei.GetLogs()
tooManyNodesErrMsg := getTooManyNodesErrMsg(allLogs)
if len(tooManyNodesErrMsg) != 0 {
d.eei.AddReturnMessage(tooManyNodesErrMsg)
return vmcommon.UserError
}

err = d.updateDelegationStatusAfterStake(status, vmOutput.ReturnData, args.Arguments)
if err != nil {
d.eei.AddReturnMessage(err.Error())
Expand All @@ -1226,6 +1233,27 @@ func (d *delegation) stakeNodes(args *vmcommon.ContractCallInput) vmcommon.Retur
return vmcommon.Ok
}

func getTooManyNodesErrMsg(logEntries []*vmcommon.LogEntry) string {
for _, logEntry := range logEntries {
topics := logEntry.Topics
if len(topics) != 3 {
continue
}
if bytes.Equal(topics[0], []byte(numberOfNodesTooHigh)) {
return formatTooManyNodesMsg(topics)
}
}

return ""
}

func formatTooManyNodesMsg(topics [][]byte) string {
numRegisteredBlsKeys := big.NewInt(0).SetBytes(topics[1]).Int64()
nodeLimit := big.NewInt(0).SetBytes(topics[2]).Int64()
return fmt.Sprintf("%s, num registered bls keys: %d, node limit: %d",
numberOfNodesTooHigh, numRegisteredBlsKeys, nodeLimit)
}

func (d *delegation) updateDelegationStatusAfterStake(
status *DelegationContractStatus,
returnData [][]byte,
Expand Down
137 changes: 137 additions & 0 deletions vm/systemSmartContracts/delegation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"github.com/multiversx/mx-chain-go/config"
"github.com/multiversx/mx-chain-go/process/smartContract/hooks"
"github.com/multiversx/mx-chain-go/testscommon/enableEpochsHandlerMock"
"github.com/multiversx/mx-chain-go/testscommon/shardingMocks"
stateMock "github.com/multiversx/mx-chain-go/testscommon/state"
"github.com/multiversx/mx-chain-go/vm"
"github.com/multiversx/mx-chain-go/vm/mock"
Expand Down Expand Up @@ -5043,3 +5044,139 @@ func TestDelegationSystemSC_SynchronizeOwner(t *testing.T) {
eei.ResetReturnMessage()
})
}

func TestDelegationSystemSC_ExecuteAddNodesStakeNodesWithNodesLimit(t *testing.T) {
t.Parallel()

sig := []byte("sig1")
args := createMockArgumentsForDelegation()
args.EnableEpochsHandler = enableEpochsHandlerMock.NewEnableEpochsHandlerStub(
common.StakingV4Step1Flag,
common.StakingV4Step2Flag,
common.StakingV4Step3Flag,
common.StakeLimitsFlag,

common.DelegationSmartContractFlag,
common.StakingV2FlagAfterEpoch,
common.AddTokensToDelegationFlag,
common.DeleteDelegatorAfterClaimRewardsFlag,
common.ComputeRewardCheckpointFlag,
common.ValidatorToDelegationFlag,
common.ReDelegateBelowMinCheckFlag,
common.MultiClaimOnDelegationFlag,
)
eei := createDefaultEei()
delegationsMap := map[string][]byte{}
delegationsMap[ownerKey] = []byte("owner")
eei.storageUpdate[string(eei.scAddress)] = delegationsMap
args.Eei = eei

d, _ := NewDelegationSystemSC(args)

blsKey1 := []byte("blsKey1")
blsKey2 := []byte("blsKey2")
key1 := &NodesData{
BLSKey: blsKey1,
}
key2 := &NodesData{
BLSKey: blsKey2,
}
dStatus := &DelegationContractStatus{
StakedKeys: []*NodesData{key1, key2},
}
_ = d.saveDelegationStatus(dStatus)

globalFund := &GlobalFundData{
TotalActive: big.NewInt(400),
}
_ = d.saveGlobalFundData(globalFund)

addValidatorAndStakingScToVmContextWithBlsKeys(eei, [][]byte{blsKey1, blsKey2})

dStatus, _ = d.getDelegationStatus()
require.Equal(t, 2, len(dStatus.StakedKeys))
require.Equal(t, 0, len(dStatus.UnStakedKeys))
require.Equal(t, 0, len(dStatus.NotStakedKeys))

newBlsKey1 := []byte("newBlsKey1")
vmInput := getDefaultVmInputForFunc("addNodes", [][]byte{newBlsKey1, sig})
output := d.Execute(vmInput)
require.Equal(t, vmcommon.Ok, output)

vmInput = getDefaultVmInputForFunc("stakeNodes", [][]byte{newBlsKey1})
output = d.Execute(vmInput)
require.Equal(t, vmcommon.Ok, output)

dStatus, _ = d.getDelegationStatus()
require.Equal(t, 3, len(dStatus.StakedKeys))
require.Equal(t, 0, len(dStatus.UnStakedKeys))
require.Equal(t, 0, len(dStatus.NotStakedKeys))

addValidatorAndStakingScToVmContextWithBlsKeys(eei, [][]byte{blsKey1, blsKey2, newBlsKey1})

newBlsKey2 := []byte("newBlsKey2")
vmInput = getDefaultVmInputForFunc("addNodes", [][]byte{newBlsKey2, sig})
output = d.Execute(vmInput)
require.Equal(t, vmcommon.Ok, output)

vmInput = getDefaultVmInputForFunc("stakeNodes", [][]byte{newBlsKey2})
output = d.Execute(vmInput)
require.Equal(t, vmcommon.UserError, output)
require.True(t, strings.Contains(eei.returnMessage, numberOfNodesTooHigh))
require.True(t, strings.Contains(eei.returnMessage, "num registered bls keys: 4"))
require.True(t, strings.Contains(eei.returnMessage, "node limit: 3"))

dStatus, _ = d.getDelegationStatus()
require.Equal(t, 3, len(dStatus.StakedKeys))
require.Equal(t, 0, len(dStatus.UnStakedKeys))
require.Equal(t, 1, len(dStatus.NotStakedKeys))
}

func addValidatorAndStakingScToVmContextWithBlsKeys(eei *vmContext, blsKeys [][]byte) {
validatorArgs := createMockArgumentsForValidatorSC()
validatorArgs.StakingSCConfig.NodeLimitPercentage = 1
validatorArgs.Eei = eei
validatorArgs.StakingSCConfig.GenesisNodePrice = "100"
validatorArgs.StakingSCAddress = vm.StakingSCAddress
validatorArgs.NodesCoordinator = &shardingMocks.NodesCoordinatorStub{
GetNumTotalEligibleCalled: func() uint64 {
return 3
},
}
validatorSc, _ := NewValidatorSmartContract(validatorArgs)

stakingArgs := createMockStakingScArguments()
stakingArgs.Eei = eei
stakingSc, _ := NewStakingSmartContract(stakingArgs)

_ = eei.SetSystemSCContainer(&mock.SystemSCContainerStub{GetCalled: func(key []byte) (contract vm.SystemSmartContract, err error) {
if bytes.Equal(key, vm.StakingSCAddress) {
return stakingSc, nil
}

if bytes.Equal(key, vm.ValidatorSCAddress) {
_ = validatorSc.saveRegistrationData([]byte("addr"), &ValidatorDataV2{
RewardAddress: []byte("rewardAddr"),
TotalStakeValue: big.NewInt(1000),
LockedStake: big.NewInt(500),
BlsPubKeys: blsKeys,
TotalUnstaked: big.NewInt(150),
UnstakedInfo: []*UnstakedValue{
{
UnstakedEpoch: 10,
UnstakedValue: big.NewInt(60),
},
{
UnstakedEpoch: 50,
UnstakedValue: big.NewInt(80),
},
},
NumRegistered: uint32(len(blsKeys)),
})
validatorSc.unBondPeriod = 50
return validatorSc, nil
}

return nil, nil
}})
}
39 changes: 27 additions & 12 deletions vm/systemSmartContracts/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
const unJailedFunds = "unJailFunds"
const unStakeUnBondPauseKey = "unStakeUnBondPause"
const minPercentage = 0.0001
const numberOfNodesTooHigh = "number of nodes too high, no new nodes activated"

var zero = big.NewInt(0)

Expand Down Expand Up @@ -935,8 +936,12 @@ func (v *validatorSC) isNumberOfNodesTooHigh(registrationData *ValidatorDataV2)
return false
}

return len(registrationData.BlsPubKeys) > v.computeNodeLimit()
}

func (v *validatorSC) computeNodeLimit() int {
nodeLimit := float64(v.nodesCoordinator.GetNumTotalEligible()) * v.nodeLimitPercentage
return len(registrationData.BlsPubKeys) > int(nodeLimit)
return int(nodeLimit)
}

func (v *validatorSC) stake(args *vmcommon.ContractCallInput) vmcommon.ReturnCode {
Expand Down Expand Up @@ -1064,17 +1069,27 @@ func (v *validatorSC) stake(args *vmcommon.ContractCallInput) vmcommon.ReturnCod
}
}

v.activateStakingFor(
blsKeys,
registrationData,
validatorConfig.NodePrice,
registrationData.RewardAddress,
args.CallerAddr,
)

if v.isNumberOfNodesTooHigh(registrationData) {
v.eei.AddReturnMessage("number of nodes is too high")
return vmcommon.UserError
if !v.isNumberOfNodesTooHigh(registrationData) {
v.activateStakingFor(
blsKeys,
registrationData,
validatorConfig.NodePrice,
registrationData.RewardAddress,
args.CallerAddr,
)
} else {
numRegisteredBlsKeys := int64(len(registrationData.BlsPubKeys))
nodeLimit := int64(v.computeNodeLimit())
entry := &vmcommon.LogEntry{
Identifier: []byte(args.Function),
Address: args.RecipientAddr,
Topics: [][]byte{
[]byte(numberOfNodesTooHigh),
big.NewInt(numRegisteredBlsKeys).Bytes(),
big.NewInt(nodeLimit).Bytes(),
},
}
v.eei.AddLogEntry(entry)
}

err = v.saveRegistrationData(args.CallerAddr, registrationData)
Expand Down
9 changes: 6 additions & 3 deletions vm/systemSmartContracts/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,8 +460,10 @@ func TestStakingValidatorSC_ExecuteStakeTooManyNodes(t *testing.T) {
}
return nil
}
eei.AddReturnMessageCalled = func(msg string) {
assert.Equal(t, msg, "number of nodes is too high")
called := false
eei.AddLogEntryCalled = func(entry *vmcommon.LogEntry) {
called = true
assert.Equal(t, entry.Topics[0], []byte(numberOfNodesTooHigh))
}

key1 := []byte("Key1")
Expand All @@ -472,7 +474,8 @@ func TestStakingValidatorSC_ExecuteStakeTooManyNodes(t *testing.T) {
arguments.Arguments = [][]byte{big.NewInt(3).Bytes(), key1, []byte("msg1"), key2, []byte("msg2"), key3, []byte("msg3")}

errCode := stakingValidatorSc.Execute(arguments)
assert.Equal(t, vmcommon.UserError, errCode)
assert.Equal(t, vmcommon.Ok, errCode)
assert.True(t, called)
}

func TestStakingValidatorSC_ExecuteStakeAddedNewPubKeysShouldWork(t *testing.T) {
Expand Down

0 comments on commit fa7b722

Please sign in to comment.