Skip to content

Commit

Permalink
Merge pull request #1073 from quantumagi/collateralheightchecks2
Browse files Browse the repository at this point in the history
Add collateral height checks to miner and rules
  • Loading branch information
fassadlr committed Oct 12, 2022
2 parents 6c2ae61 + 5f4fccc commit 7edbdec
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 6 deletions.
2 changes: 2 additions & 0 deletions src/Stratis.Bitcoin.Features.PoA/PoAConsensusErrors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,7 @@ public static class PoAConsensusErrors
public static ConsensusError CollateralCommitmentHeightMissing => new ConsensusError("collateral-commitment-height-missing", "collateral commitment height missing");

public static ConsensusError InvalidCollateralAmountCommitmentTooNew => new ConsensusError("collateral-commitment-too-new", "collateral commitment too new");

public static ConsensusError InvalidCollateralAmountCommitmentTooOld => new ConsensusError("collateral-commitment-too-old", "collateral commitment too old");
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.Base.Deployments;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Features.PoA;
using Stratis.Features.PoA.Collateral;
Expand Down Expand Up @@ -36,6 +38,29 @@ public override Task RunAsync(RuleContext context)
PoAConsensusErrors.CollateralCommitmentHeightMissing.Throw();
}

// Check that the commitment height is not less that of the prior block.
int release1324ActivationHeight = 0;
NodeDeployments nodeDeployments = this.Parent.NodeDeployments;
if (nodeDeployments.BIP9.ArraySize > 0 /* Not NoBIP9Deployments */)
release1324ActivationHeight = nodeDeployments.BIP9.ActivationHeightProviders[1 /* Release1324 */].ActivationHeight;

if (context.ValidationContext.ChainedHeaderToValidate.Height >= release1324ActivationHeight)
{
ChainedHeader prevHeader = context.ValidationContext.ChainedHeaderToValidate.Previous;
if (prevHeader.BlockDataAvailability == BlockDataAvailabilityState.BlockAvailable)
{
if (prevHeader.Block != null)
{
(int? commitmentHeightPrev, _) = commitmentHeightEncoder.DecodeCommitmentHeight(prevHeader.Block.Transactions.First());
if (commitmentHeight < commitmentHeightPrev)
{
this.Logger.LogTrace("(-)[COLLATERAL_COMMITMENT_TOO_OLD]");
PoAConsensusErrors.InvalidCollateralAmountCommitmentTooOld.Throw();
}
}
}
}

return Task.CompletedTask;
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/Stratis.Features.Collateral/CollateralChecker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public interface ICollateralChecker : IDisposable
/// <returns><c>True</c> if the collateral requirement is fulfilled and <c>false</c> otherwise.</returns>
bool CheckCollateral(IFederationMember federationMember, int heightToCheckAt, int localChainHeight);

Task UpdateCollateralInfoAsync(CancellationToken cancellation);

int GetCounterChainConsensusHeight();
}

Expand Down Expand Up @@ -163,7 +165,7 @@ private async Task DelayCollateralCheckAsync()
}
}

private async Task UpdateCollateralInfoAsync(CancellationToken cancellation)
public async Task UpdateCollateralInfoAsync(CancellationToken cancellation)
{
List<string> addressesToCheck;

Expand Down
24 changes: 24 additions & 0 deletions src/Stratis.Features.Collateral/CollateralPoAMiner.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Microsoft.Extensions.Logging;
using NBitcoin;
using Stratis.Bitcoin.AsyncWork;
Expand Down Expand Up @@ -39,6 +40,8 @@ public class CollateralPoAMiner : PoAMiner

private readonly JoinFederationRequestMonitor joinFederationRequestMonitor;

private readonly CancellationTokenSource cancellationSource;

public CollateralPoAMiner(IConsensusManager consensusManager, IDateTimeProvider dateTimeProvider, Network network, INodeLifetime nodeLifetime, IInitialBlockDownloadState ibdState,
BlockDefinition blockDefinition, ISlotsManager slotsManager, IConnectionManager connectionManager, JoinFederationRequestMonitor joinFederationRequestMonitor, PoABlockHeaderValidator poaHeaderValidator,
IFederationManager federationManager, IFederationHistory federationHistory, IIntegrityValidator integrityValidator, IWalletManager walletManager, ChainIndexer chainIndexer, INodeStats nodeStats,
Expand All @@ -52,6 +55,7 @@ public CollateralPoAMiner(IConsensusManager consensusManager, IDateTimeProvider
this.encoder = new CollateralHeightCommitmentEncoder();
this.chainIndexer = chainIndexer;
this.joinFederationRequestMonitor = joinFederationRequestMonitor;
this.cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(nodeLifetime.ApplicationStopping);
}

/// <inheritdoc />
Expand All @@ -76,6 +80,26 @@ protected override void FillBlockTemplate(BlockTemplate blockTemplate, out bool
return;
}

// Check that the commitment height is not less that of the prior block.
ChainedHeaderBlock prevBlock = this.consensusManager.GetBlockData(blockTemplate.Block.Header.HashPrevBlock);
(int? commitmentHeightPrev, _) = this.encoder.DecodeCommitmentHeight(prevBlock.Block.Transactions.First());
// If the intended commitment height is less than the previous block's commitment height, update our local
// counter chain height and try again.
if (commitmentHeight < commitmentHeightPrev)
{
this.collateralChecker.UpdateCollateralInfoAsync(this.cancellationSource.Token).GetAwaiter().GetResult();
counterChainHeight = this.collateralChecker.GetCounterChainConsensusHeight();
commitmentHeight = counterChainHeight - maxReorgLength - AddressIndexer.SyncBuffer;

if (commitmentHeight < commitmentHeightPrev)
{
dropTemplate = true;
this.logger.LogWarning("Block can't be produced, the counter chain should first advance at least {0} blocks. ", commitmentHeightPrev - commitmentHeight);
this.logger.LogTrace("(-)[LOW_COMMITMENT_HEIGHT]");
return;
}
}

IFederationMember currentMember = this.federationManager.GetCurrentFederationMember();

if (currentMember == null)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Moq;
using NBitcoin;
using Stratis.Bitcoin.Base;
using Stratis.Bitcoin.Base.Deployments;
using Stratis.Bitcoin.Configuration;
using Stratis.Bitcoin.Configuration.Logging;
using Stratis.Bitcoin.Configuration.Settings;
using Stratis.Bitcoin.Consensus;
using Stratis.Bitcoin.Consensus.Rules;
using Stratis.Bitcoin.Utilities;
using Stratis.Features.Collateral;
using Stratis.Features.PoA.Collateral;
using Stratis.Sidechains.Networks;
using Xunit;

namespace Stratis.Features.FederatedPeg.Tests
{
public class CheckCollateralCommitmentHeightRuleTests
{
private readonly CheckCollateralCommitmentHeightRule rule;
private readonly RuleContext ruleContext;
private readonly CollateralHeightCommitmentEncoder commitmentHeightEncoder;

private void EncodePreviousHeaderCommitmentHeight(int commitmentHeight)
{
// Setup previous block.
var encodedHeight = this.commitmentHeightEncoder.EncodeCommitmentHeight(commitmentHeight);
var commitmentHeightData = new Script(OpcodeType.OP_RETURN, Op.GetPushOp(encodedHeight));

Block prevBlock = this.ruleContext.ValidationContext.ChainedHeaderToValidate.Previous.Block;
prevBlock.Transactions = new List<Transaction>();
prevBlock.AddTransaction(new Transaction());
prevBlock.Transactions[0].AddOutput(Money.Zero, commitmentHeightData);
}

public CheckCollateralCommitmentHeightRuleTests()
{
this.ruleContext = new RuleContext(new ValidationContext(), DateTimeOffset.Now);
var prevHeader = new BlockHeader { Time = 5200 };
var prevChainedHeader = new ChainedHeader(prevHeader, prevHeader.GetHash(), int.MaxValue - 1);
var prevBlock = new Block(prevHeader);
prevChainedHeader.Block = prevBlock;
prevChainedHeader.BlockDataAvailability = BlockDataAvailabilityState.BlockAvailable;
var header = new BlockHeader() { Time = 5234, HashPrevBlock = prevHeader.GetHash() };
this.ruleContext.ValidationContext.BlockToValidate = new Block(header);
this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(header, header.GetHash(), prevChainedHeader);

Block block = this.ruleContext.ValidationContext.BlockToValidate;
block.AddTransaction(new Transaction());

var loggerFactory = new ExtendedLoggerFactory();
ILogger logger = loggerFactory.CreateLogger(this.GetType().FullName);

this.commitmentHeightEncoder = new CollateralHeightCommitmentEncoder();

// Setup block.
byte[] encodedHeight = this.commitmentHeightEncoder.EncodeCommitmentHeight(1000);
var commitmentHeightData = new Script(OpcodeType.OP_RETURN, Op.GetPushOp(encodedHeight));
block.Transactions[0].AddOutput(Money.Zero, commitmentHeightData);

var network = new CirrusMain();
var chainIndexer = new ChainIndexer(network);

var consensusRules = new Mock<ConsensusRuleEngine>(
network,
loggerFactory,
new Mock<IDateTimeProvider>().Object,
chainIndexer,
new NodeDeployments(network, chainIndexer),
new ConsensusSettings(new NodeSettings(network)),
new Mock<ICheckpoints>().Object,
new Mock<IChainState>().Object,
new Mock<IInvalidBlockHashStore>().Object,
new Mock<INodeStats>().Object,
new ConsensusRulesContainer());

this.rule = new CheckCollateralCommitmentHeightRule()
{
Logger = logger,
Parent = consensusRules.Object
};

this.rule.Initialize();
}

[Fact]
public async Task PassesIfCollateralHeightsAreOrderedAsync()
{
EncodePreviousHeaderCommitmentHeight(999);
await this.rule.RunAsync(this.ruleContext);
}

[Fact]
public async Task FailsIfCollateralHeightsAreDisorderedAsync()
{
EncodePreviousHeaderCommitmentHeight(1001);
await Assert.ThrowsAsync<ConsensusErrorException>(() => this.rule.RunAsync(this.ruleContext));
}
}
}
3 changes: 2 additions & 1 deletion src/Stratis.Sidechains.Networks/CirrusDev.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ public CirrusDev()
var bip9Deployments = new CirrusBIP9Deployments()
{
// Deployment will go active once 75% of nodes are on 1.3.0.0 or later.
[CirrusBIP9Deployments.Release1320] = new BIP9DeploymentsParameters("Release1320", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-6-15 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Now.AddDays(50).ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold)
[CirrusBIP9Deployments.Release1320] = new BIP9DeploymentsParameters("Release1320", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-6-15 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Now.AddDays(50).ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold),
[CirrusBIP9Deployments.Release1324] = new BIP9DeploymentsParameters("Release1324", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-10-10 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Now.AddDays(50).ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold)
};

this.Consensus = new Consensus(
Expand Down
3 changes: 2 additions & 1 deletion src/Stratis.Sidechains.Networks/CirrusMain.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@ public CirrusMain()
var bip9Deployments = new CirrusBIP9Deployments()
{
// Deployment will go active once 75% of nodes are on 1.3.0.0 or later.
[CirrusBIP9Deployments.Release1320] = new BIP9DeploymentsParameters("Release1320", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-6-15 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Parse("2023-1-1 +0").ToUnixTimestamp(), 8100 /* 75% Activation Threshold */)
[CirrusBIP9Deployments.Release1320] = new BIP9DeploymentsParameters("Release1320", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-6-15 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Parse("2023-1-1 +0").ToUnixTimestamp(), 8100 /* 75% Activation Threshold */),
[CirrusBIP9Deployments.Release1324] = new BIP9DeploymentsParameters("Release1324", CirrusBIP9Deployments.FlagBitRelease1324, DateTime.Parse("2022-10-10 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Parse("2023-3-1 +0").ToUnixTimestamp(), 8100 /* 75% Activation Threshold */)
};

this.Consensus = new Consensus(
Expand Down
3 changes: 2 additions & 1 deletion src/Stratis.Sidechains.Networks/CirrusRegTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,8 @@ public CirrusRegTest()
var bip9Deployments = new CirrusBIP9Deployments()
{
// Deployment will go active once 75% of nodes are on 1.3.0.0 or later.
[CirrusBIP9Deployments.Release1320] = new BIP9DeploymentsParameters("Release1320", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-6-15 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Now.AddDays(50).ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold)
[CirrusBIP9Deployments.Release1320] = new BIP9DeploymentsParameters("Release1320", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-6-15 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Now.AddDays(50).ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold),
[CirrusBIP9Deployments.Release1324] = new BIP9DeploymentsParameters("Release1324", CirrusBIP9Deployments.FlagBitRelease1324, DateTime.Parse("2022-10-10 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Now.AddDays(50).ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold)
};

this.Consensus = new Consensus(
Expand Down
3 changes: 2 additions & 1 deletion src/Stratis.Sidechains.Networks/CirrusTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,8 @@ public CirrusTest()
var bip9Deployments = new CirrusBIP9Deployments()
{
// Deployment will go active once 75% of nodes are on 1.3.0.0 or later.
[CirrusBIP9Deployments.Release1320] = new BIP9DeploymentsParameters("Release1320", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-6-15 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Parse("2023-1-1 +0").ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold)
[CirrusBIP9Deployments.Release1320] = new BIP9DeploymentsParameters("Release1320", CirrusBIP9Deployments.FlagBitRelease1320, DateTime.Parse("2022-6-15 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Parse("2023-1-1 +0").ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold),
[CirrusBIP9Deployments.Release1324] = new BIP9DeploymentsParameters("Release1324", CirrusBIP9Deployments.FlagBitRelease1324, DateTime.Parse("2022-10-10 +0").ToUnixTimestamp() /* Activation date lower bound */, DateTime.Parse("2023-3-1 +0").ToUnixTimestamp(), BIP9DeploymentsParameters.DefaultRegTestThreshold)
};

this.Consensus = new Consensus(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@ public class CirrusBIP9Deployments : BIP9DeploymentsArray
{
// The position of each deployment in the deployments array. Note that this is decoupled from the actual position of the flag bit for the deployment in the block version.
public const int Release1320 = 0;
public const int Release1324 = 1;

public const int FlagBitRelease1320 = 1;
public const int FlagBitRelease1324 = 2;

// The number of deployments.
public const int NumberOfDeployments = 1;
public const int NumberOfDeployments = 2;

/// <summary>
/// Constructs the BIP9 deployments array.
Expand Down

0 comments on commit 7edbdec

Please sign in to comment.