Exemple #1
0
        private void OnBlockConnected(BlockConnected blockConnected)
        {
            ChainedHeaderBlock chBlock          = blockConnected.ConnectedBlock;
            uint256            newFinalizedHash = this.finalizedBlockInfo.GetFinalizedBlockInfo().Hash;

            lock (this.locker)
            {
                foreach (Poll poll in this.polls.Where(x => !x.IsPending && x.PollVotedInFavorBlockData.Hash == newFinalizedHash).ToList())
                {
                    this.logger.LogDebug("Applying poll '{0}'.", poll);
                    this.pollResultExecutor.ApplyChange(poll.VotingData);

                    poll.PollExecutedBlockData = new HashHeightPair(chBlock.ChainedHeader);
                    this.pollsRepository.UpdatePoll(poll);
                }
            }

            byte[] rawVotingData = this.votingDataEncoder.ExtractRawVotingData(chBlock.Block.Transactions[0]);

            if (rawVotingData == null)
            {
                this.logger.LogTrace("(-)[NO_VOTING_DATA]");
                return;
            }

            // Pub key of a fed member that created voting data.
            string fedMemberKeyHex = this.slotsManager.GetFederationMemberForTimestamp(chBlock.Block.Header.Time).PubKey.ToHex();

            List <VotingData> votingDataList = this.votingDataEncoder.Decode(rawVotingData);

            this.logger.LogDebug("Applying {0} voting data items included in a block by '{1}'.", votingDataList.Count, fedMemberKeyHex);

            lock (this.locker)
            {
                foreach (VotingData data in votingDataList)
                {
                    Poll poll = this.polls.SingleOrDefault(x => x.VotingData == data && x.IsPending);

                    if (poll == null)
                    {
                        poll = new Poll()
                        {
                            Id = this.pollsRepository.GetHighestPollId() + 1,
                            PollVotedInFavorBlockData = null,
                            PollExecutedBlockData     = null,
                            PollStartBlockData        = new HashHeightPair(chBlock.ChainedHeader),
                            VotingData             = data,
                            PubKeysHexVotedInFavor = new List <string>()
                            {
                                fedMemberKeyHex
                            }
                        };

                        this.polls.Add(poll);
                        this.pollsRepository.AddPolls(poll);

                        this.logger.LogDebug("New poll was created: '{0}'.", poll);
                    }
                    else if (!poll.PubKeysHexVotedInFavor.Contains(fedMemberKeyHex))
                    {
                        poll.PubKeysHexVotedInFavor.Add(fedMemberKeyHex);
                        this.pollsRepository.UpdatePoll(poll);

                        this.logger.LogDebug("Voted on existing poll: '{0}'.", poll);
                    }
                    else
                    {
                        this.logger.LogDebug("Fed member '{0}' already voted for this poll. Ignoring his vote. Poll: '{1}'.", fedMemberKeyHex, poll);
                    }

                    List <string> fedMembersHex = this.federationManager.GetFederationMembers().Select(x => x.PubKey.ToHex()).ToList();

                    // It is possible that there is a vote from a federation member that was deleted from the federation.
                    // Do not count votes from entities that are not active fed members.
                    int validVotesCount = poll.PubKeysHexVotedInFavor.Count(x => fedMembersHex.Contains(x));

                    int requiredVotesCount = (fedMembersHex.Count / 2) + 1;

                    this.logger.LogDebug("Fed members count: {0}, valid votes count: {1}, required votes count: {2}.", fedMembersHex.Count, validVotesCount, requiredVotesCount);

                    if (validVotesCount < requiredVotesCount)
                    {
                        continue;
                    }

                    poll.PollVotedInFavorBlockData = new HashHeightPair(chBlock.ChainedHeader);
                    this.pollsRepository.UpdatePoll(poll);
                }
            }
        }
Exemple #2
0
        private void OnBlockDisconnected(BlockDisconnected blockDisconnected)
        {
            ChainedHeaderBlock chBlock = blockDisconnected.DisconnectedBlock;

            lock (this.locker)
            {
                foreach (Poll poll in this.polls.Where(x => !x.IsPending && x.PollExecutedBlockData?.Hash == chBlock.ChainedHeader.HashBlock).ToList())
                {
                    this.logger.LogDebug("Reverting poll execution '{0}'.", poll);
                    this.pollResultExecutor.RevertChange(poll.VotingData);

                    poll.PollExecutedBlockData = null;
                    this.pollsRepository.UpdatePoll(poll);
                }
            }

            byte[] rawVotingData = this.votingDataEncoder.ExtractRawVotingData(chBlock.Block.Transactions[0]);

            if (rawVotingData == null)
            {
                this.logger.LogTrace("(-)[NO_VOTING_DATA]");
                return;
            }

            List <VotingData> votingDataList = this.votingDataEncoder.Decode(rawVotingData);

            votingDataList.Reverse();

            lock (this.locker)
            {
                foreach (VotingData votingData in votingDataList)
                {
                    if (this.IsVotingOnMultisigMember(votingData))
                    {
                        continue;
                    }

                    // If the poll is pending, that's the one we want. There should be maximum 1 of these.
                    Poll targetPoll = this.polls.SingleOrDefault(x => x.VotingData == votingData && x.IsPending);

                    // Otherwise, get the most recent poll. There could currently be unlimited of these, though they're harmless.
                    if (targetPoll == null)
                    {
                        targetPoll = this.polls.Last(x => x.VotingData == votingData);
                    }

                    this.logger.LogDebug("Reverting poll voting in favor: '{0}'.", targetPoll);

                    if (targetPoll.PollVotedInFavorBlockData == new HashHeightPair(chBlock.ChainedHeader))
                    {
                        targetPoll.PollVotedInFavorBlockData = null;

                        this.pollsRepository.UpdatePoll(targetPoll);
                    }

                    // Pub key of a fed member that created voting data.
                    string fedMemberKeyHex = this.slotsManager.GetFederationMemberForTimestamp(chBlock.Block.Header.Time).PubKey.ToHex();

                    targetPoll.PubKeysHexVotedInFavor.Remove(fedMemberKeyHex);

                    if (targetPoll.PubKeysHexVotedInFavor.Count == 0)
                    {
                        this.polls.Remove(targetPoll);
                        this.pollsRepository.RemovePolls(targetPoll.Id);

                        this.logger.LogDebug("Poll with Id {0} was removed.", targetPoll.Id);
                    }
                }
            }
        }
        private void OnBlockConnected(BlockConnected blockConnected)
        {
            try
            {
                ChainedHeaderBlock chBlock          = blockConnected.ConnectedBlock;
                uint256            newFinalizedHash = this.finalizedBlockInfo.GetFinalizedBlockInfo().Hash;

                lock (this.locker)
                {
                    foreach (Poll poll in this.polls.Where(x => !x.IsPending && x.PollVotedInFavorBlockData.Hash == newFinalizedHash).ToList())
                    {
                        this.logger.LogDebug("Applying poll '{0}'.", poll);
                        this.pollResultExecutor.ApplyChange(poll.VotingData);

                        poll.PollExecutedBlockData = new HashHeightPair(chBlock.ChainedHeader);
                        this.pollsRepository.UpdatePoll(poll);
                    }
                }

                byte[] rawVotingData = this.votingDataEncoder.ExtractRawVotingData(chBlock.Block.Transactions[0]);

                if (rawVotingData == null)
                {
                    this.logger.LogTrace("(-)[NO_VOTING_DATA]");
                    return;
                }

                // Pub key of a fed member that created voting data.
                string fedMemberKeyHex = this.slotsManager.GetFederationMemberForTimestamp(chBlock.Block.Header.Time).PubKey.ToHex();

                List <VotingData> votingDataList = this.votingDataEncoder.Decode(rawVotingData);

                this.logger.LogDebug("Applying {0} voting data items included in a block by '{1}'.", votingDataList.Count, fedMemberKeyHex);

                lock (this.locker)
                {
                    foreach (VotingData data in votingDataList)
                    {
                        if (this.IsVotingOnMultisigMember(data))
                        {
                            continue;
                        }

                        Poll poll = this.polls.SingleOrDefault(x => x.VotingData == data && x.IsPending);

                        if (poll == null)
                        {
                            // Ensures that highestPollId can't be changed before the poll is committed.
                            this.pollsRepository.Synchronous(() =>
                            {
                                poll = new Poll()
                                {
                                    Id = this.pollsRepository.GetHighestPollId() + 1,
                                    PollVotedInFavorBlockData = null,
                                    PollExecutedBlockData     = null,
                                    PollStartBlockData        = new HashHeightPair(chBlock.ChainedHeader),
                                    VotingData             = data,
                                    PubKeysHexVotedInFavor = new List <string>()
                                    {
                                        fedMemberKeyHex
                                    }
                                };

                                this.polls.Add(poll);
                                this.pollsRepository.AddPolls(poll);

                                this.logger.LogDebug("New poll was created: '{0}'.", poll);
                            });
                        }
                        else if (!poll.PubKeysHexVotedInFavor.Contains(fedMemberKeyHex))
                        {
                            poll.PubKeysHexVotedInFavor.Add(fedMemberKeyHex);
                            this.pollsRepository.UpdatePoll(poll);

                            this.logger.LogDebug("Voted on existing poll: '{0}'.", poll);
                        }
                        else
                        {
                            this.logger.LogDebug("Fed member '{0}' already voted for this poll. Ignoring his vote. Poll: '{1}'.", fedMemberKeyHex, poll);
                        }

                        var fedMembersHex = new ConcurrentHashSet <string>(this.federationManager.GetFederationMembers().Select(x => x.PubKey.ToHex()));

                        // Member that were about to be kicked when voting started don't participate.
                        if (this.idleFederationMembersKicker != null)
                        {
                            ChainedHeader chainedHeader = chBlock.ChainedHeader.GetAncestor(poll.PollStartBlockData.Height);

                            foreach (string pubKey in fedMembersHex)
                            {
                                if (this.idleFederationMembersKicker.ShouldMemberBeKicked(new PubKey(pubKey), chainedHeader.Header.Time, out _))
                                {
                                    fedMembersHex.TryRemove(pubKey);
                                }
                            }
                        }

                        // It is possible that there is a vote from a federation member that was deleted from the federation.
                        // Do not count votes from entities that are not active fed members.
                        int validVotesCount = poll.PubKeysHexVotedInFavor.Count(x => fedMembersHex.Contains(x));

                        int requiredVotesCount = (fedMembersHex.Count / 2) + 1;

                        this.logger.LogDebug("Fed members count: {0}, valid votes count: {1}, required votes count: {2}.", fedMembersHex.Count, validVotesCount, requiredVotesCount);

                        if (validVotesCount < requiredVotesCount)
                        {
                            continue;
                        }

                        poll.PollVotedInFavorBlockData = new HashHeightPair(chBlock.ChainedHeader);
                        this.pollsRepository.UpdatePoll(poll);
                    }
                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, ex.ToString());
                throw;
            }
        }
Exemple #4
0
        public IActionResult GetCurrentMemberInfo()
        {
            try
            {
                if (this.federationManager.CurrentFederationKey == null)
                {
                    throw new Exception("Your node is not registered as a federation member.");
                }

                var federationMemberModel = new FederationMemberDetailedModel
                {
                    PubKey = this.federationManager.CurrentFederationKey.PubKey.ToHex()
                };

                KeyValuePair <IFederationMember, uint> lastActive = this.federationHistory.GetFederationMembersByLastActiveTime().FirstOrDefault(x => x.Key.PubKey == this.federationManager.CurrentFederationKey.PubKey);
                if (lastActive.Key != null)
                {
                    federationMemberModel.LastActiveTime     = new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(lastActive.Value);
                    federationMemberModel.PeriodOfInActivity = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0).AddSeconds(lastActive.Value);
                }

                // Is this member part of a pending poll
                Poll poll = this.votingManager.GetPendingPolls().MemberPolls().OrderByDescending(p => p.PollStartBlockData.Height).FirstOrDefault(p => this.votingManager.GetMemberVotedOn(p.VotingData).PubKey == this.federationManager.CurrentFederationKey.PubKey);
                if (poll != null)
                {
                    federationMemberModel.PollType                  = poll.VotingData.Key.ToString();
                    federationMemberModel.PollStartBlockHeight      = poll.PollStartBlockData.Height;
                    federationMemberModel.PollNumberOfVotesAcquired = poll.PubKeysHexVotedInFavor.Count;
                }

                // Has the poll finished?
                poll = this.votingManager.GetApprovedPolls().MemberPolls().OrderByDescending(p => p.PollVotedInFavorBlockData.Height).FirstOrDefault(p => this.votingManager.GetMemberVotedOn(p.VotingData).PubKey == this.federationManager.CurrentFederationKey.PubKey);
                if (poll != null)
                {
                    federationMemberModel.PollType                                    = poll.VotingData.Key.ToString();
                    federationMemberModel.PollStartBlockHeight                        = poll.PollStartBlockData.Height;
                    federationMemberModel.PollNumberOfVotesAcquired                   = poll.PubKeysHexVotedInFavor.Count;
                    federationMemberModel.PollFinishedBlockHeight                     = poll.PollVotedInFavorBlockData.Height;
                    federationMemberModel.MemberWillStartMiningAtBlockHeight          = poll.PollVotedInFavorBlockData.Height + this.network.Consensus.MaxReorgLength;
                    federationMemberModel.MemberWillStartEarningRewardsEstimateHeight = federationMemberModel.MemberWillStartMiningAtBlockHeight + 480;

                    if (this.chainIndexer.Height > poll.PollVotedInFavorBlockData.Height + this.network.Consensus.MaxReorgLength)
                    {
                        federationMemberModel.PollWillFinishInBlocks = 0;
                    }
                    else
                    {
                        federationMemberModel.PollWillFinishInBlocks = (poll.PollVotedInFavorBlockData.Height + this.network.Consensus.MaxReorgLength) - this.chainIndexer.Tip.Height;
                    }
                }

                // Has the poll executed?
                poll = this.votingManager.GetExecutedPolls().MemberPolls().OrderByDescending(p => p.PollExecutedBlockData.Height).FirstOrDefault(p => this.votingManager.GetMemberVotedOn(p.VotingData).PubKey == this.federationManager.CurrentFederationKey.PubKey);
                if (poll != null)
                {
                    federationMemberModel.PollExecutedBlockHeight = poll.PollExecutedBlockData.Height;
                }

                federationMemberModel.RewardEstimatePerBlock = 9d / this.federationManager.GetFederationMembers().Count;

                return(Json(federationMemberModel));
            }
            catch (Exception e)
            {
                this.logger.Error("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
        private void OnBlockConnected(BlockConnected blockConnected)
        {
            try
            {
                ChainedHeaderBlock chBlock          = blockConnected.ConnectedBlock;
                HashHeightPair     newFinalizedHash = this.finalizedBlockInfo.GetFinalizedBlockInfo();

                lock (this.locker)
                {
                    if (this.isBusyReconstructing)
                    {
                        foreach (Poll poll in this.GetApprovedPolls().ToList())
                        {
                            if (blockConnected.ConnectedBlock.ChainedHeader.Height - poll.PollVotedInFavorBlockData.Height == this.network.Consensus.MaxReorgLength)
                            {
                                this.logger.LogDebug("Applying poll '{0}'.", poll);
                                this.pollResultExecutor.ApplyChange(poll.VotingData);

                                poll.PollExecutedBlockData = new HashHeightPair(chBlock.ChainedHeader);
                                this.pollsRepository.UpdatePoll(poll);
                            }
                        }
                    }
                    else
                    {
                        foreach (Poll poll in this.GetApprovedPolls().Where(x => x.PollVotedInFavorBlockData.Hash == newFinalizedHash.Hash).ToList())
                        {
                            this.logger.LogDebug("Applying poll '{0}'.", poll);
                            this.pollResultExecutor.ApplyChange(poll.VotingData);

                            poll.PollExecutedBlockData = new HashHeightPair(chBlock.ChainedHeader);
                            this.pollsRepository.UpdatePoll(poll);
                        }
                    }
                }

                byte[] rawVotingData = this.votingDataEncoder.ExtractRawVotingData(chBlock.Block.Transactions[0]);

                if (rawVotingData == null)
                {
                    this.logger.LogTrace("(-)[NO_VOTING_DATA]");
                    return;
                }

                string fedMemberKeyHex;

                // Please see the description under `VotingManagerV2ActivationHeight`.
                // PubKey of the federation member that created the voting data.
                if (this.poaConsensusOptions.VotingManagerV2ActivationHeight == 0 || blockConnected.ConnectedBlock.ChainedHeader.Height < this.poaConsensusOptions.VotingManagerV2ActivationHeight)
                {
                    fedMemberKeyHex = this.federationHistory.GetFederationMemberForTimestamp(chBlock.Block.Header.Time, this.poaConsensusOptions).PubKey.ToHex();
                }
                else
                {
                    fedMemberKeyHex = this.federationHistory.GetFederationMemberForBlock(chBlock.ChainedHeader).PubKey.ToHex();
                }

                List <VotingData> votingDataList = this.votingDataEncoder.Decode(rawVotingData);

                this.logger.LogDebug("Applying {0} voting data items included in a block by '{1}'.", votingDataList.Count, fedMemberKeyHex);

                lock (this.locker)
                {
                    foreach (VotingData data in votingDataList)
                    {
                        if (this.federationManager.CurrentFederationKey?.PubKey.ToHex() == fedMemberKeyHex)
                        {
                            // Any votes found in the block is no longer scheduled.
                            // This avoids clinging to votes scheduled during IBD.
                            if (this.scheduledVotingData.Any(v => v == data))
                            {
                                this.scheduledVotingData.Remove(data);
                            }
                        }

                        if (this.IsVotingOnMultisigMember(data))
                        {
                            continue;
                        }

                        Poll poll = this.polls.SingleOrDefault(x => x.VotingData == data && x.IsPending);

                        if (poll == null)
                        {
                            // Ensures that highestPollId can't be changed before the poll is committed.
                            this.pollsRepository.Synchronous(() =>
                            {
                                poll = new Poll()
                                {
                                    Id = this.pollsRepository.GetHighestPollId() + 1,
                                    PollVotedInFavorBlockData = null,
                                    PollExecutedBlockData     = null,
                                    PollStartBlockData        = new HashHeightPair(chBlock.ChainedHeader),
                                    VotingData             = data,
                                    PubKeysHexVotedInFavor = new List <string>()
                                    {
                                        fedMemberKeyHex
                                    }
                                };

                                this.polls.Add(poll);
                                this.pollsRepository.AddPolls(poll);

                                this.logger.LogDebug("New poll was created: '{0}'.", poll);
                            });
                        }
                        else if (!poll.PubKeysHexVotedInFavor.Contains(fedMemberKeyHex))
                        {
                            poll.PubKeysHexVotedInFavor.Add(fedMemberKeyHex);
                            this.pollsRepository.UpdatePoll(poll);

                            this.logger.LogDebug("Voted on existing poll: '{0}'.", poll);
                        }
                        else
                        {
                            this.logger.LogDebug("Fed member '{0}' already voted for this poll. Ignoring his vote. Poll: '{1}'.", fedMemberKeyHex, poll);
                        }

                        var fedMembersHex = new ConcurrentHashSet <string>(this.federationManager.GetFederationMembers().Select(x => x.PubKey.ToHex()));

                        // Member that were about to be kicked when voting started don't participate.
                        if (this.idleFederationMembersKicker != null)
                        {
                            ChainedHeader chainedHeader = chBlock.ChainedHeader.GetAncestor(poll.PollStartBlockData.Height);

                            if (chainedHeader?.Header == null)
                            {
                                this.logger.LogWarning("Couldn't retrieve header for block at height-hash: {0}-{1}.", poll.PollStartBlockData.Height, poll.PollStartBlockData.Hash?.ToString());

                                Guard.NotNull(chainedHeader, nameof(chainedHeader));
                                Guard.NotNull(chainedHeader.Header, nameof(chainedHeader.Header));
                            }

                            foreach (string pubKey in fedMembersHex)
                            {
                                if (this.idleFederationMembersKicker.ShouldMemberBeKicked(new PubKey(pubKey), chainedHeader.Header.Time, out _))
                                {
                                    fedMembersHex.TryRemove(pubKey);
                                }
                            }
                        }

                        // It is possible that there is a vote from a federation member that was deleted from the federation.
                        // Do not count votes from entities that are not active fed members.
                        int validVotesCount = poll.PubKeysHexVotedInFavor.Count(x => fedMembersHex.Contains(x));

                        int requiredVotesCount = (fedMembersHex.Count / 2) + 1;

                        this.logger.LogDebug("Fed members count: {0}, valid votes count: {1}, required votes count: {2}.", fedMembersHex.Count, validVotesCount, requiredVotesCount);

                        if (validVotesCount < requiredVotesCount)
                        {
                            continue;
                        }

                        poll.PollVotedInFavorBlockData = new HashHeightPair(chBlock.ChainedHeader);
                        this.pollsRepository.UpdatePoll(poll);
                    }
                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, ex.ToString());
                throw;
            }
        }
Exemple #6
0
        // TODO subscribe to signals
        // TODO more logs and log polls
        private void onBlockConnected(ChainedHeaderBlock chBlock)
        {
            byte[] rawVotingData = this.votingDataEncoder.ExtractRawVotingData(chBlock.Block.Transactions[0]);

            if (rawVotingData == null)
            {
                this.logger.LogTrace("(-)[NO_VOTING_DATA]");
                return;
            }

            // Pub key of a fed member that created voting data.
            string fedMemberKeyHex = this.slotsManager.GetPubKeyForTimestamp(chBlock.Block.Header.Time).ToHex();

            List <VotingData> votingDataList = this.votingDataEncoder.Decode(rawVotingData);

            lock (this.locker)
            {
                foreach (VotingData data in votingDataList)
                {
                    Poll existingPoll = this.pendingPolls.SingleOrDefault(x => x.VotingData == data);

                    if (existingPoll == null)
                    {
                        existingPoll = new Poll()
                        {
                            PollAppliedBlockHash   = null,
                            PollStartBlockHash     = chBlock.Block.GetHash(),
                            VotingData             = data,
                            PubKeysHexVotedInFavor = new List <string>()
                            {
                                fedMemberKeyHex
                            }
                        };

                        this.pendingPolls.Add(existingPoll);
                        this.logger.LogDebug("New poll was created.");
                    }
                    else if (!existingPoll.PubKeysHexVotedInFavor.Contains(fedMemberKeyHex))
                    {
                        existingPoll.PubKeysHexVotedInFavor.Add(fedMemberKeyHex);

                        this.logger.LogDebug("Voted on existing poll.");
                    }

                    List <string> fedMembersHex = this.federationManager.GetFederationMembers().Select(x => x.ToHex()).ToList();

                    // It is possible that there is a vote from a federation member that was deleted from the federation.
                    // Do not count votes from entities that are not active fed members.
                    int validVotesCount = existingPoll.PubKeysHexVotedInFavor.Count(x => fedMembersHex.Contains(x));

                    int requiredVotesCount = (fedMembersHex.Count / 2) + 1;

                    if (validVotesCount > requiredVotesCount)
                    {
                        // TODO apply changes, move active poll to finished polls, set finished block hash

                        // TODO to apply changes use voting data result executor that has methods like apply change and revert change
                    }
                }

                this.SavePolls(this.pendingPolls, PendingPollsDbKey);
                this.SavePolls(this.finishedPolls, FinishedPollsDbKey);
            }
        }