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);
        }
コード例 #2
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);
        }
コード例 #3
0
        /// <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);
        }