private void Run() { try { const ulong checkInterval = 1000; var lastCheckedBlockHeight = (ulong)0; var passingCycle = -1; Logger.LogInformation($"Validator status manager started, {_withdrawTriggered}, {_stakeSize}, {_stopRequested}"); while (!_withdrawTriggered) { if (_stopRequested) { break; } if (lastCheckedBlockHeight == _stateManager.LastApprovedSnapshot.Blocks.GetTotalBlockHeight() || GetCurrentCycle() == passingCycle) { Thread.Sleep(TimeSpan.FromMilliseconds(checkInterval)); continue; } lastCheckedBlockHeight = _stateManager.LastApprovedSnapshot.Blocks.GetTotalBlockHeight(); Logger.LogInformation($"Check {lastCheckedBlockHeight} height"); if (_sendingTxHash != null) { if (_stateManager.LastApprovedSnapshot.Transactions.GetTransactionByHash(_sendingTxHash) == null) { Logger.LogInformation( $"Transaction {_sendingTxHash.ToHex()} submitted, waiting for including in block"); Thread.Sleep(TimeSpan.FromMilliseconds(checkInterval)); continue; } _sendingTxHash = null; } var stake = _systemContractReader.GetStake().ToBigInteger(); Logger.LogInformation($"Stake size is {stake}"); var isStaker = !stake.IsZero; if (!isStaker) { var coverFeesAmount = new BigInteger(10) * BigInteger.Pow(10, 18); Logger.LogInformation($"Trying to become staker"); var balance = _stateManager.CurrentSnapshot.Balances.GetBalance(_systemContractReader.NodeAddress()); Logger.LogInformation($"Balance is {balance.ToWei()}"); if (_stakeSize is null) { Logger.LogInformation("Stake size is null, cannot become staker"); continue; } Logger.LogInformation($"Stake size is {_stakeSize}"); var isEnoughBalance = balance.ToWei() > _stakeSize.Value + coverFeesAmount; if (isEnoughBalance) { var rolls = _stakeSize.Value / StakingContract.TokenUnitsInRoll; Logger.LogInformation($"Sending transaction to become staker for {rolls} rolls"); BecomeStaker(rolls * StakingContract.TokenUnitsInRoll); _stakeSize = null; continue; } Logger.LogInformation($"Not enough balance to become staker"); continue; } var requestCycle = _systemContractReader.GetWithdrawRequestCycle(); Logger.LogInformation($"Request cycle is {requestCycle}"); if (requestCycle != 0) { Logger.LogInformation( $"Stake withdrawal triggered externally in cycle {requestCycle}. Processing withdrawal..."); _withdrawTriggered = true; continue; } if (_systemContractReader.IsAttendanceDetectionPhase() && _systemContractReader.IsPreviousValidator() && !_systemContractReader.IsCheckedIn()) { Logger.LogInformation( $"The node is previous validator. Trying to submit attendance detection."); SubmitAttendanceDetection(); continue; } if (_systemContractReader.IsNextValidator()) { Logger.LogDebug($"The node chosen as next validator. Nothing to do."); passingCycle = GetCurrentCycle(); continue; } if (!_systemContractReader.IsAbleToBeValidator() || !_systemContractReader.IsVrfSubmissionPhase()) { Logger.LogInformation($"Current submission phase missed. Waiting for the next one."); passingCycle = GetCurrentCycle(); continue; } var(isWinner, proof) = GetVrfProof(stake); if (isWinner) { Logger.LogDebug( $"The node won the VRF lottery. Submitting transaction to become the next cycle validator"); SubmitVrf(proof); continue; } Logger.LogInformation($"The node didn't win the VRF lottery. Waiting for the next cycle."); passingCycle = GetCurrentCycle(); } lastCheckedBlockHeight = 0; passingCycle = -1; // Try to withdraw stake while (!_systemContractReader.GetStake().IsZero()) { if (_stopRequested) { break; } if (_sendingTxHash != null) { if (_stateManager.LastApprovedSnapshot.Transactions.GetTransactionByHash(_sendingTxHash) == null) { Logger.LogInformation( $"Transaction {_sendingTxHash.ToHex()} submitted, waiting for including in block"); Thread.Sleep(TimeSpan.FromMilliseconds(checkInterval)); continue; } _sendingTxHash = null; } if (lastCheckedBlockHeight == _stateManager.LastApprovedSnapshot.Blocks.GetTotalBlockHeight() || GetCurrentCycle() == passingCycle) { Thread.Sleep(TimeSpan.FromMilliseconds(checkInterval)); continue; } Logger.LogWarning($"Trying to withdraw stake"); lastCheckedBlockHeight = _stateManager.LastApprovedSnapshot.Blocks.GetTotalBlockHeight(); if (_systemContractReader.IsAttendanceDetectionPhase() && _systemContractReader.IsPreviousValidator() && !_systemContractReader.IsCheckedIn()) { Logger.LogInformation( $"The node is previous validator. Trying to submit attendance detection."); SubmitAttendanceDetection(); continue; } var requestCycle = _systemContractReader.GetWithdrawRequestCycle(); if (requestCycle == 0) { if (IsNextValidator()) { Logger.LogWarning($"Stake reserved for the next cycle. Waiting for the next cycle."); passingCycle = GetCurrentCycle(); continue; } RequestStakeWithdrawal(); passingCycle = GetCurrentCycle(); Logger.LogWarning($"Submitted withdrawal stake request. Waiting for the next cycle."); continue; } if (GetCurrentCycle() <= requestCycle) { Logger.LogInformation( $"Stake withdrawal request in cycle {requestCycle}, current cycle is {GetCurrentCycle()}. " + $"Waiting for the next cycle to withdraw stake..." ); passingCycle = GetCurrentCycle(); continue; } if (!IsWithdrawalPhase()) { Logger.LogWarning($"Waiting for withdrawal phase..."); continue; } WithdrawStakeTx(); Logger.LogWarning( $"Stake withdrawal transaction submitted. Waiting for the next block to ensure withdrawal succeeded."); } _started = false; Logger.LogWarning($"Stake withdrawn. Validator status manager stopped."); } catch (Exception e) { Logger.LogCritical($"Fatal error in validator status manager, exiting: {e}"); Environment.Exit(1); } }