From 31619d9097db5d8cba391ff341c808c8c8cfae12 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Mon, 25 Oct 2021 11:28:18 +0300 Subject: [PATCH 1/9] - added a new functionality that will fix the length on the waiting list length. --- .../config/gasSchedules/gasScheduleV1.toml | 1 + .../config/gasSchedules/gasScheduleV2.toml | 11 ++- .../config/gasSchedules/gasScheduleV3.toml | 1 + .../config/gasSchedules/gasScheduleV4.toml | 1 + vm/gasCost.go | 1 + vm/systemSmartContracts/staking.go | 79 ++++++++++++++++++- 6 files changed, 89 insertions(+), 5 deletions(-) diff --git a/cmd/node/config/gasSchedules/gasScheduleV1.toml b/cmd/node/config/gasSchedules/gasScheduleV1.toml index e96b9bb4e59..4320fa2957c 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV1.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV1.toml @@ -38,6 +38,7 @@ DelegationMgrOps = 50000000 ValidatorToDelegation = 500000000 GetAllNodeStates = 100000000 + FixWaitingListSize = 500000000 [BaseOperationCost] StorePerByte = 50000 diff --git a/cmd/node/config/gasSchedules/gasScheduleV2.toml b/cmd/node/config/gasSchedules/gasScheduleV2.toml index e91683a1337..a37cc8b8fc0 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV2.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV2.toml @@ -25,9 +25,6 @@ ChangeRewardAddress = 5000000 ChangeValidatorKeys = 5000000 UnJail = 5000000 - DelegationOps = 1000000 - DelegationMgrOps = 50000000 - ValidatorToDelegation = 500000000 ESDTIssue = 50000000 ESDTOperations = 50000000 Proposal = 5000000 @@ -35,7 +32,13 @@ DelegateVote = 1000000 RevokeVote = 500000 CloseProposal = 1000000 - GetAllNodeStates = 20000000 + DelegationOps = 1000000 + UnstakeTokens = 5000000 + UnbondTokens = 5000000 + DelegationMgrOps = 50000000 + ValidatorToDelegation = 500000000 + GetAllNodeStates = 100000000 + FixWaitingListSize = 500000000 [BaseOperationCost] StorePerByte = 50000 diff --git a/cmd/node/config/gasSchedules/gasScheduleV3.toml b/cmd/node/config/gasSchedules/gasScheduleV3.toml index cef035d3bdf..c1b59ff5cc2 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV3.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV3.toml @@ -38,6 +38,7 @@ GetAllNodeStates = 20000000 UnstakeTokens = 5000000 UnbondTokens = 5000000 + FixWaitingListSize = 500000000 [BaseOperationCost] StorePerByte = 50000 diff --git a/cmd/node/config/gasSchedules/gasScheduleV4.toml b/cmd/node/config/gasSchedules/gasScheduleV4.toml index bb55339e20b..20ea1758ccd 100644 --- a/cmd/node/config/gasSchedules/gasScheduleV4.toml +++ b/cmd/node/config/gasSchedules/gasScheduleV4.toml @@ -38,6 +38,7 @@ GetAllNodeStates = 20000000 UnstakeTokens = 5000000 UnbondTokens = 5000000 + FixWaitingListSize = 500000000 [BaseOperationCost] StorePerByte = 10000 diff --git a/vm/gasCost.go b/vm/gasCost.go index 6da0c558de1..57762655960 100644 --- a/vm/gasCost.go +++ b/vm/gasCost.go @@ -34,6 +34,7 @@ type MetaChainSystemSCsCost struct { DelegationMgrOps uint64 ValidatorToDelegation uint64 GetAllNodeStates uint64 + FixWaitingListSize uint64 } // BuiltInCost defines cost for built-in methods diff --git a/vm/systemSmartContracts/staking.go b/vm/systemSmartContracts/staking.go index 1cc09c3fc2f..84c85194bcc 100644 --- a/vm/systemSmartContracts/staking.go +++ b/vm/systemSmartContracts/staking.go @@ -31,7 +31,7 @@ const waitingElementPrefix = "w_" type stakingSC struct { eei vm.SystemEI unBondPeriod uint64 - stakeAccessAddr []byte //TODO add a viewAddress field and use it on all system SC view functions + stakeAccessAddr []byte // TODO add a viewAddress field and use it on all system SC view functions jailAccessAddr []byte endOfEpochAccessAddr []byte numRoundsWithoutBleed uint64 @@ -225,6 +225,8 @@ func (s *stakingSC) Execute(args *vmcommon.ContractCallInput) vmcommon.ReturnCod return s.cleanAdditionalQueue(args) case "changeOwnerAndRewardAddress": return s.changeOwnerAndRewardAddress(args) + case "fixWaitingListQueueSize": + return s.fixWaitingListQueueSize(args) } return vmcommon.UserError @@ -1898,15 +1900,90 @@ func (s *stakingSC) getFirstElementsFromWaitingList(numNodes uint32) (*waitingLi blsKeysToStake = append(blsKeysToStake, element.BLSPublicKey) stakedDataList = append(stakedDataList, stakedData) index++ + if len(element.NextKey) == 0 { + break + } copy(nextKey, element.NextKey) } + if len(blsKeysToStake) != int(waitingListHead.Length) { + log.Warn("mismatch length on waiting list elements in stakingSC.getFirstElementsFromWaitingList") + } + waitingListData.blsKeys = blsKeysToStake waitingListData.stakedDataList = stakedDataList waitingListData.lastKey = nextKey return waitingListData, nil } +func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vmcommon.ReturnCode { + if !s.flagCorrectFirstQueued.IsSet() { + s.eei.AddReturnMessage("invalid method to call") + return vmcommon.UserError + } + + if args.CallValue.Cmp(zero) != 0 { + s.eei.AddReturnMessage(vm.TransactionValueMustBeZero) + return vmcommon.UserError + } + + err := s.eei.UseGas(s.gasCost.MetaChainSystemSCsCost.FixWaitingListSize) + if err != nil { + s.eei.AddReturnMessage("insufficient gas") + return vmcommon.OutOfGas + } + + waitingListHead, err := s.getWaitingListHead() + if err != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + + if waitingListHead.Length == 0 { + waitingListHead.Length = 0 + + return s.saveWaitingListReturningErrorCode(waitingListHead) + } + + index := uint32(1) + nextKey := make([]byte, len(waitingListHead.FirstKey)) + copy(nextKey, waitingListHead.FirstKey) + for len(nextKey) != 0 && index <= waitingListHead.Length { + element, errGet := s.getWaitingListElement(nextKey) + if errGet != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + + _, errGet = s.getOrCreateRegisteredData(element.BLSPublicKey) + if errGet != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + + index++ + if len(element.NextKey) == 0 { + break + } + copy(nextKey, element.NextKey) + } + + waitingListHead.Length = index + waitingListHead.LastKey = nextKey + + return s.saveWaitingListReturningErrorCode(waitingListHead) +} + +func (s *stakingSC) saveWaitingListReturningErrorCode(waitingList *WaitingList) vmcommon.ReturnCode { + err := s.saveWaitingListHead(waitingList) + if err != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + + return vmcommon.Ok +} + // EpochConfirmed is called whenever a new epoch is confirmed func (s *stakingSC) EpochConfirmed(epoch uint32, _ uint64) { s.flagEnableStaking.Toggle(epoch >= s.enableStakingEpoch) From 7fc0cf372e3db6215b8d1e1e2118a27f9b5b1d11 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Mon, 25 Oct 2021 12:18:48 +0300 Subject: [PATCH 2/9] - fixes - added unit test --- vm/systemSmartContracts/staking.go | 9 ++-- vm/systemSmartContracts/staking_test.go | 65 +++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/vm/systemSmartContracts/staking.go b/vm/systemSmartContracts/staking.go index 84c85194bcc..dbc49c90235 100644 --- a/vm/systemSmartContracts/staking.go +++ b/vm/systemSmartContracts/staking.go @@ -1063,6 +1063,9 @@ func (s *stakingSC) searchPreviousFromHead(waitingList *WaitingList, inWaitingLi } index++ nextKey = make([]byte, len(element.NextKey)) + if len(element.NextKey) == 0 { + break + } copy(nextKey, element.NextKey) } return nil, vm.ErrElementNotFound @@ -1939,10 +1942,8 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm return vmcommon.UserError } - if waitingListHead.Length == 0 { - waitingListHead.Length = 0 - - return s.saveWaitingListReturningErrorCode(waitingListHead) + if waitingListHead.Length <= 1 { + return vmcommon.Ok } index := uint32(1) diff --git a/vm/systemSmartContracts/staking_test.go b/vm/systemSmartContracts/staking_test.go index 2ae0a8a4cf2..c90b0335468 100644 --- a/vm/systemSmartContracts/staking_test.go +++ b/vm/systemSmartContracts/staking_test.go @@ -2546,6 +2546,71 @@ func TestStakingSc_InsertAfterLastJailedAfterFixWithEmptyQueue(t *testing.T) { assert.Equal(t, 0, len(firstElement.NextKey)) } +func TestStakingSc_getWaitingListRegisterNonceAndRewardAddressWhenLentghIsHigherThanOne(t *testing.T) { + t.Parallel() + + blockChainHook := &mock.BlockChainHookStub{} + marshalizer := &marshal.JsonMarshalizer{} + eei, _ := NewVMContext(blockChainHook, hooks.NewVMCryptoHook(), &mock.ArgumentParserMock{}, &stateMock.AccountsStub{}, &mock.RaterMock{}) + m := make(map[string]interface{}) + waitingListHead := &WaitingList{nil, nil, 0, nil} + m[waitingListHeadKey] = waitingListHead + + blockChainHook.GetStorageDataCalled = func(accountsAddress []byte, index []byte) (i []byte, e error) { + obj, found := m[string(index)] + if found { + return marshalizer.Marshal(obj) + } + + return nil, nil + } + + args := createMockStakingScArguments() + args.Marshalizer = marshalizer + args.Eei = eei + stakingAccessAddress := []byte("stakingAccessAddress") + args.StakingAccessAddr = stakingAccessAddress + args.StakingSCConfig.MaxNumberOfNodesForStake = 2 + sc, _ := NewStakingSmartContract(args) + sc.flagCorrectFirstQueued.Set() + stakerAddress := []byte("stakerAddr") + + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), + } + doStake(t, sc, stakingAccessAddress, stakerAddress, []byte("eligibleBlsKey1")) + doStake(t, sc, stakingAccessAddress, stakerAddress, []byte("eligibleBlsKey2")) + for _, waitingKey := range waitingBlsKeys { + doStake(t, sc, stakingAccessAddress, stakerAddress, waitingKey) + } + + // manually alter the length + buff := eei.GetStorage([]byte(waitingListHeadKey)) + existingWaitingListHead := &WaitingList{} + err := marshalizer.Unmarshal(existingWaitingListHead, buff) + require.Nil(t, err) + existingWaitingListHead.Length++ + buff, err = marshalizer.Marshal(existingWaitingListHead) + require.Nil(t, err) + eei.SetStorage([]byte(waitingListHeadKey), buff) + + eei.output = make([][]byte, 0) + + arguments := CreateVmContractCallInput() + arguments.Function = "getQueueRegisterNonceAndRewardAddress" + arguments.CallerAddr = stakingAccessAddress + arguments.Arguments = make([][]byte, 0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + assert.Equal(t, 3*len(waitingBlsKeys), len(eei.output)) + for i, waitingKey := range waitingBlsKeys { + assert.Equal(t, waitingKey, eei.output[i*3]) + } +} + func doUnStakeAtEndOfEpoch(t *testing.T, sc *stakingSC, blsKey []byte, expectedReturnCode vmcommon.ReturnCode) { arguments := CreateVmContractCallInput() arguments.CallerAddr = sc.endOfEpochAccessAddr From 8de34141447fc61a6de2e77f861522211bb4269c Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Mon, 25 Oct 2021 12:21:15 +0300 Subject: [PATCH 3/9] - more fixes --- vm/systemSmartContracts/staking.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/vm/systemSmartContracts/staking.go b/vm/systemSmartContracts/staking.go index dbc49c90235..a9b3218e639 100644 --- a/vm/systemSmartContracts/staking.go +++ b/vm/systemSmartContracts/staking.go @@ -1909,7 +1909,7 @@ func (s *stakingSC) getFirstElementsFromWaitingList(numNodes uint32) (*waitingLi copy(nextKey, element.NextKey) } - if len(blsKeysToStake) != int(waitingListHead.Length) { + if numNodes >= waitingListHead.Length && len(blsKeysToStake) != int(waitingListHead.Length) { log.Warn("mismatch length on waiting list elements in stakingSC.getFirstElementsFromWaitingList") } @@ -1972,11 +1972,7 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm waitingListHead.Length = index waitingListHead.LastKey = nextKey - return s.saveWaitingListReturningErrorCode(waitingListHead) -} - -func (s *stakingSC) saveWaitingListReturningErrorCode(waitingList *WaitingList) vmcommon.ReturnCode { - err := s.saveWaitingListHead(waitingList) + err = s.saveWaitingListHead(waitingListHead) if err != nil { s.eei.AddReturnMessage(err.Error()) return vmcommon.UserError From 011e5eb803eea862ab21164cf4082a2eda14500b Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Mon, 25 Oct 2021 13:06:16 +0300 Subject: [PATCH 4/9] - more fixes - added more unit tests --- vm/systemSmartContracts/staking.go | 11 +- vm/systemSmartContracts/staking_test.go | 220 +++++++++++++++++++++--- 2 files changed, 208 insertions(+), 23 deletions(-) diff --git a/vm/systemSmartContracts/staking.go b/vm/systemSmartContracts/staking.go index a9b3218e639..950cbb415b0 100644 --- a/vm/systemSmartContracts/staking.go +++ b/vm/systemSmartContracts/staking.go @@ -1061,11 +1061,12 @@ func (s *stakingSC) searchPreviousFromHead(waitingList *WaitingList, inWaitingLi elementToRemove.PreviousKey = createWaitingListKey(previousElement.BLSPublicKey) return previousElement, nil } - index++ + nextKey = make([]byte, len(element.NextKey)) if len(element.NextKey) == 0 { break } + index++ copy(nextKey, element.NextKey) } return nil, vm.ErrElementNotFound @@ -1305,6 +1306,9 @@ func (s *stakingSC) getWaitingListIndex(args *vmcommon.ContractCallInput) vmcomm return vmcommon.UserError } + if len(prevElement.NextKey) == 0 { + break + } index++ copy(nextKey, prevElement.NextKey) } @@ -1902,10 +1906,11 @@ func (s *stakingSC) getFirstElementsFromWaitingList(numNodes uint32) (*waitingLi blsKeysToStake = append(blsKeysToStake, element.BLSPublicKey) stakedDataList = append(stakedDataList, stakedData) - index++ + if len(element.NextKey) == 0 { break } + index++ copy(nextKey, element.NextKey) } @@ -1962,10 +1967,10 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm return vmcommon.UserError } - index++ if len(element.NextKey) == 0 { break } + index++ copy(nextKey, element.NextKey) } diff --git a/vm/systemSmartContracts/staking_test.go b/vm/systemSmartContracts/staking_test.go index c90b0335468..50414321197 100644 --- a/vm/systemSmartContracts/staking_test.go +++ b/vm/systemSmartContracts/staking_test.go @@ -2546,9 +2546,200 @@ func TestStakingSc_InsertAfterLastJailedAfterFixWithEmptyQueue(t *testing.T) { assert.Equal(t, 0, len(firstElement.NextKey)) } -func TestStakingSc_getWaitingListRegisterNonceAndRewardAddressWhenLentghIsHigherThanOne(t *testing.T) { +func TestStakingSc_getWaitingListRegisterNonceAndRewardAddressWhenLengthIsHigherThanOne(t *testing.T) { t.Parallel() + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), + } + sc, eei, marshalizer, stakingAccessAddress := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + alterWaitingListLength(t, eei, marshalizer) + + arguments := CreateVmContractCallInput() + arguments.Function = "getQueueRegisterNonceAndRewardAddress" + arguments.CallerAddr = stakingAccessAddress + arguments.Arguments = make([][]byte, 0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + assert.Equal(t, 3*len(waitingBlsKeys), len(eei.output)) + for i, waitingKey := range waitingBlsKeys { + assert.Equal(t, waitingKey, eei.output[i*3]) + } +} + +func TestStakingSc_fixWaitingListQueueSize(t *testing.T) { + t.Parallel() + + t.Run("inactive fix should error", func(t *testing.T) { + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), + } + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + alterWaitingListLength(t, eei, marshalizer) + sc.flagCorrectFirstQueued.Unset() + eei.SetGasProvided(500000000) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.UserError, retCode) + assert.Equal(t, "invalid method to call", eei.returnMessage) + }) + t.Run("provided value should error", func(t *testing.T) { + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), + } + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + alterWaitingListLength(t, eei, marshalizer) + eei.SetGasProvided(500000000) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(1) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.UserError, retCode) + assert.Equal(t, vm.TransactionValueMustBeZero, eei.returnMessage) + }) + t.Run("not enough gas should error", func(t *testing.T) { + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), + } + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + alterWaitingListLength(t, eei, marshalizer) + eei.SetGasProvided(499999999) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.OutOfGas, retCode) + assert.Equal(t, "insufficient gas", eei.returnMessage) + }) + t.Run("should repair", func(t *testing.T) { + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), + } + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + alterWaitingListLength(t, eei, marshalizer) + eei.SetGasProvided(500000000) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + + buff := eei.GetStorage([]byte(waitingListHeadKey)) + waitingListHead := &WaitingList{} + err := marshalizer.Unmarshal(waitingListHead, buff) + require.Nil(t, err) + + assert.Equal(t, len(waitingBlsKeys), int(waitingListHead.Length)) + assert.Equal(t, waitingBlsKeys[len(waitingBlsKeys)-1], waitingListHead.LastKey[2:]) + assert.Equal(t, waitingBlsKeys[0], waitingListHead.FirstKey[2:]) + }) + t.Run("should not alter if repair is not needed", func(t *testing.T) { + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), + } + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + eei.SetGasProvided(500000000) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + + buff := eei.GetStorage([]byte(waitingListHeadKey)) + waitingListHead := &WaitingList{} + err := marshalizer.Unmarshal(waitingListHead, buff) + require.Nil(t, err) + + assert.Equal(t, len(waitingBlsKeys), int(waitingListHead.Length)) + assert.Equal(t, waitingBlsKeys[len(waitingBlsKeys)-1], waitingListHead.LastKey[2:]) + assert.Equal(t, waitingBlsKeys[0], waitingListHead.FirstKey[2:]) + }) + t.Run("should not alter if the waiting list size is 1", func(t *testing.T) { + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + } + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + eei.SetGasProvided(500000000) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + + buff := eei.GetStorage([]byte(waitingListHeadKey)) + waitingListHead := &WaitingList{} + err := marshalizer.Unmarshal(waitingListHead, buff) + require.Nil(t, err) + + assert.Equal(t, len(waitingBlsKeys), int(waitingListHead.Length)) + assert.Equal(t, waitingBlsKeys[len(waitingBlsKeys)-1], waitingListHead.LastKey[2:]) + assert.Equal(t, waitingBlsKeys[0], waitingListHead.FirstKey[2:]) + }) + t.Run("should not alter if the waiting list size is 0", func(t *testing.T) { + waitingBlsKeys := make([][]byte, 0) + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + eei.SetGasProvided(500000000) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + + buff := eei.GetStorage([]byte(waitingListHeadKey)) + waitingListHead := &WaitingList{} + err := marshalizer.Unmarshal(waitingListHead, buff) + require.Nil(t, err) + + assert.Equal(t, len(waitingBlsKeys), int(waitingListHead.Length)) + assert.Nil(t, waitingListHead.LastKey) + assert.Nil(t, waitingListHead.FirstKey) + }) +} + +func makeWrongConfigForWaitingBlsKeysList(t *testing.T, waitingBlsKeys [][]byte) (*stakingSC, *vmContext, marshal.Marshalizer, []byte) { blockChainHook := &mock.BlockChainHookStub{} marshalizer := &marshal.JsonMarshalizer{} eei, _ := NewVMContext(blockChainHook, hooks.NewVMCryptoHook(), &mock.ArgumentParserMock{}, &stateMock.AccountsStub{}, &mock.RaterMock{}) @@ -2571,21 +2762,24 @@ func TestStakingSc_getWaitingListRegisterNonceAndRewardAddressWhenLentghIsHigher stakingAccessAddress := []byte("stakingAccessAddress") args.StakingAccessAddr = stakingAccessAddress args.StakingSCConfig.MaxNumberOfNodesForStake = 2 + args.GasCost.MetaChainSystemSCsCost.FixWaitingListSize = 500000000 sc, _ := NewStakingSmartContract(args) sc.flagCorrectFirstQueued.Set() stakerAddress := []byte("stakerAddr") - waitingBlsKeys := [][]byte{ - []byte("waitingBlsKey1"), - []byte("waitingBlsKey2"), - []byte("waitingBlsKey3"), - } doStake(t, sc, stakingAccessAddress, stakerAddress, []byte("eligibleBlsKey1")) doStake(t, sc, stakingAccessAddress, stakerAddress, []byte("eligibleBlsKey2")) for _, waitingKey := range waitingBlsKeys { doStake(t, sc, stakingAccessAddress, stakerAddress, waitingKey) } + eei.output = make([][]byte, 0) + eei.returnMessage = "" + + return sc, eei, marshalizer, stakingAccessAddress +} + +func alterWaitingListLength(t *testing.T, eei *vmContext, marshalizer marshal.Marshalizer) { // manually alter the length buff := eei.GetStorage([]byte(waitingListHeadKey)) existingWaitingListHead := &WaitingList{} @@ -2595,20 +2789,6 @@ func TestStakingSc_getWaitingListRegisterNonceAndRewardAddressWhenLentghIsHigher buff, err = marshalizer.Marshal(existingWaitingListHead) require.Nil(t, err) eei.SetStorage([]byte(waitingListHeadKey), buff) - - eei.output = make([][]byte, 0) - - arguments := CreateVmContractCallInput() - arguments.Function = "getQueueRegisterNonceAndRewardAddress" - arguments.CallerAddr = stakingAccessAddress - arguments.Arguments = make([][]byte, 0) - - retCode := sc.Execute(arguments) - assert.Equal(t, vmcommon.Ok, retCode) - assert.Equal(t, 3*len(waitingBlsKeys), len(eei.output)) - for i, waitingKey := range waitingBlsKeys { - assert.Equal(t, waitingKey, eei.output[i*3]) - } } func doUnStakeAtEndOfEpoch(t *testing.T, sc *stakingSC, blsKey []byte, expectedReturnCode vmcommon.ReturnCode) { From a47d44cdef025469ecfa9433b5a1080e8c069f12 Mon Sep 17 00:00:00 2001 From: Iulian Pascalau Date: Mon, 25 Oct 2021 13:45:22 +0300 Subject: [PATCH 5/9] - fixed unit tests --- factory/processComponents_test.go | 1 + process/factory/metachain/vmContainerFactory_test.go | 1 + vm/systemSmartContracts/defaults/gasMap.go | 1 + 3 files changed, 3 insertions(+) diff --git a/factory/processComponents_test.go b/factory/processComponents_test.go index fbdc9bcdb28..76cc9eaedc2 100644 --- a/factory/processComponents_test.go +++ b/factory/processComponents_test.go @@ -229,6 +229,7 @@ func FillGasMapMetaChainSystemSCsCosts(value uint64) map[string]uint64 { gasMap["DelegationMgrOps"] = value gasMap["GetAllNodeStates"] = value gasMap["ValidatorToDelegation"] = value + gasMap["FixWaitingListSize"] = value return gasMap } diff --git a/process/factory/metachain/vmContainerFactory_test.go b/process/factory/metachain/vmContainerFactory_test.go index e2afe8ab117..f6523f35f28 100644 --- a/process/factory/metachain/vmContainerFactory_test.go +++ b/process/factory/metachain/vmContainerFactory_test.go @@ -425,6 +425,7 @@ func FillGasMapMetaChainSystemSCsCosts(value uint64) map[string]uint64 { gasMap["DelegationMgrOps"] = value gasMap["GetAllNodeStates"] = value gasMap["ValidatorToDelegation"] = value + gasMap["FixWaitingListSize"] = value return gasMap } diff --git a/vm/systemSmartContracts/defaults/gasMap.go b/vm/systemSmartContracts/defaults/gasMap.go index a4cc96460c8..2e1df6f11e2 100644 --- a/vm/systemSmartContracts/defaults/gasMap.go +++ b/vm/systemSmartContracts/defaults/gasMap.go @@ -73,6 +73,7 @@ func FillGasMapMetaChainSystemSCsCosts(value uint64) map[string]uint64 { gasMap["DelegationMgrOps"] = value gasMap["GetAllNodeStates"] = value gasMap["ValidatorToDelegation"] = value + gasMap["FixWaitingListSize"] = value return gasMap } From 57ef41df84684902180c4d980ba7f970420e1174 Mon Sep 17 00:00:00 2001 From: raduchis Date: Mon, 25 Oct 2021 21:25:22 +0300 Subject: [PATCH 6/9] fixed lastJailedKey --- vm/systemSmartContracts/staking.go | 9 ++++ vm/systemSmartContracts/staking_test.go | 72 ++++++++++++++++++++++++- 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/vm/systemSmartContracts/staking.go b/vm/systemSmartContracts/staking.go index 950cbb415b0..63ee4e17d30 100644 --- a/vm/systemSmartContracts/staking.go +++ b/vm/systemSmartContracts/staking.go @@ -1951,6 +1951,8 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm return vmcommon.Ok } + foundLastJailedKey := len(waitingListHead.LastJailedKey) == 0 + index := uint32(1) nextKey := make([]byte, len(waitingListHead.FirstKey)) copy(nextKey, waitingListHead.FirstKey) @@ -1961,6 +1963,10 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm return vmcommon.UserError } + if bytes.Equal(waitingListHead.LastJailedKey, nextKey) { + foundLastJailedKey = true + } + _, errGet = s.getOrCreateRegisteredData(element.BLSPublicKey) if errGet != nil { s.eei.AddReturnMessage(err.Error()) @@ -1976,6 +1982,9 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm waitingListHead.Length = index waitingListHead.LastKey = nextKey + if !foundLastJailedKey { + waitingListHead.LastJailedKey = make([]byte, 0) + } err = s.saveWaitingListHead(waitingListHead) if err != nil { diff --git a/vm/systemSmartContracts/staking_test.go b/vm/systemSmartContracts/staking_test.go index 50414321197..4e2f8d34655 100644 --- a/vm/systemSmartContracts/staking_test.go +++ b/vm/systemSmartContracts/staking_test.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/hex" "encoding/json" + "fmt" "math" "math/big" "strconv" @@ -2737,14 +2738,83 @@ func TestStakingSc_fixWaitingListQueueSize(t *testing.T) { assert.Nil(t, waitingListHead.LastKey) assert.Nil(t, waitingListHead.FirstKey) }) + t.Run("should not alter lastJailedKey if exists", func(t *testing.T) { + lastJailedBLSString := "lastJailedKey1" + waitingBlsKeys := [][]byte{ + []byte(lastJailedBLSString), + []byte("waitingBlsKey2"), + } + lastJailedKey := []byte(fmt.Sprintf("w_%s", lastJailedBLSString)) + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysListWithLastJailed(t, waitingBlsKeys, lastJailedKey) + eei.SetGasProvided(500000000) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(0) + + beforeBuff := eei.GetStorage([]byte(waitingListHeadKey)) + beforeWaitingListHead := &WaitingList{} + beforeErr := marshalizer.Unmarshal(beforeWaitingListHead, beforeBuff) + require.Nil(t, beforeErr) + assert.Equal(t, lastJailedKey, beforeWaitingListHead.LastJailedKey) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + + buff := eei.GetStorage([]byte(waitingListHeadKey)) + waitingListHead := &WaitingList{} + err := marshalizer.Unmarshal(waitingListHead, buff) + require.Nil(t, err) + + assert.Equal(t, len(waitingBlsKeys), int(waitingListHead.Length)) + assert.Equal(t, lastJailedKey, waitingListHead.LastJailedKey) + }) + t.Run("should alter lastJailedKey if NOT exists", func(t *testing.T) { + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + } + lastJailedKey := []byte("lastJailedKey") + sc, eei, marshalizer, _ := makeWrongConfigForWaitingBlsKeysListWithLastJailed(t, waitingBlsKeys, lastJailedKey) + eei.SetGasProvided(500000000) + + arguments := CreateVmContractCallInput() + arguments.Function = "fixWaitingListQueueSize" + arguments.CallerAddr = []byte("caller") + arguments.Arguments = make([][]byte, 0) + arguments.CallValue = big.NewInt(0) + + beforeBuff := eei.GetStorage([]byte(waitingListHeadKey)) + beforeWaitingListHead := &WaitingList{} + beforeErr := marshalizer.Unmarshal(beforeWaitingListHead, beforeBuff) + require.Nil(t, beforeErr) + assert.Equal(t, lastJailedKey, beforeWaitingListHead.LastJailedKey) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + + buff := eei.GetStorage([]byte(waitingListHeadKey)) + waitingListHead := &WaitingList{} + err := marshalizer.Unmarshal(waitingListHead, buff) + require.Nil(t, err) + + assert.Equal(t, len(waitingBlsKeys), int(waitingListHead.Length)) + assert.Equal(t, 0, len(waitingListHead.LastJailedKey)) + }) } func makeWrongConfigForWaitingBlsKeysList(t *testing.T, waitingBlsKeys [][]byte) (*stakingSC, *vmContext, marshal.Marshalizer, []byte) { + return makeWrongConfigForWaitingBlsKeysListWithLastJailed(t, waitingBlsKeys, nil) +} + +func makeWrongConfigForWaitingBlsKeysListWithLastJailed(t *testing.T, waitingBlsKeys [][]byte, lastJailedKey []byte) (*stakingSC, *vmContext, marshal.Marshalizer, []byte) { blockChainHook := &mock.BlockChainHookStub{} marshalizer := &marshal.JsonMarshalizer{} eei, _ := NewVMContext(blockChainHook, hooks.NewVMCryptoHook(), &mock.ArgumentParserMock{}, &stateMock.AccountsStub{}, &mock.RaterMock{}) m := make(map[string]interface{}) - waitingListHead := &WaitingList{nil, nil, 0, nil} + waitingListHead := &WaitingList{nil, nil, 0, lastJailedKey} m[waitingListHeadKey] = waitingListHead blockChainHook.GetStorageDataCalled = func(accountsAddress []byte, index []byte) (i []byte, e error) { From cbfd5d347c07a86f9256852d6fab625624b71b9f Mon Sep 17 00:00:00 2001 From: Robert Sasu Date: Tue, 26 Oct 2021 11:47:04 +0300 Subject: [PATCH 7/9] added new function to correct values --- vm/systemSmartContracts/staking.go | 102 +++++++++++++++++++++++++---- 1 file changed, 88 insertions(+), 14 deletions(-) diff --git a/vm/systemSmartContracts/staking.go b/vm/systemSmartContracts/staking.go index 63ee4e17d30..699d0b450ce 100644 --- a/vm/systemSmartContracts/staking.go +++ b/vm/systemSmartContracts/staking.go @@ -227,6 +227,8 @@ func (s *stakingSC) Execute(args *vmcommon.ContractCallInput) vmcommon.ReturnCod return s.changeOwnerAndRewardAddress(args) case "fixWaitingListQueueSize": return s.fixWaitingListQueueSize(args) + case "addMissingNodeToQueue": + return s.addMissingNodeToQueue(args) } return vmcommon.UserError @@ -832,18 +834,7 @@ func (s *stakingSC) addToWaitingList(blsKey []byte, addJailed bool) error { waitingList.Length += 1 if waitingList.Length == 1 { - waitingList.FirstKey = inWaitingListKey - waitingList.LastKey = inWaitingListKey - if addJailed { - waitingList.LastJailedKey = inWaitingListKey - } - - elementInWaiting := &ElementInList{ - BLSPublicKey: blsKey, - PreviousKey: waitingList.LastKey, - NextKey: make([]byte, 0), - } - return s.saveElementAndList(inWaitingListKey, elementInWaiting, waitingList) + return s.startWaitingList(waitingList, addJailed, blsKey) } if addJailed { @@ -853,6 +844,26 @@ func (s *stakingSC) addToWaitingList(blsKey []byte, addJailed bool) error { return s.addToEndOfTheList(waitingList, blsKey) } +func (s *stakingSC) startWaitingList( + waitingList *WaitingList, + addJailed bool, + blsKey []byte, +) error { + inWaitingListKey := createWaitingListKey(blsKey) + waitingList.FirstKey = inWaitingListKey + waitingList.LastKey = inWaitingListKey + if addJailed { + waitingList.LastJailedKey = inWaitingListKey + } + + elementInWaiting := &ElementInList{ + BLSPublicKey: blsKey, + PreviousKey: waitingList.LastKey, + NextKey: make([]byte, 0), + } + return s.saveElementAndList(inWaitingListKey, elementInWaiting, waitingList) +} + func (s *stakingSC) addToEndOfTheList(waitingList *WaitingList, blsKey []byte) error { inWaitingListKey := createWaitingListKey(blsKey) oldLastKey := make([]byte, len(waitingList.LastKey)) @@ -1959,7 +1970,7 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm for len(nextKey) != 0 && index <= waitingListHead.Length { element, errGet := s.getWaitingListElement(nextKey) if errGet != nil { - s.eei.AddReturnMessage(err.Error()) + s.eei.AddReturnMessage(errGet.Error()) return vmcommon.UserError } @@ -1969,7 +1980,7 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm _, errGet = s.getOrCreateRegisteredData(element.BLSPublicKey) if errGet != nil { - s.eei.AddReturnMessage(err.Error()) + s.eei.AddReturnMessage(errGet.Error()) return vmcommon.UserError } @@ -1995,6 +2006,69 @@ func (s *stakingSC) fixWaitingListQueueSize(args *vmcommon.ContractCallInput) vm return vmcommon.Ok } +func (s *stakingSC) addMissingNodeToQueue(args *vmcommon.ContractCallInput) vmcommon.ReturnCode { + if !s.flagCorrectFirstQueued.IsSet() { + s.eei.AddReturnMessage("invalid method to call") + return vmcommon.UserError + } + if args.CallValue.Cmp(zero) != 0 { + s.eei.AddReturnMessage(vm.TransactionValueMustBeZero) + return vmcommon.UserError + } + err := s.eei.UseGas(s.gasCost.MetaChainSystemSCsCost.FixWaitingListSize) + if err != nil { + s.eei.AddReturnMessage("insufficient gas") + return vmcommon.OutOfGas + } + if len(args.Arguments) != 1 { + s.eei.AddReturnMessage("not enough arguments") + return vmcommon.UserError + } + + blsKey := args.Arguments[0] + _, err = s.getWaitingListElement(blsKey) + if err != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + + waitingListData, err := s.getFirstElementsFromWaitingList(math.MaxUint32) + if err != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + + for _, keyInList := range waitingListData.blsKeys { + if bytes.Equal(keyInList, blsKey) { + s.eei.AddReturnMessage("key is in queue, not missing") + return vmcommon.UserError + } + } + + waitingList, err := s.getWaitingListHead() + if err != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + + waitingList.Length += 1 + if waitingList.Length == 1 { + err = s.startWaitingList(waitingList, false, blsKey) + if err != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + } + + err = s.addToEndOfTheList(waitingList, blsKey) + if err != nil { + s.eei.AddReturnMessage(err.Error()) + return vmcommon.UserError + } + + return vmcommon.Ok +} + // EpochConfirmed is called whenever a new epoch is confirmed func (s *stakingSC) EpochConfirmed(epoch uint32, _ uint64) { s.flagEnableStaking.Toggle(epoch >= s.enableStakingEpoch) From fb688eb2e1c891aad857c2cf0857a6b24810c73c Mon Sep 17 00:00:00 2001 From: Robert Sasu Date: Tue, 26 Oct 2021 12:34:04 +0300 Subject: [PATCH 8/9] added new function to correct values --- vm/systemSmartContracts/staking.go | 2 ++ vm/systemSmartContracts/staking_test.go | 24 ++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/vm/systemSmartContracts/staking.go b/vm/systemSmartContracts/staking.go index 699d0b450ce..f3e72f2e2d6 100644 --- a/vm/systemSmartContracts/staking.go +++ b/vm/systemSmartContracts/staking.go @@ -2058,6 +2058,8 @@ func (s *stakingSC) addMissingNodeToQueue(args *vmcommon.ContractCallInput) vmco s.eei.AddReturnMessage(err.Error()) return vmcommon.UserError } + + return vmcommon.Ok } err = s.addToEndOfTheList(waitingList, blsKey) diff --git a/vm/systemSmartContracts/staking_test.go b/vm/systemSmartContracts/staking_test.go index 4e2f8d34655..4aec15b13c6 100644 --- a/vm/systemSmartContracts/staking_test.go +++ b/vm/systemSmartContracts/staking_test.go @@ -3012,3 +3012,27 @@ func checkIsStaked(t *testing.T, sc *stakingSC, callerAddr, stakerPubKey []byte, retCode := sc.Execute(arguments) assert.Equal(t, expectedCode, retCode) } + +func TestStakingSc_fixMissingNodeOnQueue(t *testing.T) { + t.Parallel() + + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), + } + sc, eei, marshalizer, stakingAccessAddress := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + alterWaitingListLength(t, eei, marshalizer) + + arguments := CreateVmContractCallInput() + arguments.Function = "getQueueRegisterNonceAndRewardAddress" + arguments.CallerAddr = stakingAccessAddress + arguments.Arguments = make([][]byte, 0) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + assert.Equal(t, 3*len(waitingBlsKeys), len(eei.output)) + for i, waitingKey := range waitingBlsKeys { + assert.Equal(t, waitingKey, eei.output[i*3]) + } +} From 14b56dc0785d3bfaf16d009f429144c51ea96792 Mon Sep 17 00:00:00 2001 From: Robert Sasu Date: Tue, 26 Oct 2021 13:19:51 +0300 Subject: [PATCH 9/9] fix and unit tests --- vm/systemSmartContracts/staking.go | 4 +- vm/systemSmartContracts/staking_test.go | 104 ++++++++++++++++++++++-- 2 files changed, 99 insertions(+), 9 deletions(-) diff --git a/vm/systemSmartContracts/staking.go b/vm/systemSmartContracts/staking.go index f3e72f2e2d6..6d8a2c391e7 100644 --- a/vm/systemSmartContracts/staking.go +++ b/vm/systemSmartContracts/staking.go @@ -2021,12 +2021,12 @@ func (s *stakingSC) addMissingNodeToQueue(args *vmcommon.ContractCallInput) vmco return vmcommon.OutOfGas } if len(args.Arguments) != 1 { - s.eei.AddReturnMessage("not enough arguments") + s.eei.AddReturnMessage("invalid number of arguments") return vmcommon.UserError } blsKey := args.Arguments[0] - _, err = s.getWaitingListElement(blsKey) + _, err = s.getWaitingListElement(createWaitingListKey(blsKey)) if err != nil { s.eei.AddReturnMessage(err.Error()) return vmcommon.UserError diff --git a/vm/systemSmartContracts/staking_test.go b/vm/systemSmartContracts/staking_test.go index 4aec15b13c6..231fcbe1cad 100644 --- a/vm/systemSmartContracts/staking_test.go +++ b/vm/systemSmartContracts/staking_test.go @@ -3021,18 +3021,108 @@ func TestStakingSc_fixMissingNodeOnQueue(t *testing.T) { []byte("waitingBlsKey2"), []byte("waitingBlsKey3"), } - sc, eei, marshalizer, stakingAccessAddress := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) - alterWaitingListLength(t, eei, marshalizer) + sc, eei, _, stakingAccessAddress := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) arguments := CreateVmContractCallInput() - arguments.Function = "getQueueRegisterNonceAndRewardAddress" - arguments.CallerAddr = stakingAccessAddress + arguments.Function = "addMissingNodeToQueue" + arguments.CallerAddr = bytes.Repeat([]byte{1}, 32) + arguments.Arguments = make([][]byte, 0) + + eei.returnMessage = "" + sc.flagCorrectFirstQueued.Unset() + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.UserError, retCode) + assert.Equal(t, "invalid method to call", eei.returnMessage) + + eei.returnMessage = "" + sc.flagCorrectFirstQueued.Set() + arguments.CallValue = big.NewInt(10) + retCode = sc.Execute(arguments) + assert.Equal(t, vmcommon.UserError, retCode) + assert.Equal(t, vm.TransactionValueMustBeZero, eei.returnMessage) + + eei.gasRemaining = 1 + sc.gasCost.MetaChainSystemSCsCost.FixWaitingListSize = 50 + eei.returnMessage = "" + arguments.CallValue = big.NewInt(0) + retCode = sc.Execute(arguments) + assert.Equal(t, vmcommon.OutOfGas, retCode) + assert.Equal(t, "insufficient gas", eei.returnMessage) + + eei.gasRemaining = 50 + eei.returnMessage = "" + retCode = sc.Execute(arguments) + assert.Equal(t, vmcommon.UserError, retCode) + assert.Equal(t, "invalid number of arguments", eei.returnMessage) + + eei.gasRemaining = 50 + eei.returnMessage = "" + arguments.Arguments = append(arguments.Arguments, []byte("waitingBlsKey4")) + retCode = sc.Execute(arguments) + assert.Equal(t, vmcommon.UserError, retCode) + assert.Equal(t, "element was not found", eei.returnMessage) + + doStake(t, sc, stakingAccessAddress, arguments.CallerAddr, []byte("waitingBlsKey4")) + + eei.gasRemaining = 50 + eei.returnMessage = "" + retCode = sc.Execute(arguments) + assert.Equal(t, vmcommon.UserError, retCode) + assert.Equal(t, "key is in queue, not missing", eei.returnMessage) +} + +func TestStakingSc_fixMissingNodeAddOneNodeOnly(t *testing.T) { + t.Parallel() + + sc, eei, _, _ := makeWrongConfigForWaitingBlsKeysList(t, nil) + + arguments := CreateVmContractCallInput() + arguments.Function = "addMissingNodeToQueue" + arguments.CallerAddr = bytes.Repeat([]byte{1}, 32) arguments.Arguments = make([][]byte, 0) + blsKey := []byte("waitingBlsKey1") + eei.returnMessage = "" + arguments.Arguments = append(arguments.Arguments, blsKey) + eei.gasRemaining = 50 + + sc.gasCost.MetaChainSystemSCsCost.FixWaitingListSize = 50 + _ = sc.saveWaitingListElement(createWaitingListKey(blsKey), &ElementInList{BLSPublicKey: blsKey}) + retCode := sc.Execute(arguments) assert.Equal(t, vmcommon.Ok, retCode) - assert.Equal(t, 3*len(waitingBlsKeys), len(eei.output)) - for i, waitingKey := range waitingBlsKeys { - assert.Equal(t, waitingKey, eei.output[i*3]) + + waitingListData, _ := sc.getFirstElementsFromWaitingList(50) + assert.Equal(t, len(waitingListData.blsKeys), 1) + assert.Equal(t, waitingListData.blsKeys[0], blsKey) +} + +func TestStakingSc_fixMissingNodeAddAsLast(t *testing.T) { + t.Parallel() + + waitingBlsKeys := [][]byte{ + []byte("waitingBlsKey1"), + []byte("waitingBlsKey2"), + []byte("waitingBlsKey3"), } + sc, eei, _, _ := makeWrongConfigForWaitingBlsKeysList(t, waitingBlsKeys) + sc.gasCost.MetaChainSystemSCsCost.FixWaitingListSize = 50 + + arguments := CreateVmContractCallInput() + arguments.Function = "addMissingNodeToQueue" + arguments.CallerAddr = bytes.Repeat([]byte{1}, 32) + arguments.Arguments = make([][]byte, 0) + + blsKey := []byte("waitingBlsKey4") + eei.returnMessage = "" + arguments.Arguments = append(arguments.Arguments, blsKey) + eei.gasRemaining = 50 + _ = sc.saveWaitingListElement(createWaitingListKey(blsKey), &ElementInList{BLSPublicKey: blsKey}) + + retCode := sc.Execute(arguments) + assert.Equal(t, vmcommon.Ok, retCode) + + waitingListData, _ := sc.getFirstElementsFromWaitingList(50) + assert.Equal(t, len(waitingListData.blsKeys), 4) + assert.Equal(t, waitingListData.blsKeys[3], blsKey) }