From 8ff480c067cea9d650c0f2c8605d64bd8f360c1e Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 20:01:53 +1000 Subject: [PATCH 01/11] Access Cirrus cross-chain feature activations from the main chain --- .../ConsensusController.cs | 8 ++- .../Models/ThresholdActivationModel.cs | 12 ++++ .../Deployments/ThresholdConditionCache.cs | 2 + .../FederationGatewayControllerTests.cs | 6 +- .../DepositExtractorTests.cs | 2 +- .../MaturedBlocksProviderTests.cs | 2 +- .../SourceChain/RetrievalTypeConfirmations.cs | 23 +++++- .../TargetChain/MaturedBlocksSyncManager.cs | 70 ++++++++++++++++++- .../Properties/launchSettings.json | 3 +- 9 files changed, 118 insertions(+), 10 deletions(-) diff --git a/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs b/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs index 22ddb17ed9..90891dbe88 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs @@ -109,7 +109,13 @@ public IActionResult LockedInDeployments() List metrics = ruleEngine.NodeDeployments.BIP9.GetThresholdStateMetrics(this.ChainState.ConsensusTip.Previous, thresholdStates, activationHeights); - return this.Json(metrics.Select(m => new ThresholdActivationModel() { activationHeight = m.SinceHeight, DeploymentIndex = m.DeploymentIndex, DeploymentName = m.DeploymentName, Votes = m.Votes }).ToArray()); + return this.Json(metrics.Select(m => new ThresholdActivationModel() { + activationHeight = m.SinceHeight, + DeploymentIndex = m.DeploymentIndex, + DeploymentName = m.DeploymentName, + Votes = m.Votes, + lockedInHeight = m.SinceHeight - ruleEngine.Network.Consensus.MinerConfirmationWindow, + lockedInTimestamp = this.ChainIndexer[m.SinceHeight - ruleEngine.Network.Consensus.MinerConfirmationWindow].Header.Time}).ToArray()); } catch (Exception e) { diff --git a/src/Stratis.Bitcoin/Base/Deployments/Models/ThresholdActivationModel.cs b/src/Stratis.Bitcoin/Base/Deployments/Models/ThresholdActivationModel.cs index f36cb13707..e08f3df347 100644 --- a/src/Stratis.Bitcoin/Base/Deployments/Models/ThresholdActivationModel.cs +++ b/src/Stratis.Bitcoin/Base/Deployments/Models/ThresholdActivationModel.cs @@ -30,5 +30,17 @@ public class ThresholdActivationModel /// [JsonProperty(PropertyName = "votes")] public int Votes { get; set; } + + /// + /// The height at which the deployment was locked-in. + /// + [JsonProperty(PropertyName = "lockedInHeight")] + public int? lockedInHeight { get; set; } + + /// + /// The timestamp of the blocked at the "lockedInHeight". + /// + [JsonProperty(PropertyName = "lockedInTimestamp")] + public long? lockedInTimestamp { get; set; } } } \ No newline at end of file diff --git a/src/Stratis.Bitcoin/Base/Deployments/ThresholdConditionCache.cs b/src/Stratis.Bitcoin/Base/Deployments/ThresholdConditionCache.cs index 2d6a32ec37..ea57273173 100644 --- a/src/Stratis.Bitcoin/Base/Deployments/ThresholdConditionCache.cs +++ b/src/Stratis.Bitcoin/Base/Deployments/ThresholdConditionCache.cs @@ -103,6 +103,8 @@ public List GetThresholdStateMetrics(ChainedHeader indexPre // Choose the last header that's within the window where voting led to locked-in state. sinceHeight = activationHeights[deploymentIndex]; indexPrev = referenceHeader.GetAncestor(sinceHeight - period - 1); + if (indexPrev == null) + continue; } else { diff --git a/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs b/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs index e016863eec..744ab85356 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs @@ -74,7 +74,7 @@ public FederationGatewayControllerTests() private FederationGatewayController CreateController(IFederatedPegSettings federatedPegSettings) { - var retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), federatedPegSettings); + var retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), federatedPegSettings, null, null); var controller = new FederationGatewayController( Substitute.For(), @@ -210,7 +210,7 @@ public void Call_Sidechain_Gateway_Get_Info() var federatedPegSettings = new FederatedPegSettings(nodeSettings, new CounterChainNetworkWrapper(KnownNetworks.StraxRegTest)); - var retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), federatedPegSettings); + var retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), federatedPegSettings, null, null); var controller = new FederationGatewayController( Substitute.For(), @@ -304,7 +304,7 @@ public void Call_Mainchain_Gateway_Get_Info() this.federationWalletManager.IsFederationWalletActive().Returns(true); var settings = new FederatedPegSettings(nodeSettings, new CounterChainNetworkWrapper(KnownNetworks.StraxRegTest)); - var retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), settings); + var retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), settings, null, null); var controller = new FederationGatewayController( Substitute.For(), diff --git a/src/Stratis.Features.FederatedPeg.Tests/DepositExtractorTests.cs b/src/Stratis.Features.FederatedPeg.Tests/DepositExtractorTests.cs index 72f607382b..81d8a79436 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/DepositExtractorTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/DepositExtractorTests.cs @@ -55,7 +55,7 @@ public DepositExtractorTests() this.depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federationSettings, this.network, this.opReturnDataReader); this.transactionBuilder = new TestTransactionBuilder(); - this.retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), this.federationSettings); + this.retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), this.federationSettings, null, null); } // Normal Deposits diff --git a/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs b/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs index b71fbf6c8d..dca776969e 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs @@ -80,7 +80,7 @@ public MaturedBlocksProviderTests() return this.blocks.SkipWhile(x => x.ChainedHeader.Height <= chainedHeader.Height).Where(x => x.ChainedHeader.Height <= this.consensusManager.Tip.Height).ToArray(); }); - this.retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), this.federatedPegSettings); + this.retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), this.federatedPegSettings, null, null); } [Fact] diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index be84381cfe..fea61e2933 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -3,6 +3,8 @@ using NBitcoin; using Stratis.Bitcoin.Base.Deployments; using Stratis.Features.FederatedPeg.Interfaces; +using Stratis.Features.FederatedPeg.TargetChain; +using Stratis.Features.PoA.Collateral.CounterChain; namespace Stratis.Features.FederatedPeg.SourceChain { @@ -35,11 +37,15 @@ public class RetrievalTypeConfirmations : IRetrievalTypeConfirmations private readonly Dictionary legacyRetrievalTypeConfirmations; private readonly Dictionary retrievalTypeConfirmations; private readonly Network network; + private readonly IMaturedBlocksSyncManager maturedBlocksSyncManager; + private readonly ICounterChainSettings counterChainSettings; - public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeployments, IFederatedPegSettings federatedPegSettings) + public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeployments, IFederatedPegSettings federatedPegSettings, IMaturedBlocksSyncManager maturedBlocksSyncManager, ICounterChainSettings counterChainSettings) { this.nodeDeployments = nodeDeployments; this.network = network; + this.maturedBlocksSyncManager = maturedBlocksSyncManager; + this.counterChainSettings = counterChainSettings; this.legacyRetrievalTypeConfirmations = new Dictionary { [DepositRetrievalType.Small] = federatedPegSettings.MinimumConfirmationsSmallDeposits, @@ -99,7 +105,20 @@ public int MaximumConfirmationsAtMaturityHeight(int maturityHeight) return this.retrievalTypeConfirmations.Values.Max(); } - private int Release1300ActivationHeight => (this.nodeDeployments?.BIP9.ArraySize > 0) ? this.nodeDeployments.BIP9.ActivationHeightProviders[0 /* Release 1300 */].ActivationHeight : 0; + private int Release1300ActivationHeight + { + get + { + if (this.network.Name.StartsWith("Cirrus")) + return (this.nodeDeployments?.BIP9.ArraySize > 0) ? this.nodeDeployments.BIP9.ActivationHeightProviders[0 /* Release 1300 */].ActivationHeight : 0; + + // This code is running on the main chain. + if (this.counterChainSettings.CounterChainNetwork.Consensus.BIP9Deployments.Length == 0) + return 0; + + return this.maturedBlocksSyncManager.GetMainChainActivationHeight(); + } + } public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retrievalType) { diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 96811693d8..da0d23794c 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -1,11 +1,15 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http; +using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NBitcoin; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Base.Deployments.Models; using Stratis.Bitcoin.Configuration.Logging; +using Stratis.Bitcoin.Controllers; using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Features.PoA; using Stratis.Bitcoin.Interfaces; @@ -17,6 +21,7 @@ using Stratis.Features.FederatedPeg.Models; using Stratis.Features.FederatedPeg.SourceChain; using Stratis.Features.FederatedPeg.Wallet; +using Stratis.Features.PoA.Collateral.CounterChain; namespace Stratis.Features.FederatedPeg.TargetChain { @@ -38,6 +43,8 @@ public interface IMaturedBlocksSyncManager : IDisposable /// The deposit that will be injected into the that distributes a fee to all the multisig nodes /// for submitting a interop transfer. This is currently used for SRC20 to ERC20 transfers. void AddInterOpFeeDeposit(IDeposit deposit); + + int GetMainChainActivationHeight(); } /// @@ -53,11 +60,14 @@ public class MaturedBlocksSyncManager : IMaturedBlocksSyncManager private readonly ILogger logger; private readonly INodeLifetime nodeLifetime; private readonly IConversionRequestRepository conversionRequestRepository; + private readonly ICounterChainSettings counterChainSettings; + private readonly IHttpClientFactory httpClientFactory; private readonly ChainIndexer chainIndexer; private readonly IExternalApiPoller externalApiPoller; private readonly IConversionRequestFeeService conversionRequestFeeService; private readonly Network network; private readonly IFederationManager federationManager; + private int mainChainActivationHeight; private IAsyncLoop requestDepositsTask; /// When we are fully synced we stop asking for more blocks for this amount of time. @@ -85,7 +95,9 @@ public MaturedBlocksSyncManager( IFederatedPegSettings federatedPegSettings, IFederationManager federationManager = null, IExternalApiPoller externalApiPoller = null, - IConversionRequestFeeService conversionRequestFeeService = null) + IConversionRequestFeeService conversionRequestFeeService = null, + ICounterChainSettings counterChainSettings = null, + IHttpClientFactory httpClientFactory = null) { this.asyncProvider = asyncProvider; this.chainIndexer = chainIndexer; @@ -97,24 +109,80 @@ public MaturedBlocksSyncManager( this.initialBlockDownloadState = initialBlockDownloadState; this.nodeLifetime = nodeLifetime; this.conversionRequestRepository = conversionRequestRepository; + this.counterChainSettings = counterChainSettings; + this.httpClientFactory = httpClientFactory; this.chainIndexer = chainIndexer; this.externalApiPoller = externalApiPoller; this.conversionRequestFeeService = conversionRequestFeeService; this.network = network; this.federationManager = federationManager; + this.mainChainActivationHeight = int.MaxValue; this.lockObject = new object(); this.logger = LogManager.GetCurrentClassLogger(); } + public class ConsensusClient : RestApiClientBase + { + /// + /// Currently the is required as it needs to be configurable for testing. + /// + /// In a production/live scenario the sidechain and mainnet federation nodes should run on the same machine. + /// + /// + public ConsensusClient(ICounterChainSettings counterChainSettings, IHttpClientFactory httpClientFactory) + : base(httpClientFactory, counterChainSettings.CounterChainApiPort, "Consensus", $"http://{counterChainSettings.CounterChainApiHost}") + { + } + + public Task> GetLockedInDeployments(CancellationToken cancellation = default) + { + return this.SendGetRequestAsync>("lockedindeployments", null, cancellation); + } + } + + public void RecordCounterChainActivations() + { + // If this is the main chain then ask the side-chain for its activation height. + if (this.network.Name.StartsWith("Cirrus")) + return; + + ConsensusClient consensusClient = new ConsensusClient(this.counterChainSettings, this.httpClientFactory); + List lockedInActivations = consensusClient.GetLockedInDeployments(this.nodeLifetime.ApplicationStopping).ConfigureAwait(false).GetAwaiter().GetResult(); + if (lockedInActivations == null || lockedInActivations.Count == 0) + return; + + ThresholdActivationModel model = lockedInActivations.FirstOrDefault(a => a.DeploymentName.ToLower() == "release1300"); + if (model == null || model.lockedInTimestamp == null) + return; + + if (this.chainIndexer.Tip.Header.Time < model.lockedInTimestamp.Value) + return; + + int mainChainLockedInHeight = this.chainIndexer.Tip.EnumerateToGenesis().TakeWhile(h => h.Header.Time > (uint)(model.lockedInTimestamp)).FirstOrDefault().Height; + + Network counterChainNetwork = this.counterChainSettings.CounterChainNetwork; + this.mainChainActivationHeight = mainChainLockedInHeight + + (int)((counterChainNetwork.Consensus.MinerConfirmationWindow * counterChainNetwork.Consensus.TargetSpacing.TotalSeconds) / this.network.Consensus.TargetSpacing.TotalSeconds); + } + + public int GetMainChainActivationHeight() + { + return this.mainChainActivationHeight; + } + /// public async Task StartAsync() { // Initialization delay; give the counter chain node some time to start it's API service. await Task.Delay(TimeSpan.FromSeconds(InitializationDelaySeconds), this.nodeLifetime.ApplicationStopping).ConfigureAwait(false); + RecordCounterChainActivations(); + this.requestDepositsTask = this.asyncProvider.CreateAndRunAsyncLoop($"{nameof(MaturedBlocksSyncManager)}.{nameof(this.requestDepositsTask)}", async token => { + RecordCounterChainActivations(); + bool delayRequired = await this.SyncDepositsAsync().ConfigureAwait(false); if (delayRequired) { diff --git a/src/Stratis.StraxD/Properties/launchSettings.json b/src/Stratis.StraxD/Properties/launchSettings.json index b58a98efb9..5789d2e9bf 100644 --- a/src/Stratis.StraxD/Properties/launchSettings.json +++ b/src/Stratis.StraxD/Properties/launchSettings.json @@ -1,7 +1,8 @@ { "profiles": { "Stratis.StraxD": { - "commandName": "Project" + "commandName": "Project", + "commandLineArgs": "-addressindex" }, "Stratis.StraxD Test": { "commandName": "Project", From 075d3ac8ef86fc4adc55cb9e20be9bf22a6db8e3 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 20:07:25 +1000 Subject: [PATCH 02/11] Revert unintentional change --- src/Stratis.StraxD/Properties/launchSettings.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Stratis.StraxD/Properties/launchSettings.json b/src/Stratis.StraxD/Properties/launchSettings.json index 5789d2e9bf..b58a98efb9 100644 --- a/src/Stratis.StraxD/Properties/launchSettings.json +++ b/src/Stratis.StraxD/Properties/launchSettings.json @@ -1,8 +1,7 @@ { "profiles": { "Stratis.StraxD": { - "commandName": "Project", - "commandLineArgs": "-addressindex" + "commandName": "Project" }, "Stratis.StraxD Test": { "commandName": "Project", From 4b3d550c38b12c1e546ba6f52ac46b8e5c155166 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 20:17:36 +1000 Subject: [PATCH 03/11] Optimize --- .../TargetChain/MaturedBlocksSyncManager.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index da0d23794c..2a2309e901 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -147,6 +147,9 @@ public void RecordCounterChainActivations() if (this.network.Name.StartsWith("Cirrus")) return; + if (this.mainChainActivationHeight != int.MaxValue) + return; + ConsensusClient consensusClient = new ConsensusClient(this.counterChainSettings, this.httpClientFactory); List lockedInActivations = consensusClient.GetLockedInDeployments(this.nodeLifetime.ApplicationStopping).ConfigureAwait(false).GetAwaiter().GetResult(); if (lockedInActivations == null || lockedInActivations.Count == 0) From 46eba212b69c9eb825a7d9ba1a8f1860ad916de8 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 21:28:51 +1000 Subject: [PATCH 04/11] Update src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs Co-authored-by: Francois de la Rouviere --- .../TargetChain/MaturedBlocksSyncManager.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 2a2309e901..cbb1ccf7e4 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -147,6 +147,7 @@ public void RecordCounterChainActivations() if (this.network.Name.StartsWith("Cirrus")) return; + // Ensures that we only check this once on startup. if (this.mainChainActivationHeight != int.MaxValue) return; From 94a8da36c0955cf2c1515027a6b721981139ced1 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 21:46:17 +1000 Subject: [PATCH 05/11] Changes based on feedback --- .../ConsensusController.cs | 6 +++--- .../Base/Deployments/Models/ThresholdActivationModel.cs | 6 +++--- .../TargetChain/MaturedBlocksSyncManager.cs | 9 +++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs b/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs index 90891dbe88..91262e43c9 100644 --- a/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs +++ b/src/Stratis.Bitcoin.Features.Consensus/ConsensusController.cs @@ -110,12 +110,12 @@ public IActionResult LockedInDeployments() List metrics = ruleEngine.NodeDeployments.BIP9.GetThresholdStateMetrics(this.ChainState.ConsensusTip.Previous, thresholdStates, activationHeights); return this.Json(metrics.Select(m => new ThresholdActivationModel() { - activationHeight = m.SinceHeight, + ActivationHeight = m.SinceHeight, DeploymentIndex = m.DeploymentIndex, DeploymentName = m.DeploymentName, Votes = m.Votes, - lockedInHeight = m.SinceHeight - ruleEngine.Network.Consensus.MinerConfirmationWindow, - lockedInTimestamp = this.ChainIndexer[m.SinceHeight - ruleEngine.Network.Consensus.MinerConfirmationWindow].Header.Time}).ToArray()); + LockedInHeight = m.SinceHeight - ruleEngine.Network.Consensus.MinerConfirmationWindow, + LockedInTimestamp = this.ChainIndexer[m.SinceHeight - ruleEngine.Network.Consensus.MinerConfirmationWindow].Header.Time}).ToArray()); } catch (Exception e) { diff --git a/src/Stratis.Bitcoin/Base/Deployments/Models/ThresholdActivationModel.cs b/src/Stratis.Bitcoin/Base/Deployments/Models/ThresholdActivationModel.cs index e08f3df347..21426e7b19 100644 --- a/src/Stratis.Bitcoin/Base/Deployments/Models/ThresholdActivationModel.cs +++ b/src/Stratis.Bitcoin/Base/Deployments/Models/ThresholdActivationModel.cs @@ -23,7 +23,7 @@ public class ThresholdActivationModel /// Activation height. /// [JsonProperty(PropertyName = "activationHeight")] - public int activationHeight { get; set; } + public int ActivationHeight { get; set; } /// /// Number of blocks with flags set that led to the deployment being locked in. @@ -35,12 +35,12 @@ public class ThresholdActivationModel /// The height at which the deployment was locked-in. /// [JsonProperty(PropertyName = "lockedInHeight")] - public int? lockedInHeight { get; set; } + public int? LockedInHeight { get; set; } /// /// The timestamp of the blocked at the "lockedInHeight". /// [JsonProperty(PropertyName = "lockedInTimestamp")] - public long? lockedInTimestamp { get; set; } + public long? LockedInTimestamp { get; set; } } } \ No newline at end of file diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 2a2309e901..e8d5c8ece0 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -50,6 +50,7 @@ public interface IMaturedBlocksSyncManager : IDisposable /// public class MaturedBlocksSyncManager : IMaturedBlocksSyncManager { + private const string Release1300DeploymentNameLower = "release1300"; private readonly IAsyncProvider asyncProvider; private readonly ICrossChainTransferStore crossChainTransferStore; private readonly IFederationGatewayClient federationGatewayClient; @@ -155,14 +156,14 @@ public void RecordCounterChainActivations() if (lockedInActivations == null || lockedInActivations.Count == 0) return; - ThresholdActivationModel model = lockedInActivations.FirstOrDefault(a => a.DeploymentName.ToLower() == "release1300"); - if (model == null || model.lockedInTimestamp == null) + ThresholdActivationModel model = lockedInActivations.FirstOrDefault(a => a.DeploymentName.ToLowerInvariant() == Release1300DeploymentNameLower); + if (model == null || model.LockedInTimestamp == null) return; - if (this.chainIndexer.Tip.Header.Time < model.lockedInTimestamp.Value) + if (this.chainIndexer.Tip.Header.Time < model.LockedInTimestamp.Value) return; - int mainChainLockedInHeight = this.chainIndexer.Tip.EnumerateToGenesis().TakeWhile(h => h.Header.Time > (uint)(model.lockedInTimestamp)).FirstOrDefault().Height; + int mainChainLockedInHeight = this.chainIndexer.Tip.EnumerateToGenesis().TakeWhile(h => h.Header.Time > (uint)(model.LockedInTimestamp)).FirstOrDefault().Height; Network counterChainNetwork = this.counterChainSettings.CounterChainNetwork; this.mainChainActivationHeight = mainChainLockedInHeight + From 5de8afb45240ae9f8026bfd4e2404b0ddf0dfaa9 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 21:53:33 +1000 Subject: [PATCH 06/11] Changes based on feedback --- .../TargetChain/MaturedBlocksSyncManager.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 13e07e92ab..05d93db030 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -155,14 +155,23 @@ public void RecordCounterChainActivations() ConsensusClient consensusClient = new ConsensusClient(this.counterChainSettings, this.httpClientFactory); List lockedInActivations = consensusClient.GetLockedInDeployments(this.nodeLifetime.ApplicationStopping).ConfigureAwait(false).GetAwaiter().GetResult(); if (lockedInActivations == null || lockedInActivations.Count == 0) + { + this.logger.LogDebug("There are {0} locked-in deployments.", lockedInActivations?.Count); return; + } ThresholdActivationModel model = lockedInActivations.FirstOrDefault(a => a.DeploymentName.ToLowerInvariant() == Release1300DeploymentNameLower); if (model == null || model.LockedInTimestamp == null) + { + this.logger.LogDebug("There are no locked-in deployments for '{0}'.", Release1300DeploymentNameLower); return; + } if (this.chainIndexer.Tip.Header.Time < model.LockedInTimestamp.Value) + { + this.logger.LogDebug("The chain tip time {0} is still below the locked in time {1}.", this.chainIndexer.Tip.Header.Time, model.LockedInTimestamp.Value); return; + } int mainChainLockedInHeight = this.chainIndexer.Tip.EnumerateToGenesis().TakeWhile(h => h.Header.Time > (uint)(model.LockedInTimestamp)).FirstOrDefault().Height; From 76a6c9c48f95bd34e7ec3535297ed2eefbf52e80 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 22:06:56 +1000 Subject: [PATCH 07/11] Create CounterChainConsensusClient --- .../CounterChainConsensusClient.cs | 31 +++++++++++++++++++ .../TargetChain/MaturedBlocksSyncManager.cs | 23 +------------- 2 files changed, 32 insertions(+), 22 deletions(-) create mode 100644 src/Stratis.Features.FederatedPeg/Controllers/CounterChainConsensusClient.cs diff --git a/src/Stratis.Features.FederatedPeg/Controllers/CounterChainConsensusClient.cs b/src/Stratis.Features.FederatedPeg/Controllers/CounterChainConsensusClient.cs new file mode 100644 index 0000000000..cff0bc1f2f --- /dev/null +++ b/src/Stratis.Features.FederatedPeg/Controllers/CounterChainConsensusClient.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Stratis.Bitcoin.Base.Deployments.Models; +using Stratis.Bitcoin.Controllers; +using Stratis.Features.PoA.Collateral.CounterChain; + +namespace Stratis.Features.FederatedPeg.Controllers +{ + public class CounterChainConsensusClient : RestApiClientBase + { + /// + /// Accesses the consensus controller on the counter-chain. + /// + /// In a production/live scenario the sidechain and mainnet federation nodes should run on the same machine. + /// + /// + /// The counter-chain settings. + /// The implementation. + public CounterChainConsensusClient(ICounterChainSettings counterChainSettings, IHttpClientFactory httpClientFactory) + : base(httpClientFactory, counterChainSettings.CounterChainApiPort, "Consensus", $"http://{counterChainSettings.CounterChainApiHost}") + { + } + + public Task> GetLockedInDeployments(CancellationToken cancellation = default) + { + return this.SendGetRequestAsync>("lockedindeployments", null, cancellation); + } + } +} diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 05d93db030..85e7d396fb 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -2,14 +2,12 @@ using System.Collections.Generic; using System.Linq; using System.Net.Http; -using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using NBitcoin; using Stratis.Bitcoin.AsyncWork; using Stratis.Bitcoin.Base.Deployments.Models; using Stratis.Bitcoin.Configuration.Logging; -using Stratis.Bitcoin.Controllers; using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Features.PoA; using Stratis.Bitcoin.Interfaces; @@ -123,25 +121,6 @@ public MaturedBlocksSyncManager( this.logger = LogManager.GetCurrentClassLogger(); } - public class ConsensusClient : RestApiClientBase - { - /// - /// Currently the is required as it needs to be configurable for testing. - /// - /// In a production/live scenario the sidechain and mainnet federation nodes should run on the same machine. - /// - /// - public ConsensusClient(ICounterChainSettings counterChainSettings, IHttpClientFactory httpClientFactory) - : base(httpClientFactory, counterChainSettings.CounterChainApiPort, "Consensus", $"http://{counterChainSettings.CounterChainApiHost}") - { - } - - public Task> GetLockedInDeployments(CancellationToken cancellation = default) - { - return this.SendGetRequestAsync>("lockedindeployments", null, cancellation); - } - } - public void RecordCounterChainActivations() { // If this is the main chain then ask the side-chain for its activation height. @@ -152,7 +131,7 @@ public void RecordCounterChainActivations() if (this.mainChainActivationHeight != int.MaxValue) return; - ConsensusClient consensusClient = new ConsensusClient(this.counterChainSettings, this.httpClientFactory); + CounterChainConsensusClient consensusClient = new CounterChainConsensusClient(this.counterChainSettings, this.httpClientFactory); List lockedInActivations = consensusClient.GetLockedInDeployments(this.nodeLifetime.ApplicationStopping).ConfigureAwait(false).GetAwaiter().GetResult(); if (lockedInActivations == null || lockedInActivations.Count == 0) { From a90672fd642f0996c538c405f0f8f21b103447d6 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 22:18:17 +1000 Subject: [PATCH 08/11] Fix --- .../TargetChain/MaturedBlocksSyncManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 85e7d396fb..643fc64d98 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -152,7 +152,7 @@ public void RecordCounterChainActivations() return; } - int mainChainLockedInHeight = this.chainIndexer.Tip.EnumerateToGenesis().TakeWhile(h => h.Header.Time > (uint)(model.LockedInTimestamp)).FirstOrDefault().Height; + int mainChainLockedInHeight = this.chainIndexer.Tip.EnumerateToGenesis().TakeWhile(h => h.Header.Time >= (uint)(model.LockedInTimestamp)).LastOrDefault().Height; Network counterChainNetwork = this.counterChainSettings.CounterChainNetwork; this.mainChainActivationHeight = mainChainLockedInHeight + From 7a3a75b7293a9a52a14b9f97b3c33acd363d5120 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 22:25:33 +1000 Subject: [PATCH 09/11] Add comment --- .../TargetChain/MaturedBlocksSyncManager.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 643fc64d98..526c719d7a 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -152,7 +152,8 @@ public void RecordCounterChainActivations() return; } - int mainChainLockedInHeight = this.chainIndexer.Tip.EnumerateToGenesis().TakeWhile(h => h.Header.Time >= (uint)(model.LockedInTimestamp)).LastOrDefault().Height; + // The above condition ensures that the 'Last' below will always return a value. + int mainChainLockedInHeight = this.chainIndexer.Tip.EnumerateToGenesis().TakeWhile(h => h.Header.Time >= (uint)(model.LockedInTimestamp)).Last().Height; Network counterChainNetwork = this.counterChainSettings.CounterChainNetwork; this.mainChainActivationHeight = mainChainLockedInHeight + From 60dd5bb6c578dbe661dd4de0b7ceb65897af3ed1 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Wed, 11 May 2022 23:13:00 +1000 Subject: [PATCH 10/11] Use IsMainChain --- .../SourceChain/RetrievalTypeConfirmations.cs | 51 ++++++------------- .../TargetChain/MaturedBlocksSyncManager.cs | 2 +- 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index fea61e2933..01295ff41a 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -30,7 +30,7 @@ public class RetrievalTypeConfirmations : IRetrievalTypeConfirmations private const int MediumConfirmations = 25; // 1125 seconds - 18m45s private const int HighConfirmations = 50; // 2250 seconds - 37m30s private const int CirrusLowConfirmations = 30; // 480 seconds - 8m0s - private const int CirrusMediumConfirmation = 70; // 1120 seconds - 18m40s + private const int CirrusMediumConfirmations = 70; // 1120 seconds - 18m40s private const int CirrusHighConfirmations = 140; // 2240 seconds - 37m20s private readonly NodeDeployments nodeDeployments; @@ -39,6 +39,7 @@ public class RetrievalTypeConfirmations : IRetrievalTypeConfirmations private readonly Network network; private readonly IMaturedBlocksSyncManager maturedBlocksSyncManager; private readonly ICounterChainSettings counterChainSettings; + private readonly IFederatedPegSettings federatedPegSettings; public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeployments, IFederatedPegSettings federatedPegSettings, IMaturedBlocksSyncManager maturedBlocksSyncManager, ICounterChainSettings counterChainSettings) { @@ -46,6 +47,7 @@ public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeploymen this.network = network; this.maturedBlocksSyncManager = maturedBlocksSyncManager; this.counterChainSettings = counterChainSettings; + this.federatedPegSettings = federatedPegSettings; this.legacyRetrievalTypeConfirmations = new Dictionary { [DepositRetrievalType.Small] = federatedPegSettings.MinimumConfirmationsSmallDeposits, @@ -53,47 +55,24 @@ public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeploymen [DepositRetrievalType.Large] = federatedPegSettings.MinimumConfirmationsLargeDeposits }; + this.retrievalTypeConfirmations = new Dictionary() + { + [DepositRetrievalType.Small] = federatedPegSettings.IsMainChain ? LowConfirmations : CirrusLowConfirmations, + [DepositRetrievalType.Normal] = federatedPegSettings.IsMainChain ? MediumConfirmations : CirrusMediumConfirmations, + [DepositRetrievalType.Large] = federatedPegSettings.IsMainChain ? HighConfirmations : CirrusHighConfirmations + }; + if (federatedPegSettings.IsMainChain) { this.legacyRetrievalTypeConfirmations[DepositRetrievalType.Distribution] = federatedPegSettings.MinimumConfirmationsDistributionDeposits; this.legacyRetrievalTypeConfirmations[DepositRetrievalType.ConversionSmall] = federatedPegSettings.MinimumConfirmationsSmallDeposits; this.legacyRetrievalTypeConfirmations[DepositRetrievalType.ConversionNormal] = federatedPegSettings.MinimumConfirmationsNormalDeposits; this.legacyRetrievalTypeConfirmations[DepositRetrievalType.ConversionLarge] = federatedPegSettings.MinimumConfirmationsLargeDeposits; - } - if (this.network.Name.StartsWith("Cirrus")) - { - this.retrievalTypeConfirmations = new Dictionary - { - [DepositRetrievalType.Small] = CirrusLowConfirmations, - [DepositRetrievalType.Normal] = CirrusMediumConfirmation, - [DepositRetrievalType.Large] = CirrusHighConfirmations - }; - - if (federatedPegSettings.IsMainChain) - { - this.retrievalTypeConfirmations[DepositRetrievalType.Distribution] = CirrusHighConfirmations; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionSmall] = CirrusLowConfirmations; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionNormal] = CirrusMediumConfirmation; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionLarge] = CirrusHighConfirmations; - } - } - else - { - this.retrievalTypeConfirmations = new Dictionary() - { - [DepositRetrievalType.Small] = LowConfirmations, - [DepositRetrievalType.Normal] = MediumConfirmations, - [DepositRetrievalType.Large] = HighConfirmations - }; - - if (federatedPegSettings.IsMainChain) - { - this.retrievalTypeConfirmations[DepositRetrievalType.Distribution] = HighConfirmations; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionSmall] = LowConfirmations; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionNormal] = MediumConfirmations; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionLarge] = HighConfirmations; - } + this.retrievalTypeConfirmations[DepositRetrievalType.Distribution] = HighConfirmations; + this.retrievalTypeConfirmations[DepositRetrievalType.ConversionSmall] = LowConfirmations; + this.retrievalTypeConfirmations[DepositRetrievalType.ConversionNormal] = MediumConfirmations; + this.retrievalTypeConfirmations[DepositRetrievalType.ConversionLarge] = HighConfirmations; } } @@ -109,7 +88,7 @@ private int Release1300ActivationHeight { get { - if (this.network.Name.StartsWith("Cirrus")) + if (!this.federatedPegSettings.IsMainChain) return (this.nodeDeployments?.BIP9.ArraySize > 0) ? this.nodeDeployments.BIP9.ActivationHeightProviders[0 /* Release 1300 */].ActivationHeight : 0; // This code is running on the main chain. diff --git a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs index 526c719d7a..f15e26ecbd 100644 --- a/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs +++ b/src/Stratis.Features.FederatedPeg/TargetChain/MaturedBlocksSyncManager.cs @@ -124,7 +124,7 @@ public MaturedBlocksSyncManager( public void RecordCounterChainActivations() { // If this is the main chain then ask the side-chain for its activation height. - if (this.network.Name.StartsWith("Cirrus")) + if (!this.federatedPegSettings.IsMainChain) return; // Ensures that we only check this once on startup. From d38dde021246473f3c72cab8337ff41d08d5f177 Mon Sep 17 00:00:00 2001 From: Francois de la Rouviere Date: Wed, 11 May 2022 14:17:48 +0100 Subject: [PATCH 11/11] Added xml docs --- .../SourceChain/RetrievalTypeConfirmations.cs | 34 +++++++++++-------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index 01295ff41a..6bc4e89772 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -76,6 +76,7 @@ public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeploymen } } + /// public int MaximumConfirmationsAtMaturityHeight(int maturityHeight) { if (maturityHeight < this.Release1300ActivationHeight) @@ -84,21 +85,7 @@ public int MaximumConfirmationsAtMaturityHeight(int maturityHeight) return this.retrievalTypeConfirmations.Values.Max(); } - private int Release1300ActivationHeight - { - get - { - if (!this.federatedPegSettings.IsMainChain) - return (this.nodeDeployments?.BIP9.ArraySize > 0) ? this.nodeDeployments.BIP9.ActivationHeightProviders[0 /* Release 1300 */].ActivationHeight : 0; - - // This code is running on the main chain. - if (this.counterChainSettings.CounterChainNetwork.Consensus.BIP9Deployments.Length == 0) - return 0; - - return this.maturedBlocksSyncManager.GetMainChainActivationHeight(); - } - } - + /// public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retrievalType) { // Keep everything maturity-height-centric. Otherwise the way we use MaximumConfirmationsAtMaturityHeight will have to change as well. @@ -108,14 +95,31 @@ public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retri return this.retrievalTypeConfirmations[retrievalType]; } + /// public int GetDepositMaturityHeight(int depositHeight, DepositRetrievalType retrievalType) { return depositHeight + GetDepositConfirmations(depositHeight, retrievalType); } + /// public DepositRetrievalType[] GetRetrievalTypes() { return this.retrievalTypeConfirmations.Keys.ToArray(); } + + private int Release1300ActivationHeight + { + get + { + if (!this.federatedPegSettings.IsMainChain) + return (this.nodeDeployments?.BIP9.ArraySize > 0) ? this.nodeDeployments.BIP9.ActivationHeightProviders[0 /* Release 1300 */].ActivationHeight : 0; + + // This code is running on the main chain. + if (this.counterChainSettings.CounterChainNetwork.Consensus.BIP9Deployments.Length == 0) + return 0; + + return this.maturedBlocksSyncManager.GetMainChainActivationHeight(); + } + } } }