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()
                    });
                }
            }
        }
Ejemplo n.º 3
0
        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);
            }
        }
Ejemplo n.º 4
0
        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.");
                    }
                }
            }
        }
Ejemplo n.º 6
0
        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();
        }
Ejemplo n.º 7
0
        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);
         }
     }
 }
Ejemplo n.º 9
0
 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();
             });
         }
     });
 }
Ejemplo n.º 10
0
        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);
            }
        }
Ejemplo n.º 11
0
        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);
            }
        }
Ejemplo n.º 12
0
        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;
            }
        }
Ejemplo n.º 13
0
        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);
                }
            }
        }
Ejemplo n.º 15
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.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);
                }
            }
        }
Ejemplo n.º 16
0
        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);
        }
Ejemplo n.º 17
0
 private void OnBlockConnected(BlockConnected blockConnected)
 {
     this.ProcessBlock(blockConnected.ConnectedBlock.Block);
 }
Ejemplo n.º 18
0
        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;
            }
        }
Ejemplo n.º 19
0
 /// <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);
 }
Ejemplo n.º 21
0
 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);
                }
            }
        }
Ejemplo n.º 23
0
 void OnBlockConnected(BlockConnected blockConnected)
 {
     ProcessBlock(blockConnected.ConnectedBlock.Block);
 }
Ejemplo n.º 24
0
        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;
            }
        }
Ejemplo n.º 25
0
 private void OnBlockConnected(BlockConnected blockConnected)
 {
     this.ProcessBlock(blockConnected.ConnectedBlock.ChainedHeader.Height, blockConnected.ConnectedBlock.Block);
 }