Skip to content

Commit

Permalink
Merge pull request #3534 from ElrondNetwork/fix-waiting-list-queue-size
Browse files Browse the repository at this point in the history
Waiting list length fix
  • Loading branch information
LucianMincu authored Oct 26, 2021
2 parents 356fc93 + bc9efa4 commit 2ef5218
Show file tree
Hide file tree
Showing 10 changed files with 621 additions and 18 deletions.
1 change: 1 addition & 0 deletions cmd/node/config/gasSchedules/gasScheduleV1.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
DelegationMgrOps = 50000000
ValidatorToDelegation = 500000000
GetAllNodeStates = 100000000
FixWaitingListSize = 500000000

[BaseOperationCost]
StorePerByte = 50000
Expand Down
11 changes: 7 additions & 4 deletions cmd/node/config/gasSchedules/gasScheduleV2.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,20 @@
ChangeRewardAddress = 5000000
ChangeValidatorKeys = 5000000
UnJail = 5000000
DelegationOps = 1000000
DelegationMgrOps = 50000000
ValidatorToDelegation = 500000000
ESDTIssue = 50000000
ESDTOperations = 50000000
Proposal = 5000000
Vote = 500000
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
Expand Down
1 change: 1 addition & 0 deletions cmd/node/config/gasSchedules/gasScheduleV3.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
GetAllNodeStates = 20000000
UnstakeTokens = 5000000
UnbondTokens = 5000000
FixWaitingListSize = 500000000

[BaseOperationCost]
StorePerByte = 50000
Expand Down
1 change: 1 addition & 0 deletions cmd/node/config/gasSchedules/gasScheduleV4.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
GetAllNodeStates = 20000000
UnstakeTokens = 5000000
UnbondTokens = 5000000
FixWaitingListSize = 500000000

[BaseOperationCost]
StorePerByte = 10000
Expand Down
1 change: 1 addition & 0 deletions factory/processComponents_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
1 change: 1 addition & 0 deletions process/factory/metachain/vmContainerFactory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
1 change: 1 addition & 0 deletions vm/gasCost.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ type MetaChainSystemSCsCost struct {
DelegationMgrOps uint64
ValidatorToDelegation uint64
GetAllNodeStates uint64
FixWaitingListSize uint64
}

// BuiltInCost defines cost for built-in methods
Expand Down
1 change: 1 addition & 0 deletions vm/systemSmartContracts/defaults/gasMap.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
192 changes: 178 additions & 14 deletions vm/systemSmartContracts/staking.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -225,6 +225,10 @@ 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)
case "addMissingNodeToQueue":
return s.addMissingNodeToQueue(args)
}

return vmcommon.UserError
Expand Down Expand Up @@ -830,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 {
Expand All @@ -851,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))
Expand Down Expand Up @@ -1059,8 +1072,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
Expand Down Expand Up @@ -1300,6 +1317,9 @@ func (s *stakingSC) getWaitingListIndex(args *vmcommon.ContractCallInput) vmcomm
return vmcommon.UserError
}

if len(prevElement.NextKey) == 0 {
break
}
index++
copy(nextKey, prevElement.NextKey)
}
Expand Down Expand Up @@ -1897,16 +1917,160 @@ func (s *stakingSC) getFirstElementsFromWaitingList(numNodes uint32) (*waitingLi

blsKeysToStake = append(blsKeysToStake, element.BLSPublicKey)
stakedDataList = append(stakedDataList, stakedData)

if len(element.NextKey) == 0 {
break
}
index++
copy(nextKey, element.NextKey)
}

if numNodes >= waitingListHead.Length && 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 <= 1 {
return vmcommon.Ok
}

foundLastJailedKey := len(waitingListHead.LastJailedKey) == 0

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(errGet.Error())
return vmcommon.UserError
}

if bytes.Equal(waitingListHead.LastJailedKey, nextKey) {
foundLastJailedKey = true
}

_, errGet = s.getOrCreateRegisteredData(element.BLSPublicKey)
if errGet != nil {
s.eei.AddReturnMessage(errGet.Error())
return vmcommon.UserError
}

if len(element.NextKey) == 0 {
break
}
index++
copy(nextKey, element.NextKey)
}

waitingListHead.Length = index
waitingListHead.LastKey = nextKey
if !foundLastJailedKey {
waitingListHead.LastJailedKey = make([]byte, 0)
}

err = s.saveWaitingListHead(waitingListHead)
if err != nil {
s.eei.AddReturnMessage(err.Error())
return vmcommon.UserError
}

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("invalid number of arguments")
return vmcommon.UserError
}

blsKey := args.Arguments[0]
_, err = s.getWaitingListElement(createWaitingListKey(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
}

return vmcommon.Ok
}

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)
Expand Down
Loading

0 comments on commit 2ef5218

Please sign in to comment.