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 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); }
/// <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); }