Skip to content

Commit 7edbdec

Browse files
authored
Merge pull request #1073 from quantumagi/collateralheightchecks2
Add collateral height checks to miner and rules
2 parents 6c2ae61 + 5f4fccc commit 7edbdec

File tree

10 files changed

+169
-6
lines changed

10 files changed

+169
-6
lines changed

src/Stratis.Bitcoin.Features.PoA/PoAConsensusErrors.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,7 @@ public static class PoAConsensusErrors
3434
public static ConsensusError CollateralCommitmentHeightMissing => new ConsensusError("collateral-commitment-height-missing", "collateral commitment height missing");
3535

3636
public static ConsensusError InvalidCollateralAmountCommitmentTooNew => new ConsensusError("collateral-commitment-too-new", "collateral commitment too new");
37+
38+
public static ConsensusError InvalidCollateralAmountCommitmentTooOld => new ConsensusError("collateral-commitment-too-old", "collateral commitment too old");
3739
}
3840
}

src/Stratis.Features.Collateral/CheckCollateralCommitmentHeightRule.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System.Linq;
22
using System.Threading.Tasks;
33
using Microsoft.Extensions.Logging;
4+
using NBitcoin;
5+
using Stratis.Bitcoin.Base.Deployments;
46
using Stratis.Bitcoin.Consensus.Rules;
57
using Stratis.Bitcoin.Features.PoA;
68
using Stratis.Features.PoA.Collateral;
@@ -36,6 +38,29 @@ public override Task RunAsync(RuleContext context)
3638
PoAConsensusErrors.CollateralCommitmentHeightMissing.Throw();
3739
}
3840

41+
// Check that the commitment height is not less that of the prior block.
42+
int release1324ActivationHeight = 0;
43+
NodeDeployments nodeDeployments = this.Parent.NodeDeployments;
44+
if (nodeDeployments.BIP9.ArraySize > 0 /* Not NoBIP9Deployments */)
45+
release1324ActivationHeight = nodeDeployments.BIP9.ActivationHeightProviders[1 /* Release1324 */].ActivationHeight;
46+
47+
if (context.ValidationContext.ChainedHeaderToValidate.Height >= release1324ActivationHeight)
48+
{
49+
ChainedHeader prevHeader = context.ValidationContext.ChainedHeaderToValidate.Previous;
50+
if (prevHeader.BlockDataAvailability == BlockDataAvailabilityState.BlockAvailable)
51+
{
52+
if (prevHeader.Block != null)
53+
{
54+
(int? commitmentHeightPrev, _) = commitmentHeightEncoder.DecodeCommitmentHeight(prevHeader.Block.Transactions.First());
55+
if (commitmentHeight < commitmentHeightPrev)
56+
{
57+
this.Logger.LogTrace("(-)[COLLATERAL_COMMITMENT_TOO_OLD]");
58+
PoAConsensusErrors.InvalidCollateralAmountCommitmentTooOld.Throw();
59+
}
60+
}
61+
}
62+
}
63+
3964
return Task.CompletedTask;
4065
}
4166
}

src/Stratis.Features.Collateral/CollateralChecker.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ public interface ICollateralChecker : IDisposable
3333
/// <returns><c>True</c> if the collateral requirement is fulfilled and <c>false</c> otherwise.</returns>
3434
bool CheckCollateral(IFederationMember federationMember, int heightToCheckAt, int localChainHeight);
3535

36+
Task UpdateCollateralInfoAsync(CancellationToken cancellation);
37+
3638
int GetCounterChainConsensusHeight();
3739
}
3840

@@ -163,7 +165,7 @@ private async Task DelayCollateralCheckAsync()
163165
}
164166
}
165167

166-
private async Task UpdateCollateralInfoAsync(CancellationToken cancellation)
168+
public async Task UpdateCollateralInfoAsync(CancellationToken cancellation)
167169
{
168170
List<string> addressesToCheck;
169171

src/Stratis.Features.Collateral/CollateralPoAMiner.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections.Generic;
33
using System.Linq;
4+
using System.Threading;
45
using Microsoft.Extensions.Logging;
56
using NBitcoin;
67
using Stratis.Bitcoin.AsyncWork;
@@ -39,6 +40,8 @@ public class CollateralPoAMiner : PoAMiner
3940

4041
private readonly JoinFederationRequestMonitor joinFederationRequestMonitor;
4142

43+
private readonly CancellationTokenSource cancellationSource;
44+
4245
public CollateralPoAMiner(IConsensusManager consensusManager, IDateTimeProvider dateTimeProvider, Network network, INodeLifetime nodeLifetime, IInitialBlockDownloadState ibdState,
4346
BlockDefinition blockDefinition, ISlotsManager slotsManager, IConnectionManager connectionManager, JoinFederationRequestMonitor joinFederationRequestMonitor, PoABlockHeaderValidator poaHeaderValidator,
4447
IFederationManager federationManager, IFederationHistory federationHistory, IIntegrityValidator integrityValidator, IWalletManager walletManager, ChainIndexer chainIndexer, INodeStats nodeStats,
@@ -52,6 +55,7 @@ public CollateralPoAMiner(IConsensusManager consensusManager, IDateTimeProvider
5255
this.encoder = new CollateralHeightCommitmentEncoder();
5356
this.chainIndexer = chainIndexer;
5457
this.joinFederationRequestMonitor = joinFederationRequestMonitor;
58+
this.cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(nodeLifetime.ApplicationStopping);
5559
}
5660

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

83+
// Check that the commitment height is not less that of the prior block.
84+
ChainedHeaderBlock prevBlock = this.consensusManager.GetBlockData(blockTemplate.Block.Header.HashPrevBlock);
85+
(int? commitmentHeightPrev, _) = this.encoder.DecodeCommitmentHeight(prevBlock.Block.Transactions.First());
86+
// If the intended commitment height is less than the previous block's commitment height, update our local
87+
// counter chain height and try again.
88+
if (commitmentHeight < commitmentHeightPrev)
89+
{
90+
this.collateralChecker.UpdateCollateralInfoAsync(this.cancellationSource.Token).GetAwaiter().GetResult();
91+
counterChainHeight = this.collateralChecker.GetCounterChainConsensusHeight();
92+
commitmentHeight = counterChainHeight - maxReorgLength - AddressIndexer.SyncBuffer;
93+
94+
if (commitmentHeight < commitmentHeightPrev)
95+
{
96+
dropTemplate = true;
97+
this.logger.LogWarning("Block can't be produced, the counter chain should first advance at least {0} blocks. ", commitmentHeightPrev - commitmentHeight);
98+
this.logger.LogTrace("(-)[LOW_COMMITMENT_HEIGHT]");
99+
return;
100+
}
101+
}
102+
79103
IFederationMember currentMember = this.federationManager.GetCurrentFederationMember();
80104

81105
if (currentMember == null)
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Threading.Tasks;
4+
using Microsoft.Extensions.Logging;
5+
using Moq;
6+
using NBitcoin;
7+
using Stratis.Bitcoin.Base;
8+
using Stratis.Bitcoin.Base.Deployments;
9+
using Stratis.Bitcoin.Configuration;
10+
using Stratis.Bitcoin.Configuration.Logging;
11+
using Stratis.Bitcoin.Configuration.Settings;
12+
using Stratis.Bitcoin.Consensus;
13+
using Stratis.Bitcoin.Consensus.Rules;
14+
using Stratis.Bitcoin.Utilities;
15+
using Stratis.Features.Collateral;
16+
using Stratis.Features.PoA.Collateral;
17+
using Stratis.Sidechains.Networks;
18+
using Xunit;
19+
20+
namespace Stratis.Features.FederatedPeg.Tests
21+
{
22+
public class CheckCollateralCommitmentHeightRuleTests
23+
{
24+
private readonly CheckCollateralCommitmentHeightRule rule;
25+
private readonly RuleContext ruleContext;
26+
private readonly CollateralHeightCommitmentEncoder commitmentHeightEncoder;
27+
28+
private void EncodePreviousHeaderCommitmentHeight(int commitmentHeight)
29+
{
30+
// Setup previous block.
31+
var encodedHeight = this.commitmentHeightEncoder.EncodeCommitmentHeight(commitmentHeight);
32+
var commitmentHeightData = new Script(OpcodeType.OP_RETURN, Op.GetPushOp(encodedHeight));
33+
34+
Block prevBlock = this.ruleContext.ValidationContext.ChainedHeaderToValidate.Previous.Block;
35+
prevBlock.Transactions = new List<Transaction>();
36+
prevBlock.AddTransaction(new Transaction());
37+
prevBlock.Transactions[0].AddOutput(Money.Zero, commitmentHeightData);
38+
}
39+
40+
public CheckCollateralCommitmentHeightRuleTests()
41+
{
42+
this.ruleContext = new RuleContext(new ValidationContext(), DateTimeOffset.Now);
43+
var prevHeader = new BlockHeader { Time = 5200 };
44+
var prevChainedHeader = new ChainedHeader(prevHeader, prevHeader.GetHash(), int.MaxValue - 1);
45+
var prevBlock = new Block(prevHeader);
46+
prevChainedHeader.Block = prevBlock;
47+
prevChainedHeader.BlockDataAvailability = BlockDataAvailabilityState.BlockAvailable;
48+
var header = new BlockHeader() { Time = 5234, HashPrevBlock = prevHeader.GetHash() };
49+
this.ruleContext.ValidationContext.BlockToValidate = new Block(header);
50+
this.ruleContext.ValidationContext.ChainedHeaderToValidate = new ChainedHeader(header, header.GetHash(), prevChainedHeader);
51+
52+
Block block = this.ruleContext.ValidationContext.BlockToValidate;
53+
block.AddTransaction(new Transaction());
54+
55+
var loggerFactory = new ExtendedLoggerFactory();
56+
ILogger logger = loggerFactory.CreateLogger(this.GetType().FullName);
57+
58+
this.commitmentHeightEncoder = new CollateralHeightCommitmentEncoder();
59+
60+
// Setup block.
61+
byte[] encodedHeight = this.commitmentHeightEncoder.EncodeCommitmentHeight(1000);
62+
var commitmentHeightData = new Script(OpcodeType.OP_RETURN, Op.GetPushOp(encodedHeight));
63+
block.Transactions[0].AddOutput(Money.Zero, commitmentHeightData);
64+
65+
var network = new CirrusMain();
66+
var chainIndexer = new ChainIndexer(network);
67+
68+
var consensusRules = new Mock<ConsensusRuleEngine>(
69+
network,
70+
loggerFactory,
71+
new Mock<IDateTimeProvider>().Object,
72+
chainIndexer,
73+
new NodeDeployments(network, chainIndexer),
74+
new ConsensusSettings(new NodeSettings(network)),
75+
new Mock<ICheckpoints>().Object,
76+
new Mock<IChainState>().Object,
77+
new Mock<IInvalidBlockHashStore>().Object,
78+
new Mock<INodeStats>().Object,
79+
new ConsensusRulesContainer());
80+
81+
this.rule = new CheckCollateralCommitmentHeightRule()
82+
{
83+
Logger = logger,
84+
Parent = consensusRules.Object
85+
};
86+
87+
this.rule.Initialize();
88+
}
89+
90+
[Fact]
91+
public async Task PassesIfCollateralHeightsAreOrderedAsync()
92+
{
93+
EncodePreviousHeaderCommitmentHeight(999);
94+
await this.rule.RunAsync(this.ruleContext);
95+
}
96+
97+
[Fact]
98+
public async Task FailsIfCollateralHeightsAreDisorderedAsync()
99+
{
100+
EncodePreviousHeaderCommitmentHeight(1001);
101+
await Assert.ThrowsAsync<ConsensusErrorException>(() => this.rule.RunAsync(this.ruleContext));
102+
}
103+
}
104+
}

src/Stratis.Sidechains.Networks/CirrusDev.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,8 @@ public CirrusDev()
107107
var bip9Deployments = new CirrusBIP9Deployments()
108108
{
109109
// Deployment will go active once 75% of nodes are on 1.3.0.0 or later.
110-
[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)
110+
[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),
111+
[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)
111112
};
112113

113114
this.Consensus = new Consensus(

src/Stratis.Sidechains.Networks/CirrusMain.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,8 @@ public CirrusMain()
198198
var bip9Deployments = new CirrusBIP9Deployments()
199199
{
200200
// Deployment will go active once 75% of nodes are on 1.3.0.0 or later.
201-
[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 */)
201+
[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 */),
202+
[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 */)
202203
};
203204

204205
this.Consensus = new Consensus(

src/Stratis.Sidechains.Networks/CirrusRegTest.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,8 @@ public CirrusRegTest()
126126
var bip9Deployments = new CirrusBIP9Deployments()
127127
{
128128
// Deployment will go active once 75% of nodes are on 1.3.0.0 or later.
129-
[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)
129+
[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),
130+
[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)
130131
};
131132

132133
this.Consensus = new Consensus(

src/Stratis.Sidechains.Networks/CirrusTest.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,8 @@ public CirrusTest()
149149
var bip9Deployments = new CirrusBIP9Deployments()
150150
{
151151
// Deployment will go active once 75% of nodes are on 1.3.0.0 or later.
152-
[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)
152+
[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),
153+
[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)
153154
};
154155

155156
this.Consensus = new Consensus(

src/Stratis.Sidechains.Networks/Deployments/CirrusBIP9Deployments.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,13 @@ public class CirrusBIP9Deployments : BIP9DeploymentsArray
99
{
1010
// 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.
1111
public const int Release1320 = 0;
12+
public const int Release1324 = 1;
1213

1314
public const int FlagBitRelease1320 = 1;
15+
public const int FlagBitRelease1324 = 2;
1416

1517
// The number of deployments.
16-
public const int NumberOfDeployments = 1;
18+
public const int NumberOfDeployments = 2;
1719

1820
/// <summary>
1921
/// Constructs the BIP9 deployments array.

0 commit comments

Comments
 (0)