public override BlockController Controller() { BlockConnected controller = new BlockConnected(MeshSize, ConnMeshSizeX, ConnMeshSizeY, ConnMeshSizeZ, EnabledDirections[0]); controller.blockName = blockName; controller.isSolid = blockIsSolid; controller.solidTowardsSameType = solidTowardsSameType; TextureCollection[] textureCoordinates = new TextureCollection[6]; for (int i = 0; i < 6; i++) { try { textureCoordinates[i] = Block.index.textureIndex.GetTextureCollection(textures[i]); } catch { if (Application.isPlaying) Debug.LogError("Couldn't find texture for " + textures[i]); } } controller.textures = textureCoordinates; return controller; }
private void OnBlockConnected(BlockConnected blockConnectedData) { // Update last active time. uint timestamp = blockConnectedData.ConnectedBlock.ChainedHeader.Header.Time; PubKey key = this.slotsManager.GetPubKeyForTimestamp(timestamp); this.fedMembersByLastActiveTime.AddOrReplace(key, timestamp); this.SaveMembersByLastActiveTime(); // Check if any fed member was idle for too long. ChainedHeader tip = this.consensusManager.Tip; foreach (KeyValuePair <PubKey, uint> fedMemberToActiveTime in this.fedMembersByLastActiveTime) { uint inactiveForSeconds = tip.Header.Time - fedMemberToActiveTime.Value; if (inactiveForSeconds > this.federationMemberMaxIdleTimeSeconds) { this.logger.LogDebug("Federation member '{0}' was inactive for {1} seconds and will be scheduled to be kicked.", fedMemberToActiveTime.Key, inactiveForSeconds); this.votingManager.ScheduleVote(new VotingData() { Key = VoteKey.KickFederationMember, Data = fedMemberToActiveTime.Key.ToBytes() }); } } }
private void OnBlockConnected(BlockConnected blockConnected) { if (this.initialBlockDownloadState.IsInitialBlockDownload()) { return; } // Check if the current block is after reward batching activation height. if (blockConnected.ConnectedBlock.ChainedHeader.Height >= this.network.RewardClaimerBatchActivationHeight) { this.logger.LogInformation($"Batching rewards, the next distribution will be at block {this.lastDistributionHeight + 1 + this.network.RewardClaimerBlockInterval}."); // Check if the reward claimer should be triggered. if (blockConnected.ConnectedBlock.ChainedHeader.Height > this.network.RewardClaimerBatchActivationHeight && blockConnected.ConnectedBlock.ChainedHeader.Height % this.network.RewardClaimerBlockInterval == 0) { BuildAndCompleteRewardClaim(true, this.lastDistributionHeight + this.network.RewardClaimerBlockInterval); } } else { this.logger.LogInformation($"Per block reward claiming in effect until block {this.network.RewardClaimerBatchActivationHeight} (rewards are not batched)."); BuildAndCompleteRewardClaim(false, blockConnected.ConnectedBlock.ChainedHeader.Height); } }
private void OnBlockConnected(BlockConnected blockConnected) { ChainedHeaderBlock blockPair = blockConnected.ConnectedBlock; ChainedHeader chainedHeader = blockPair.ChainedHeader; if (chainedHeader == null) { this.logger.LogTrace("(-)[REORG]"); return; } this.logger.LogTrace("Block hash is '{0}'.", chainedHeader.HashBlock); bool isIBD = this.initialBlockDownloadState.IsInitialBlockDownload(); // Ensure the block is written to disk before relaying. this.AddBlockToQueue(blockPair, isIBD); if (isIBD) { this.logger.LogTrace("(-)[IBD]"); return; } if (this.storeSettings.PruningEnabled) { this.logger.LogTrace("(-)[PRUNE]"); return; } this.logger.LogTrace("Block header '{0}' added to the announce queue.", chainedHeader); this.blocksToAnnounce.Enqueue(chainedHeader); }
private void OnBlockConnected(BlockConnected blockConnectedData) { // Update last active time. uint timestamp = blockConnectedData.ConnectedBlock.ChainedHeader.Header.Time; PubKey key = this.slotsManager.GetFederationMemberForTimestamp(timestamp).PubKey; this.fedPubKeysByLastActiveTime.AddOrReplace(key, timestamp); this.SaveMembersByLastActiveTime(); // Check if any fed member was idle for too long. ChainedHeader tip = this.consensusManager.Tip; foreach (KeyValuePair <PubKey, uint> fedMemberToActiveTime in this.fedPubKeysByLastActiveTime) { uint inactiveForSeconds; if (fedMemberToActiveTime.Value == 0) { // Fed member was never active, count from first block after genesis. inactiveForSeconds = tip.Header.Time - blockConnectedData.ConnectedBlock.ChainedHeader.GetAncestor(1).Header.Time; } else { inactiveForSeconds = tip.Header.Time - fedMemberToActiveTime.Value; } if (inactiveForSeconds > this.federationMemberMaxIdleTimeSeconds && this.federationManager.IsFederationMember) { IFederationMember memberToKick = this.federationManager.GetFederationMembers().SingleOrDefault(x => x.PubKey == fedMemberToActiveTime.Key); byte[] federationMemberBytes = this.consensusFactory.SerializeFederationMember(memberToKick); List <Poll> finishedPolls = this.votingManager.GetFinishedPolls(); bool alreadyKicking = finishedPolls.Any(x => !x.IsExecuted && x.VotingData.Key == VoteKey.KickFederationMember && x.VotingData.Data.SequenceEqual(federationMemberBytes) && x.PubKeysHexVotedInFavor.Contains(this.federationManager.CurrentFederationKey.PubKey.ToHex())); if (!alreadyKicking) { this.logger.LogWarning("Federation member '{0}' was inactive for {1} seconds and will be scheduled to be kicked.", fedMemberToActiveTime.Key, inactiveForSeconds); this.votingManager.ScheduleVote(new VotingData() { Key = VoteKey.KickFederationMember, Data = fedMemberToActiveTime.Key.ToBytes() }); } else { this.logger.LogDebug("Skipping because kicking is already voted for."); } } } }
private void UpdateFederationMembersLastActiveTime(BlockConnected blockConnected) { // The pubkey of the member that signed the block. PubKey key = this.slotsManager.GetFederationMemberForBlock(blockConnected.ConnectedBlock.ChainedHeader, this.votingManager).PubKey; // Update the dictionary. this.fedPubKeysByLastActiveTime.AddOrReplace(key, blockConnected.ConnectedBlock.ChainedHeader.Header.Time); // Save it back. this.SaveMembersByLastActiveTime(); }
private void OnBlockConnected(BlockConnected blockConnected) { ChainedHeaderBlock chainedHeaderBlock = blockConnected.ConnectedBlock; ChainedHeader blockHeader = chainedHeaderBlock.ChainedHeader; Task task = this.RemoveForBlock(chainedHeaderBlock.Block, blockHeader?.Height ?? -1); // wait for the mempool code to complete // until the signaler becomes async task.GetAwaiter().GetResult(); }
private void OnBlockConnected(BlockConnected e) { lock (this.lockObject) { foreach (var input in e.ConnectedBlock.Block.Transactions.SelectMany(t => t.Inputs)) { this.logger.LogDebug("Tx added to block and removed from mempool, unreserving Utxo '{0}'", input.PrevOut.Hash); this.reservedCoins.Remove(input.PrevOut); } } }
private void OnBlockConnected(BlockConnected blockConnected) { this.PollsRepository.Synchronous(() => { if (this.Synchronize(blockConnected.ConnectedBlock.ChainedHeader.Previous)) { this.PollsRepository.WithTransaction(transaction => { this.ProcessBlock(transaction, blockConnected.ConnectedBlock); transaction.Commit(); }); } }); }
private void OnBlockConnected(BlockConnected blockConnected) { if (this.initialBlockDownloadState.IsInitialBlockDownload()) { return; } Transaction transaction = BuildRewardTransaction(); if (transaction != null) { // It does not really matter whether the reward has been claimed already, as the transaction will simply be rejected by the other nodes on the network if it has. // So just broadcast it anyway. this.broadcasterManager.BroadcastTransactionAsync(transaction); } }
private void OnBlockConnected(BlockConnected blockConnected) { if (this.initialBlockDownloadState.IsInitialBlockDownload()) { return; } // Check if the current block is after reward batching activation height. if (blockConnected.ConnectedBlock.ChainedHeader.Height >= this.network.RewardClaimerBatchActivationHeight) { // Check if the block connected height is equal or below the last distribution height. // This is could happen due to a reorg and therefore we do nothing. if (blockConnected.ConnectedBlock.ChainedHeader.Height <= (this.lastDistributionHeight + 1)) { this.logger.LogInformation($"Reward claiming skipped as block window already processed; Block connected at {blockConnected.ConnectedBlock.ChainedHeader.Height}; Last distribution at {this.lastDistributionHeight}."); return; } // Check if the reward claimer should be triggered. if (blockConnected.ConnectedBlock.ChainedHeader.Height > this.network.RewardClaimerBatchActivationHeight && blockConnected.ConnectedBlock.ChainedHeader.Height % this.network.RewardClaimerBlockInterval == 0) { // If this node "skipped" a reward claimer run, then just bring the last claimer height up to // the last applicable round as the assumption is that some other node would have processed the reward claiming // properly. if (blockConnected.ConnectedBlock.ChainedHeader.Height - (this.lastDistributionHeight + 1) > this.network.RewardClaimerBlockInterval) { this.lastDistributionHeight = blockConnected.ConnectedBlock.ChainedHeader.Height - this.network.RewardClaimerBlockInterval - 1; this.logger.LogInformation($"[Reward Batching] The last reward window was skipped, resetting to {this.lastDistributionHeight}."); } this.logger.LogInformation($"[Reward Batching] Triggered at height {blockConnected.ConnectedBlock.ChainedHeader.Height}."); BuildAndCompleteRewardClaim(true, this.lastDistributionHeight + this.network.RewardClaimerBlockInterval); } else { this.logger.LogInformation($"[Reward Batching] The next distribution will be triggered at block {this.lastDistributionHeight + 1 + this.network.RewardClaimerBlockInterval}."); } } else { this.logger.LogInformation($"Per block reward claiming in effect until block {this.network.RewardClaimerBatchActivationHeight} (rewards are not batched)."); BuildAndCompleteRewardClaim(false, blockConnected.ConnectedBlock.ChainedHeader.Height); } }
private void OnBlockConnected(BlockConnected blockConnectedData) { try { // Update last active time. uint timestamp = blockConnectedData.ConnectedBlock.ChainedHeader.Header.Time; PubKey key = this.slotsManager.GetFederationMemberForBlock(blockConnectedData.ConnectedBlock.ChainedHeader, this.votingManager).PubKey; this.fedPubKeysByLastActiveTime.AddOrReplace(key, timestamp); this.SaveMembersByLastActiveTime(); // Check if any fed member was idle for too long. foreach (KeyValuePair <PubKey, uint> fedMemberToActiveTime in this.fedPubKeysByLastActiveTime) { // Check if any fed member was idle for too long. Use the timestamp of the connecting block. if (this.ShouldBeKicked(fedMemberToActiveTime.Key, timestamp, out uint inactiveForSeconds)) { IFederationMember memberToKick = this.federationManager.GetFederationMembers().SingleOrDefault(x => x.PubKey == fedMemberToActiveTime.Key); byte[] federationMemberBytes = this.consensusFactory.SerializeFederationMember(memberToKick); bool alreadyKicking = this.votingManager.AlreadyVotingFor(VoteKey.KickFederationMember, federationMemberBytes); if (!alreadyKicking) { this.logger.LogWarning("Federation member '{0}' was inactive for {1} seconds and will be scheduled to be kicked.", fedMemberToActiveTime.Key, inactiveForSeconds); this.votingManager.ScheduleVote(new VotingData() { Key = VoteKey.KickFederationMember, Data = federationMemberBytes }); } else { this.logger.LogDebug("Skipping because kicking is already voted for."); } } } } catch (Exception ex) { this.logger.LogError(ex, ex.ToString()); throw; } }
private void OnBlockConnected(BlockConnected blockConnectedData) { // Update last active time. uint timestamp = blockConnectedData.ConnectedBlock.ChainedHeader.Header.Time; PubKey key = this.slotsManager.GetFederationMemberForTimestamp(timestamp).PubKey; this.fedPubKeysByLastActiveTime.AddOrReplace(key, timestamp); this.SaveMembersByLastActiveTime(); // Check if any fed member was idle for too long. ChainedHeader tip = this.consensusManager.Tip; foreach (KeyValuePair <PubKey, uint> fedMemberToActiveTime in this.fedPubKeysByLastActiveTime) { uint inactiveForSeconds = tip.Header.Time - fedMemberToActiveTime.Value; if (inactiveForSeconds > this.federationMemberMaxIdleTimeSeconds && this.federationManager.IsFederationMember && !FederationVotingController.IsMultisigMember(this.network, fedMemberToActiveTime.Key)) { IFederationMember memberToKick = this.federationManager.GetFederationMembers().SingleOrDefault(x => x.PubKey == fedMemberToActiveTime.Key); byte[] federationMemberBytes = this.consensusFactory.SerializeFederationMember(memberToKick); bool alreadyKicking = this.votingManager.AlreadyVotingFor(VoteKey.KickFederationMember, federationMemberBytes); if (!alreadyKicking) { this.logger.LogWarning("Federation member '{0}' was inactive for {1} seconds and will be scheduled to be kicked.", fedMemberToActiveTime.Key, inactiveForSeconds); this.votingManager.ScheduleVote(new VotingData() { Key = VoteKey.KickFederationMember, Data = federationMemberBytes }); } else { this.logger.LogDebug("Skipping because kicking is already voted for."); } } } }
public void OnBlockConnected(BlockConnected blockConnectedData) { if (!(this.network.Consensus.ConsensusFactory is CollateralPoAConsensusFactory consensusFactory)) { return; } // Only mining federation members vote to include new members. if (this.federationManager.CurrentFederationKey?.PubKey == null) { return; } List <IFederationMember> modifiedFederation = null; List <Transaction> transactions = blockConnectedData.ConnectedBlock.Block.Transactions; var encoder = new JoinFederationRequestEncoder(); for (int i = 0; i < transactions.Count; i++) { Transaction tx = transactions[i]; try { JoinFederationRequest request = JoinFederationRequestBuilder.Deconstruct(tx, encoder); if (request == null) { continue; } // Skip if the member already exists. if (this.votingManager.IsFederationMember(request.PubKey)) { continue; } // Only mining federation members vote to include new members. modifiedFederation ??= this.votingManager.GetModifiedFederation(blockConnectedData.ConnectedBlock.ChainedHeader); if (!modifiedFederation.Any(m => m.PubKey == this.federationManager.CurrentFederationKey.PubKey)) { this.logger.LogDebug($"Ignoring as member '{this.federationManager.CurrentFederationKey.PubKey}' is not part of the federation at block '{blockConnectedData.ConnectedBlock.ChainedHeader}'."); return; } // Check if the collateral amount is valid. decimal collateralAmount = request.CollateralAmount.ToDecimal(MoneyUnit.BTC); var expectedCollateralAmount = CollateralFederationMember.GetCollateralAmountForPubKey((PoANetwork)this.network, request.PubKey); if (collateralAmount != expectedCollateralAmount) { this.logger.LogDebug("Ignoring voting collateral amount '{0}', when expecting '{1}'.", collateralAmount, expectedCollateralAmount); continue; } // Fill in the request.removalEventId (if any). byte[] federationMemberBytes = JoinFederationRequestService.GetFederationMemberBytes(request, this.network, this.counterChainNetwork); // Nothing to do if already voted. if (this.votingManager.AlreadyVotingFor(VoteKey.AddFederationMember, federationMemberBytes)) { this.logger.LogDebug("Skipping because already voted for adding '{0}'.", request.PubKey.ToHex()); continue; } // Populate the RemovalEventId. JoinFederationRequestService.SetLastRemovalEventId(request, federationMemberBytes, this.votingManager); // Check the signature. PubKey key = PubKey.RecoverFromMessage(request.SignatureMessage, request.Signature); if (key.Hash != request.CollateralMainchainAddress) { this.logger.LogDebug("Invalid collateral address validation signature for joining federation via transaction '{0}'", tx.GetHash()); continue; } // Vote to add the member. this.logger.LogDebug("Voting to add federation member '{0}'.", request.PubKey.ToHex()); this.votingManager.ScheduleVote(new VotingData() { Key = VoteKey.AddFederationMember, Data = federationMemberBytes }); } catch (Exception err) { this.logger.LogError(err.Message); } } }
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.GetPubKeyForTimestamp(chBlock.Block.Header.Time).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.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); } } }
private void OnBlockConnected(BlockConnected blockConnected) { // Get the minimum stake confirmations for the current network. int minStakeConfirmations = ((PosConsensusOptions)this.network.Consensus.Options).GetStakeMinConfirmations(this.chainIndexer.Height, this.network); // Take a local copy of the tip. ChainedHeader chainTip = this.chainIndexer.Tip; if (chainTip.Height < minStakeConfirmations) { // If the chain is not at least minStakeConfirmations long then just do nothing. return; } // Get the block that is minStakeConfirmations behind the current tip. ChainedHeader chainedHeader = this.chainIndexer.GetHeader(chainTip.Height - minStakeConfirmations); Block maturedBlock = chainedHeader.Block; // As this runs on the mainchain we presume there will be a coinstake transaction in the block (but during the PoW era there obviously may not be). // If not, just do nothing with this block. if (maturedBlock.Transactions.Count < 2 || !blockConnected.ConnectedBlock.Block.Transactions[1].IsCoinStake) { return; } // We are only interested in the coinstake, as it is the only transaction that we expect to contain outputs paying the reward script. Transaction coinStake = blockConnected.ConnectedBlock.Block.Transactions[1]; // Identify any outputs paying the reward script a nonzero amount. TxOut[] rewardOutputs = coinStake.Outputs.Where(o => o.ScriptPubKey == StraxCoinstakeRule.CirrusRewardScript && o.Value != 0).ToArray(); // This shouldn't be the case but check anyway. if (rewardOutputs.Length == 0) { return; } // Build a transaction using these inputs, paying the federation. var builder = new TransactionBuilder(this.network); foreach (TxOut txOutput in rewardOutputs) { // The reward script is P2SH, so we need to inform the builder of the corresponding redeem script to enable it to be spent. var coin = ScriptCoin.Create(this.network, coinStake, txOutput, StraxCoinstakeRule.CirrusRewardScriptRedeem); builder.AddCoins(coin); } // An OP_RETURN for a dummy Cirrus address that tells the sidechain federation they can distribute the transaction. builder.Send(StraxCoinstakeRule.CirrusTransactionTag, Money.Zero); // TODO: Revisit the handling of fees here - the consensus rules won't allow the fee to be paid from the actual reward builder.Send(this.network.Federations.GetOnlyFederation().MultisigScript, rewardOutputs.Sum(o => o.Value)); Transaction builtTransaction = builder.BuildTransaction(true); TransactionPolicyError[] errors = builder.Check(builtTransaction); if (errors.Length > 0) { foreach (TransactionPolicyError error in errors) { this.logger.LogWarning("Unable to validate reward claim transaction '{0}', error: {1}", builtTransaction.ToHex(), error.ToString()); } // Not much further can be done at this point. return; } // It does not really matter whether the reward has been claimed already, as the transaction will simply be rejected by the other nodes on the network if it has. // So just broadcast it anyway. this.broadcasterManager.BroadcastTransactionAsync(builtTransaction); }
private void OnBlockConnected(BlockConnected blockConnected) { this.ProcessBlock(blockConnected.ConnectedBlock.Block); }
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.Debug("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.Debug("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.Trace("(-)[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.Debug("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.Debug("New poll was created: '{0}'.", poll); }); } else if (!poll.PubKeysHexVotedInFavor.Contains(fedMemberKeyHex)) { poll.PubKeysHexVotedInFavor.Add(fedMemberKeyHex); this.pollsRepository.UpdatePoll(poll); this.logger.Debug("Voted on existing poll: '{0}'.", poll); } else { this.logger.Debug("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.Warn("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.Debug("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.Error(ex, ex.ToString()); throw; } }
/// <summary> /// This is to ensure that we keep <see cref="fedPubKeysByLastActiveTime"></see> up to date from other blocks being mined. /// </summary> /// <param name="blockConnected">The block that was connected.</param> private void OnBlockConnected(BlockConnected blockConnected) { UpdateFederationMembersLastActiveTime(blockConnected.ConnectedBlock); }
/// <summary> /// Every time a new block gets generated, this date time provider will be signaled, /// updating the last block time by 65 seconds. /// </summary> private void OnBlockConnected(BlockConnected blockConnected) { startFrom = startFrom.AddSeconds(65); }
void OnBlockConnected(BlockConnected blockConnected) { base.SyncWallet(); }
private void OnBlockConnected(BlockConnected blockConnectedData) { if (!(this.network.Consensus.ConsensusFactory is CollateralPoAConsensusFactory consensusFactory)) { return; } List <Transaction> transactions = blockConnectedData.ConnectedBlock.Block.Transactions; var encoder = new JoinFederationRequestEncoder(this.loggerFactory); for (int i = 0; i < transactions.Count; i++) { Transaction tx = transactions[i]; try { JoinFederationRequest request = JoinFederationRequestBuilder.Deconstruct(tx, encoder); if (request == null) { continue; } // Skip if the member already exists. if (this.votingManager.IsFederationMember(request.PubKey)) { continue; } // Check if the collateral amount is valid. decimal collateralAmount = request.CollateralAmount.ToDecimal(MoneyUnit.BTC); var expectedCollateralAmount = CollateralFederationMember.GetCollateralAmountForPubKey((PoANetwork)this.network, request.PubKey); if (collateralAmount != expectedCollateralAmount) { this.logger.LogDebug("Ignoring voting collateral amount '{0}', when expecting '{1}'.", collateralAmount, expectedCollateralAmount); continue; } // Fill in the request.removalEventId (if any). Script collateralScript = PayToPubkeyHashTemplate.Instance.GenerateScriptPubKey(request.CollateralMainchainAddress); var collateralFederationMember = new CollateralFederationMember(request.PubKey, false, request.CollateralAmount, collateralScript.GetDestinationAddress(this.counterChainNetwork).ToString()); byte[] federationMemberBytes = consensusFactory.SerializeFederationMember(collateralFederationMember); // Nothing to do if already voted. if (this.votingManager.AlreadyVotingFor(VoteKey.AddFederationMember, federationMemberBytes)) { this.logger.LogDebug("Skipping because already voted for adding '{0}'.", request.PubKey.ToHex()); continue; } // Populate the RemovalEventId. Poll poll = this.votingManager.GetFinishedPolls().FirstOrDefault(x => x.IsExecuted && x.VotingData.Key == VoteKey.KickFederationMember && x.VotingData.Data.SequenceEqual(federationMemberBytes)); request.RemovalEventId = (poll == null) ? Guid.Empty : new Guid(poll.PollExecutedBlockData.Hash.ToBytes().TakeLast(16).ToArray()); // Check the signature. PubKey key = PubKey.RecoverFromMessage(request.SignatureMessage, request.Signature); if (key.Hash != request.CollateralMainchainAddress) { this.logger.LogDebug("Invalid collateral address validation signature for joining federation via transaction '{0}'", tx.GetHash()); continue; } // Vote to add the member. this.logger.LogDebug("Voting to add federation member '{0}'.", request.PubKey.ToHex()); this.votingManager.ScheduleVote(new VotingData() { Key = VoteKey.AddFederationMember, Data = federationMemberBytes }); } catch (Exception err) { this.logger.LogDebug(err.Message); } } }
void OnBlockConnected(BlockConnected blockConnected) { ProcessBlock(blockConnected.ConnectedBlock.Block); }
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; } }
private void OnBlockConnected(BlockConnected blockConnected) { this.ProcessBlock(blockConnected.ConnectedBlock.ChainedHeader.Height, blockConnected.ConnectedBlock.Block); }