public override Task RunAsync(RuleContext context)
        {
            // The genesis block won't contain any commitment data.
            if (context.ValidationContext.ChainedHeaderToValidate.Height == 0)
            {
                return(Task.CompletedTask);
            }

            // If the network's CollateralCommitmentActivationHeight is set then check that the current height is less than than that.
            if (context.ValidationContext.ChainedHeaderToValidate.Height < (this.Parent.Network as PoANetwork).CollateralCommitmentActivationHeight)
            {
                return(Task.CompletedTask);
            }

            var commitmentHeightEncoder = new CollateralHeightCommitmentEncoder(this.Logger);

            (int?commitmentHeight, _) = commitmentHeightEncoder.DecodeCommitmentHeight(context.ValidationContext.BlockToValidate.Transactions.First());
            if (commitmentHeight == null)
            {
                // Every PoA miner on a sidechain network is forced to include commitment data to the blocks mined.
                // Not having a commitment should always result in a permanent ban of the block.
                this.Logger.LogTrace("(-)[COLLATERAL_COMMITMENT_HEIGHT_MISSING]");
                PoAConsensusErrors.CollateralCommitmentHeightMissing.Throw();
            }

            return(Task.CompletedTask);
        }
 public CollateralPoAMiner(IConsensusManager consensusManager, IDateTimeProvider dateTimeProvider, Network network, INodeLifetime nodeLifetime, ILoggerFactory loggerFactory,
                           IInitialBlockDownloadState ibdState, BlockDefinition blockDefinition, ISlotsManager slotsManager, IConnectionManager connectionManager,
                           PoABlockHeaderValidator poaHeaderValidator, IFederationManager federationManager, IIntegrityValidator integrityValidator, IWalletManager walletManager,
                           INodeStats nodeStats, VotingManager votingManager, PoAMinerSettings poAMinerSettings, ICollateralChecker collateralChecker, IAsyncProvider asyncProvider)
     : base(consensusManager, dateTimeProvider, network, nodeLifetime, loggerFactory, ibdState, blockDefinition, slotsManager, connectionManager,
            poaHeaderValidator, federationManager, integrityValidator, walletManager, nodeStats, votingManager, poAMinerSettings, asyncProvider)
 {
     this.collateralChecker = collateralChecker;
     this.encoder           = new CollateralHeightCommitmentEncoder();
 }
        public CheckCollateralFullValidationRule(IInitialBlockDownloadState ibdState, ICollateralChecker collateralChecker,
                                                 ISlotsManager slotsManager, IDateTimeProvider dateTime, Network network)
        {
            this.network           = network;
            this.encoder           = new CollateralHeightCommitmentEncoder();
            this.ibdState          = ibdState;
            this.collateralChecker = collateralChecker;
            this.slotsManager      = slotsManager;
            this.dateTime          = dateTime;

            this.collateralCheckBanDurationSeconds = (int)(this.network.Consensus.Options as PoAConsensusOptions).TargetSpacingSeconds / 2;
        }
예제 #4
0
        public override Task RunAsync(RuleContext context)
        {
            if (this.ibdState.IsInitialBlockDownload())
            {
                this.Logger.LogTrace("(-)[SKIPPED_IN_IBD]");
                return(Task.CompletedTask);
            }

            var commitmentHeightEncoder = new CollateralHeightCommitmentEncoder(this.Logger);
            int?commitmentHeight        = commitmentHeightEncoder.DecodeCommitmentHeight(context.ValidationContext.BlockToValidate.Transactions.First());

            if (commitmentHeight == null)
            {
                // We return here as it is CheckCollateralCommitmentHeightRule's responsibility to perform this check.
                this.Logger.LogTrace("(-)SKIPPED_AS_COLLATERAL_COMMITMENT_HEIGHT_MISSING]");
                return(Task.CompletedTask);
            }

            this.Logger.LogDebug("Commitment is: {0}.", commitmentHeight);

            // TODO: Both this and CollateralPoAMiner are using this chain's MaxReorg instead of the Counter chain's MaxReorg. Beware: fixing requires fork.

            int counterChainHeight = this.collateralChecker.GetCounterChainConsensusHeight();
            int maxReorgLength     = AddressIndexer.GetMaxReorgOrFallbackMaxReorg(this.network);

            // Check if commitment height is less than `mainchain consensus tip height - MaxReorg`.
            if (commitmentHeight > counterChainHeight - maxReorgLength)
            {
                // Temporary reject the block since it's possible that due to network connectivity problem counter chain is out of sync and
                // we are relying on chain state old data. It is possible that when we advance on counter chain commitment height will be
                // sufficiently old.
                context.ValidationContext.RejectUntil = this.dateTime.GetUtcNow() + TimeSpan.FromSeconds(this.collateralCheckBanDurationSeconds);

                this.Logger.LogDebug("commitmentHeight is {0}, counterChainHeight is {1}.", commitmentHeight, counterChainHeight);

                this.Logger.LogTrace("(-)[COMMITMENT_TOO_NEW]");
                PoAConsensusErrors.InvalidCollateralAmountCommitmentTooNew.Throw();
            }

            IFederationMember federationMember = this.slotsManager.GetFederationMemberForTimestamp(context.ValidationContext.BlockToValidate.Header.Time);

            if (!this.collateralChecker.CheckCollateral(federationMember, commitmentHeight.Value))
            {
                // By setting rejectUntil we avoid banning a peer that provided a block.
                context.ValidationContext.RejectUntil = this.dateTime.GetUtcNow() + TimeSpan.FromSeconds(this.collateralCheckBanDurationSeconds);

                this.Logger.LogTrace("(-)[BAD_COLLATERAL]");
                PoAConsensusErrors.InvalidCollateralAmount.Throw();
            }

            return(Task.CompletedTask);
        }
예제 #5
0
 public CollateralPoAMiner(IConsensusManager consensusManager, IDateTimeProvider dateTimeProvider, Network network, INodeLifetime nodeLifetime, ILoggerFactory loggerFactory,
                           IInitialBlockDownloadState ibdState, BlockDefinition blockDefinition, ISlotsManager slotsManager, IConnectionManager connectionManager, JoinFederationRequestMonitor joinFederationRequestMonitor,
                           PoABlockHeaderValidator poaHeaderValidator, IFederationManager federationManager, IIntegrityValidator integrityValidator, IWalletManager walletManager, ChainIndexer chainIndexer,
                           INodeStats nodeStats, VotingManager votingManager, PoAMinerSettings poAMinerSettings, ICollateralChecker collateralChecker, IAsyncProvider asyncProvider, ICounterChainSettings counterChainSettings, IIdleFederationMembersKicker idleFederationMembersKicker)
     : base(consensusManager, dateTimeProvider, network, nodeLifetime, loggerFactory, ibdState, blockDefinition, slotsManager, connectionManager,
            poaHeaderValidator, federationManager, integrityValidator, walletManager, nodeStats, votingManager, poAMinerSettings, asyncProvider, idleFederationMembersKicker)
 {
     this.counterChainNetwork          = counterChainSettings.CounterChainNetwork;
     this.collateralChecker            = collateralChecker;
     this.encoder                      = new CollateralHeightCommitmentEncoder(this.logger);
     this.chainIndexer                 = chainIndexer;
     this.joinFederationRequestMonitor = joinFederationRequestMonitor;
 }
        /// <inheritdoc />
        public override int?GetMultisigMinersApplicabilityHeight()
        {
            IConsensusManager consensusManager = this.fullNode.NodeService <IConsensusManager>();
            ChainedHeader     fork             = (this.lastBlockChecked == null) ? null : consensusManager.Tip.FindFork(this.lastBlockChecked);

            if (this.multisigMinersApplicabilityHeight != null && fork?.HashBlock == this.lastBlockChecked?.HashBlock)
            {
                return(this.multisigMinersApplicabilityHeight);
            }

            this.lastBlockChecked = fork;
            this.multisigMinersApplicabilityHeight = null;
            var commitmentHeightEncoder = new CollateralHeightCommitmentEncoder(this.logger);

            ChainedHeader[] headers = consensusManager.Tip.EnumerateToGenesis().TakeWhile(h => h != this.lastBlockChecked && h.Height >= this.network.CollateralCommitmentActivationHeight).Reverse().ToArray();

            ChainedHeader first = BinarySearch.BinaryFindFirst <ChainedHeader>(headers, (chainedHeader) =>
            {
                ChainedHeaderBlock block = consensusManager.GetBlockData(chainedHeader.HashBlock);
                if (block == null)
                {
                    return(null);
                }

                // Finding the height of the first STRAX collateral commitment height.
                (int?commitmentHeight, uint?magic) = commitmentHeightEncoder.DecodeCommitmentHeight(block.Block.Transactions.First());
                if (commitmentHeight == null)
                {
                    return(null);
                }

                return(magic == this.counterChainSettings.CounterChainNetwork.Magic);
            });

            this.lastBlockChecked = headers.LastOrDefault();
            this.multisigMinersApplicabilityHeight = first?.Height;

            this.UpdateMultisigMiners(first != null);

            return(this.multisigMinersApplicabilityHeight);
        }
        public override Task RunAsync(RuleContext context)
        {
            if (this.ibdState.IsInitialBlockDownload())
            {
                this.Logger.LogTrace("(-)[SKIPPED_IN_IBD]");
                return(Task.CompletedTask);
            }

            var commitmentHeightEncoder = new CollateralHeightCommitmentEncoder(this.Logger);

            (int?commitmentHeight, uint?commitmentNetworkMagic) = commitmentHeightEncoder.DecodeCommitmentHeight(context.ValidationContext.BlockToValidate.Transactions.First());
            if (commitmentHeight == null)
            {
                // We return here as it is CheckCollateralCommitmentHeightRule's responsibility to perform this check.
                this.Logger.LogTrace("(-)SKIPPED_AS_COLLATERAL_COMMITMENT_HEIGHT_MISSING]");
                return(Task.CompletedTask);
            }

            this.Logger.LogDebug("Commitment is: {0}. Magic is: {1}", commitmentHeight, commitmentNetworkMagic);

            // Strategy:
            // 1. I'm a Cirrus miner on STRAX. If the block's miner is also on STRAX then check the collateral. Pass or Fail as appropriate.
            // 2. The block miner is on STRAT. If most nodes were on STRAT(prev round) then they will check the rule. Pass the rule.
            // 3. The miner is on STRAT and most nodes were on STRAX(prev round).Fail the rule.

            // 1. If the block miner is on STRAX then skip this code and go check the collateral.
            Network counterChainNetwork = this.fullNode.NodeService <CounterChainNetworkWrapper>().CounterChainNetwork;

            if (this.network.Name.StartsWith("Cirrus") && commitmentNetworkMagic != counterChainNetwork.Magic)
            {
                // 2. The block miner is on STRAT.
                IConsensusManager consensusManager = this.fullNode.NodeService <IConsensusManager>();
                int  memberCount = 1;
                int  membersOnDifferentCounterChain = 1;
                uint minimumRoundLength             = this.slotsManager.GetRoundLengthSeconds() - ((PoAConsensusOptions)this.network.Consensus.Options).TargetSpacingSeconds / 2;

                // Check and any prior blocks in the same round.
                foreach (ChainedHeader chainedHeader in context.ValidationContext.ChainedHeaderToValidate.EnumerateToGenesis().Skip(1))
                {
                    Block block = chainedHeader?.Block ?? consensusManager.GetBlockData(chainedHeader.HashBlock).Block;
                    if (block == null || (block.Header.Time + minimumRoundLength) < context.ValidationContext.BlockToValidate.Header.Time)
                    {
                        break;
                    }

                    (int?commitmentHeight2, uint?magic2) = commitmentHeightEncoder.DecodeCommitmentHeight(block.Transactions.First());
                    if (commitmentHeight2 == null)
                    {
                        continue;
                    }

                    if (magic2 != counterChainNetwork.Magic)
                    {
                        membersOnDifferentCounterChain++;
                    }

                    memberCount++;
                }
                ;

                // If most nodes were on STRAT(prev round) then they will check the rule. Pass the rule.
                // This condition will execute if everyone is still on STRAT.
                if (membersOnDifferentCounterChain * 2 > memberCount)
                {
                    this.Logger.LogTrace("(-)SKIPPED_DURING_SWITCHOVER]");
                    return(Task.CompletedTask);
                }

                // 3. The miner is on STRAT and most nodes were on STRAX(prev round). Fail the rule.
                this.Logger.LogTrace("(-)[DISALLOW_STRAT_MINER]");
                PoAConsensusErrors.InvalidCollateralAmount.Throw();
            }

            // TODO: Both this and CollateralPoAMiner are using this chain's MaxReorg instead of the Counter chain's MaxReorg. Beware: fixing requires fork.
            int counterChainHeight = this.collateralChecker.GetCounterChainConsensusHeight();
            int maxReorgLength     = AddressIndexer.GetMaxReorgOrFallbackMaxReorg(this.network);

            // Check if commitment height is less than `mainchain consensus tip height - MaxReorg`.
            if (commitmentHeight > counterChainHeight - maxReorgLength)
            {
                // Temporary reject the block since it's possible that due to network connectivity problem counter chain is out of sync and
                // we are relying on chain state old data. It is possible that when we advance on counter chain commitment height will be
                // sufficiently old.
                context.ValidationContext.RejectUntil = this.dateTime.GetUtcNow() + TimeSpan.FromSeconds(this.collateralCheckBanDurationSeconds);

                this.Logger.LogDebug("commitmentHeight is {0}, counterChainHeight is {1}.", commitmentHeight, counterChainHeight);

                this.Logger.LogTrace("(-)[COMMITMENT_TOO_NEW]");
                PoAConsensusErrors.InvalidCollateralAmountCommitmentTooNew.Throw();
            }

            IFederationMember federationMember = this.slotsManager.GetFederationMemberForBlock(context.ValidationContext.ChainedHeaderToValidate, this.votingManager);

            if (!this.collateralChecker.CheckCollateral(federationMember, commitmentHeight.Value))
            {
                // By setting rejectUntil we avoid banning a peer that provided a block.
                context.ValidationContext.RejectUntil = this.dateTime.GetUtcNow() + TimeSpan.FromSeconds(this.collateralCheckBanDurationSeconds);

                this.Logger.LogTrace("(-)[BAD_COLLATERAL]");
                PoAConsensusErrors.InvalidCollateralAmount.Throw();
            }

            return(Task.CompletedTask);
        }