From 029288c04b90bbfa23d605d03775837b59b405e0 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 10 May 2022 15:54:13 +1000 Subject: [PATCH 1/8] Update minimum confirmations for cross-chain transfers --- .../FederatedPegFeature.cs | 1 + .../Interfaces/IDepositExtractor.cs | 2 +- .../SourceChain/DepositExtractor.cs | 8 +- .../SourceChain/MaturedBlocksProvider.cs | 34 ++--- .../SourceChain/RetrievalTypeConfirmations.cs | 119 ++++++++++++++++++ 5 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs diff --git a/src/Stratis.Features.FederatedPeg/FederatedPegFeature.cs b/src/Stratis.Features.FederatedPeg/FederatedPegFeature.cs index 9c2be2b48d..1d3e2a9828 100644 --- a/src/Stratis.Features.FederatedPeg/FederatedPegFeature.cs +++ b/src/Stratis.Features.FederatedPeg/FederatedPegFeature.cs @@ -260,6 +260,7 @@ public static IFullNodeBuilder AddFederatedPeg(this IFullNodeBuilder fullNodeBui .FeatureServices(services => { services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(provider => new OpReturnDataReader(provider.GetService().CounterChainNetwork)); services.AddSingleton(); diff --git a/src/Stratis.Features.FederatedPeg/Interfaces/IDepositExtractor.cs b/src/Stratis.Features.FederatedPeg/Interfaces/IDepositExtractor.cs index 880b3f2635..fe734e8817 100644 --- a/src/Stratis.Features.FederatedPeg/Interfaces/IDepositExtractor.cs +++ b/src/Stratis.Features.FederatedPeg/Interfaces/IDepositExtractor.cs @@ -19,7 +19,7 @@ public interface IDepositExtractor /// The height of the block containing the transactions. /// The types of retrieval to perform. The type will determine the value of the deposit to be processed. /// The extracted deposit (if any), otherwise returns an empty list. - Task> ExtractDepositsFromBlock(Block block, int blockHeight, Dictionary depositRetrievalTypes); + Task> ExtractDepositsFromBlock(Block block, int blockHeight, IRetrievalTypeConfirmations depositRetrievalTypes); /// /// Extracts a deposit from a transaction (if any). diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/DepositExtractor.cs b/src/Stratis.Features.FederatedPeg/SourceChain/DepositExtractor.cs index d54ad38d1f..f51e6f30a1 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/DepositExtractor.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/DepositExtractor.cs @@ -64,7 +64,7 @@ public DepositExtractor(IConversionRequestRepository conversionRequestRepository }; /// - public async Task> ExtractDepositsFromBlock(Block block, int blockHeight, Dictionary confirmationsByRetrievalType) + public async Task> ExtractDepositsFromBlock(Block block, int blockHeight, IRetrievalTypeConfirmations confirmationsByRetrievalType) { List deposits; @@ -82,7 +82,7 @@ public async Task> ExtractDepositsFromBlock(Block block, ((Deposit)deposit).BlockHash = block.GetHash(); } - DepositRetrievalType[] retrievalTypes = confirmationsByRetrievalType.Keys.ToArray(); + DepositRetrievalType[] retrievalTypes = confirmationsByRetrievalType.GetRetrievalTypes(); // If it's an empty block (i.e. only the coinbase transaction is present), there's no deposits inside. if (block.Transactions.Count > 1) @@ -110,7 +110,7 @@ public async Task> ExtractDepositsFromBlock(Block block, /// Add burn requests to this list of deposits. /// The block height to inspect. /// The various retrieval types and their required confirmations. - private void ProcessInterFluxBurnRequests(List deposits, int inspectForDepositsAtHeight, Dictionary confirmationsByRetrievalType) + private void ProcessInterFluxBurnRequests(List deposits, int inspectForDepositsAtHeight, IRetrievalTypeConfirmations confirmationsByRetrievalType) { List burnRequests = this.conversionRequestRepository.GetAllBurn(true); @@ -141,7 +141,7 @@ private void ProcessInterFluxBurnRequests(List deposits, int inspectFo if (inspectForDepositsAtHeight > burnRequest.BlockHeight) { DepositRetrievalType retrievalType = DetermineDepositRetrievalType(burnRequest.Amount.GetLow64()); - var requiredConfirmations = confirmationsByRetrievalType[retrievalType]; + var requiredConfirmations = confirmationsByRetrievalType.GetDepositConfirmations(burnRequest.BlockHeight, retrievalType); // If the inspection height is now equal to the burn request's processing height plus // the required confirmations, set it to processed. diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs b/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs index ceea5ba2c9..012675f889 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs @@ -19,12 +19,13 @@ namespace Stratis.Features.FederatedPeg.SourceChain public interface IMaturedBlocksProvider { /// - /// Retrieves deposits for the indicated blocks from the block repository and throws an error if the blocks are not mature enough. + /// Retrieves all deposits maturing from the specified height onwards. + /// Stops returning deposits once a time limit is reached but the client can resume by calling the method again. /// - /// The block height at which to start retrieving blocks. + /// Deposits maturing from this height onwards will be returned. /// /// A list of mature block deposits. - Task>> RetrieveDepositsAsync(int retrieveFromHeight); + Task>> RetrieveDepositsAsync(int maturityHeight); /// /// Retrieves the list of maturing deposits from the cache (if available). @@ -49,30 +50,17 @@ public sealed class MaturedBlocksProvider : IMaturedBlocksProvider private readonly IDepositExtractor depositExtractor; private readonly ConcurrentDictionary deposits; private readonly ILogger logger; - private readonly Dictionary retrievalTypeConfirmations; + private readonly IRetrievalTypeConfirmations retrievalTypeConfirmations; - public MaturedBlocksProvider(IConsensusManager consensusManager, IDepositExtractor depositExtractor, IFederatedPegSettings federatedPegSettings) + public MaturedBlocksProvider(IConsensusManager consensusManager, IDepositExtractor depositExtractor, IRetrievalTypeConfirmations retrievalTypeConfirmations) { this.consensusManager = consensusManager; this.depositExtractor = depositExtractor; + this.retrievalTypeConfirmations = retrievalTypeConfirmations; this.logger = LogManager.GetCurrentClassLogger(); // Take a copy of the tip upfront so that we work with the same tip later. this.deposits = new ConcurrentDictionary(); - this.retrievalTypeConfirmations = new Dictionary - { - [DepositRetrievalType.Small] = federatedPegSettings.MinimumConfirmationsSmallDeposits, - [DepositRetrievalType.Normal] = federatedPegSettings.MinimumConfirmationsNormalDeposits, - [DepositRetrievalType.Large] = federatedPegSettings.MinimumConfirmationsLargeDeposits - }; - - if (federatedPegSettings.IsMainChain) - { - this.retrievalTypeConfirmations[DepositRetrievalType.Distribution] = federatedPegSettings.MinimumConfirmationsDistributionDeposits; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionSmall] = federatedPegSettings.MinimumConfirmationsSmallDeposits; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionNormal] = federatedPegSettings.MinimumConfirmationsNormalDeposits; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionLarge] = federatedPegSettings.MinimumConfirmationsLargeDeposits; - } } /// @@ -91,7 +79,7 @@ public async Task>> RetrieveD if (maturityHeight > this.consensusManager.Tip.Height) return result; - int maxConfirmations = this.retrievalTypeConfirmations.Values.Max(); + int maxConfirmations = this.retrievalTypeConfirmations.MaximumConfirmationsAtMaturityHeight(maturityHeight); int startHeight = maturityHeight - maxConfirmations; // Determine the first block to extract deposits for. @@ -148,7 +136,7 @@ public async Task>> RetrieveD { ChainedHeader tip = this.consensusManager.Tip; - int maxConfirmations = this.retrievalTypeConfirmations.Values.Max(); + int maxConfirmations = this.retrievalTypeConfirmations.MaximumConfirmationsAtMaturityHeight(tip.Height); var deposits = new SortedDictionary>(); @@ -158,7 +146,7 @@ public async Task>> RetrieveD { foreach (IDeposit deposit in blockDeposits.Deposits) { - int blocksBeforeMature = (deposit.BlockNumber + this.retrievalTypeConfirmations[deposit.RetrievalType]) - tip.Height; + int blocksBeforeMature = (deposit.BlockNumber + this.retrievalTypeConfirmations.GetDepositConfirmations(deposit.BlockNumber, deposit.RetrievalType)) - tip.Height; if (blocksBeforeMature >= 0) { if (!deposits.TryGetValue(blocksBeforeMature, out List depositList)) @@ -179,7 +167,7 @@ public async Task>> RetrieveD .ToArray(); } - private async Task RecordBlockDepositsAsync(ChainedHeaderBlock chainedHeaderBlock, Dictionary retrievalTypes) + private async Task RecordBlockDepositsAsync(ChainedHeaderBlock chainedHeaderBlock, IRetrievalTypeConfirmations retrievalTypes) { // Already have this recorded? if (this.deposits.TryGetValue(chainedHeaderBlock.ChainedHeader.Height, out BlockDeposits blockDeposits) && blockDeposits.BlockHash == chainedHeaderBlock.ChainedHeader.HashBlock) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs new file mode 100644 index 0000000000..3b025911f8 --- /dev/null +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -0,0 +1,119 @@ +using System.Collections.Generic; +using System.Linq; +using NBitcoin; +using Stratis.Bitcoin.Base.Deployments; +using Stratis.Features.FederatedPeg.Interfaces; + +namespace Stratis.Features.FederatedPeg.SourceChain +{ + public interface IRetrievalTypeConfirmations + { + int GetDepositConfirmations(int depositHeight, DepositRetrievalType retrievalType); + + int GetDepositMaturityHeight(int depositHeight, DepositRetrievalType retrievalType); + + /// + /// Determines the maximum distance to look back to find deposits that would be maturing at the specified height. + /// + /// The minimum height at which deposits would be maturing. + /// The maximum distance to look back to find deposits that would be maturing at the specified height. + int MaximumConfirmationsAtMaturityHeight(int maturityHeight); + + DepositRetrievalType[] GetRetrievalTypes(); + } + + public class RetrievalTypeConfirmations : IRetrievalTypeConfirmations + { + private const int LowConfirmations = 10; + private const int MediumConfirmations = 25; + private const int HighConfirmations = 50; + private const int CirrusLowConfirmations = 30; + private const int CirrusMediumConfirmation = 70; + private const int CirrusHighConfirmations = 140; + + private readonly NodeDeployments nodeDeployments; + private readonly Dictionary legacyRetrievalTypeConfirmations; + private readonly Dictionary retrievalTypeConfirmations; + private readonly Network network; + + public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeployments, IFederatedPegSettings federatedPegSettings) + { + this.nodeDeployments = nodeDeployments; + this.network = network; + this.legacyRetrievalTypeConfirmations = new Dictionary + { + [DepositRetrievalType.Small] = federatedPegSettings.MinimumConfirmationsSmallDeposits, + [DepositRetrievalType.Normal] = federatedPegSettings.MinimumConfirmationsNormalDeposits, + [DepositRetrievalType.Large] = federatedPegSettings.MinimumConfirmationsLargeDeposits + }; + + 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[DepositRetrievalType.Small] = CirrusLowConfirmations; + this.retrievalTypeConfirmations[DepositRetrievalType.Normal] = CirrusMediumConfirmation; + this.retrievalTypeConfirmations[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[DepositRetrievalType.Small] = LowConfirmations; + this.retrievalTypeConfirmations[DepositRetrievalType.Normal] = MediumConfirmations; + this.retrievalTypeConfirmations[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; + } + } + } + + public int MaximumConfirmationsAtMaturityHeight(int maturityHeight) + { + if (maturityHeight > this.Release1300ActivationHeight) + return this.retrievalTypeConfirmations.Values.Max(); + + return this.legacyRetrievalTypeConfirmations.Values.Max(); + } + + private int Release1300ActivationHeight => (this.nodeDeployments?.BIP9.ArraySize > 0) ? this.nodeDeployments.BIP9.ActivationHeightProviders[0 /* Release 1300 */].ActivationHeight : 0; + + public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retrievalType) + { + if (depositHeight + this.legacyRetrievalTypeConfirmations[retrievalType] < this.Release1300ActivationHeight) + return this.legacyRetrievalTypeConfirmations[retrievalType]; + + if (depositHeight >= this.Release1300ActivationHeight) + return this.retrievalTypeConfirmations[retrievalType]; + + return this.Release1300ActivationHeight - depositHeight; + } + + public int GetDepositMaturityHeight(int depositHeight, DepositRetrievalType retrievalType) + { + return depositHeight + GetDepositConfirmations(depositHeight, retrievalType); + } + + public DepositRetrievalType[] GetRetrievalTypes() + { + return this.retrievalTypeConfirmations.Keys.ToArray(); + } + } +} From 166b9326418be4146771b154f4ee22af7c2e6dc1 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 10 May 2022 16:47:00 +1000 Subject: [PATCH 2/8] Update deposit selection --- .../SourceChain/MaturedBlocksProvider.cs | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs b/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs index 012675f889..d67c34f0c9 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/MaturedBlocksProvider.cs @@ -105,16 +105,12 @@ public async Task>> RetrieveD continue; } - var maturedDeposits = new List(); - - // Inspect the deposits in the block for each retrieval type (validate against the retrieval type's confirmation requirement). - foreach ((DepositRetrievalType retrievalType, int requiredConfirmations) in this.retrievalTypeConfirmations) - { - // If the block height is more than the required confirmations, then the potential deposits - // contained within are valid for the given retrieval type. - if (chainedHeaderBlock.ChainedHeader.Height > requiredConfirmations) - maturedDeposits.AddRange(this.RecallBlockDeposits(chainedHeaderBlock.ChainedHeader.Height - requiredConfirmations, retrievalType)); - } + // Record all deposits maturing in this block. + List maturedDeposits = ( + from kv in this.deposits + from deposit in kv.Value.Deposits + where this.retrievalTypeConfirmations.GetDepositMaturityHeight(kv.Key, deposit.RetrievalType) == chainedHeaderBlock.ChainedHeader.Height + select deposit).ToList(); this.logger.LogDebug("{0} mature deposits retrieved from block '{1}'.", maturedDeposits.Count, chainedHeaderBlock.ChainedHeader); @@ -186,11 +182,6 @@ private async Task RecordBlockDepositsAsync(ChainedHeaderBlock chainedHeaderBloc Deposits = deposits }; } - - private IEnumerable RecallBlockDeposits(int blockHeight, DepositRetrievalType retrievalType) - { - return this.deposits[blockHeight].Deposits.Where(d => d.RetrievalType == retrievalType); - } } /// From d148f0a47ae33f1bd99fd6f96906184409ada9f2 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 10 May 2022 16:47:27 +1000 Subject: [PATCH 3/8] Update RetrievalTypeConfirmations --- .../SourceChain/RetrievalTypeConfirmations.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index 3b025911f8..cb7af0270a 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -100,10 +100,7 @@ public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retri if (depositHeight + this.legacyRetrievalTypeConfirmations[retrievalType] < this.Release1300ActivationHeight) return this.legacyRetrievalTypeConfirmations[retrievalType]; - if (depositHeight >= this.Release1300ActivationHeight) - return this.retrievalTypeConfirmations[retrievalType]; - - return this.Release1300ActivationHeight - depositHeight; + return this.retrievalTypeConfirmations[retrievalType]; } public int GetDepositMaturityHeight(int depositHeight, DepositRetrievalType retrievalType) From c4901788bfe0827fbb14a7eaa75d9b5ad22846df Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 10 May 2022 16:58:18 +1000 Subject: [PATCH 4/8] Refactor --- .../SourceChain/RetrievalTypeConfirmations.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index cb7af0270a..a3f08db5a1 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -97,10 +97,7 @@ public int MaximumConfirmationsAtMaturityHeight(int maturityHeight) public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retrievalType) { - if (depositHeight + this.legacyRetrievalTypeConfirmations[retrievalType] < this.Release1300ActivationHeight) - return this.legacyRetrievalTypeConfirmations[retrievalType]; - - return this.retrievalTypeConfirmations[retrievalType]; + return (depositHeight < this.Release1300ActivationHeight) ? this.legacyRetrievalTypeConfirmations[retrievalType] : this.retrievalTypeConfirmations[retrievalType]; } public int GetDepositMaturityHeight(int depositHeight, DepositRetrievalType retrievalType) From d0df8d69fa3c150f9a0cb0bba65f6e757a015534 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 10 May 2022 17:18:19 +1000 Subject: [PATCH 5/8] Update tests --- .../FederationGatewayControllerTests.cs | 20 +++++++---- .../DepositExtractorTests.cs | 15 +++------ .../MaturedBlocksProviderTests.cs | 33 +++++++------------ .../SourceChain/RetrievalTypeConfirmations.cs | 18 ++++++---- 4 files changed, 41 insertions(+), 45 deletions(-) diff --git a/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs b/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs index 24edccbef2..e016863eec 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/ControllersTests/FederationGatewayControllerTests.cs @@ -12,6 +12,7 @@ using NSubstitute.Core; using Stratis.Bitcoin; using Stratis.Bitcoin.AsyncWork; +using Stratis.Bitcoin.Base.Deployments; using Stratis.Bitcoin.Configuration; using Stratis.Bitcoin.Connection; using Stratis.Bitcoin.Consensus; @@ -73,12 +74,14 @@ public FederationGatewayControllerTests() private FederationGatewayController CreateController(IFederatedPegSettings federatedPegSettings) { + var retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), federatedPegSettings); + var controller = new FederationGatewayController( Substitute.For(), new ChainIndexer(), Substitute.For(), this.crossChainTransferStore, - this.GetMaturedBlocksProvider(federatedPegSettings), + this.GetMaturedBlocksProvider(retrievalTypeConfirmations), this.network, this.federatedPegSettings, this.federationWalletManager, @@ -89,7 +92,7 @@ private FederationGatewayController CreateController(IFederatedPegSettings feder return controller; } - private MaturedBlocksProvider GetMaturedBlocksProvider(IFederatedPegSettings federatedPegSettings) + private MaturedBlocksProvider GetMaturedBlocksProvider(IRetrievalTypeConfirmations retrievalTypeConfirmations) { IBlockRepository blockRepository = Substitute.For(); @@ -108,7 +111,7 @@ private MaturedBlocksProvider GetMaturedBlocksProvider(IFederatedPegSettings fed IExternalApiPoller externalApiPoller = Substitute.For(); - return new MaturedBlocksProvider(this.consensusManager, this.depositExtractor, federatedPegSettings); + return new MaturedBlocksProvider(this.consensusManager, this.depositExtractor, retrievalTypeConfirmations); } [Fact] @@ -154,8 +157,8 @@ public async Task GetMaturedBlockDeposits_Gets_All_Matured_Block_DepositsAsync() ChainedHeader earlierBlock = tip.GetAncestor(minConfirmations); int depositExtractorCallCount = 0; - this.depositExtractor.ExtractDepositsFromBlock(Arg.Any(), Arg.Any(), Arg.Any>()).Returns(new List()); - this.depositExtractor.When(x => x.ExtractDepositsFromBlock(Arg.Any(), Arg.Any(), Arg.Any>())).Do(info => + this.depositExtractor.ExtractDepositsFromBlock(Arg.Any(), Arg.Any(), Arg.Any()).Returns(new List()); + this.depositExtractor.When(x => x.ExtractDepositsFromBlock(Arg.Any(), Arg.Any(), Arg.Any())).Do(info => { depositExtractorCallCount++; }); @@ -207,12 +210,14 @@ 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 controller = new FederationGatewayController( Substitute.For(), new ChainIndexer(), Substitute.For(), this.crossChainTransferStore, - this.GetMaturedBlocksProvider(federatedPegSettings), + this.GetMaturedBlocksProvider(retrievalTypeConfirmations), this.network, federatedPegSettings, this.federationWalletManager, @@ -299,13 +304,14 @@ 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 controller = new FederationGatewayController( Substitute.For(), new ChainIndexer(), Substitute.For(), this.crossChainTransferStore, - this.GetMaturedBlocksProvider(settings), + this.GetMaturedBlocksProvider(retrievalTypeConfirmations), this.network, settings, this.federationWalletManager, diff --git a/src/Stratis.Features.FederatedPeg.Tests/DepositExtractorTests.cs b/src/Stratis.Features.FederatedPeg.Tests/DepositExtractorTests.cs index afc4ee2011..72f607382b 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/DepositExtractorTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/DepositExtractorTests.cs @@ -3,9 +3,11 @@ using System.Text; using System.Threading.Tasks; using FluentAssertions; +using Moq; using NBitcoin; using NSubstitute; using Stratis.Bitcoin; +using Stratis.Bitcoin.Base.Deployments; using Stratis.Bitcoin.Features.ExternalApi; using Stratis.Bitcoin.Features.Wallet; using Stratis.Bitcoin.Networks; @@ -29,7 +31,7 @@ public class DepositExtractorTests private readonly Network network; private readonly MultisigAddressHelper addressHelper; private readonly TestTransactionBuilder transactionBuilder; - private readonly Dictionary retrievalTypeConfirmations; + private readonly RetrievalTypeConfirmations retrievalTypeConfirmations; public DepositExtractorTests() { @@ -53,16 +55,7 @@ public DepositExtractorTests() this.depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federationSettings, this.network, this.opReturnDataReader); this.transactionBuilder = new TestTransactionBuilder(); - this.retrievalTypeConfirmations = new Dictionary - { - [DepositRetrievalType.Small] = this.federationSettings.MinimumConfirmationsSmallDeposits, - [DepositRetrievalType.Normal] = this.federationSettings.MinimumConfirmationsNormalDeposits, - [DepositRetrievalType.Large] = this.federationSettings.MinimumConfirmationsLargeDeposits, - [DepositRetrievalType.Distribution] = this.federationSettings.MinimumConfirmationsDistributionDeposits, - [DepositRetrievalType.ConversionSmall] = this.federationSettings.MinimumConfirmationsSmallDeposits, - [DepositRetrievalType.ConversionNormal] = this.federationSettings.MinimumConfirmationsNormalDeposits, - [DepositRetrievalType.ConversionLarge] = this.federationSettings.MinimumConfirmationsLargeDeposits - }; + this.retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), this.federationSettings); } // Normal Deposits diff --git a/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs b/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs index 064bff3a4c..b71fbf6c8d 100644 --- a/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs +++ b/src/Stratis.Features.FederatedPeg.Tests/MaturedBlocksProviderTests.cs @@ -8,6 +8,7 @@ using NSubstitute; using NSubstitute.Core; using Stratis.Bitcoin; +using Stratis.Bitcoin.Base.Deployments; using Stratis.Bitcoin.Consensus; using Stratis.Bitcoin.Features.Wallet; using Stratis.Bitcoin.Networks; @@ -39,7 +40,7 @@ public sealed class MaturedBlocksProviderTests private readonly byte[] opReturnBytes; private readonly BitcoinPubKeyAddress targetAddress; private List blocks; - private Dictionary retrievalTypeConfirmations; + private IRetrievalTypeConfirmations retrievalTypeConfirmations; public MaturedBlocksProviderTests() { @@ -79,17 +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 Dictionary - { - [DepositRetrievalType.Small] = this.federatedPegSettings.MinimumConfirmationsSmallDeposits, - [DepositRetrievalType.Normal] = this.federatedPegSettings.MinimumConfirmationsNormalDeposits, - [DepositRetrievalType.Large] = this.federatedPegSettings.MinimumConfirmationsLargeDeposits, - }; - - this.retrievalTypeConfirmations[DepositRetrievalType.Distribution] = this.federatedPegSettings.MinimumConfirmationsDistributionDeposits; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionSmall] = this.federatedPegSettings.MinimumConfirmationsSmallDeposits; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionNormal] = this.federatedPegSettings.MinimumConfirmationsNormalDeposits; - this.retrievalTypeConfirmations[DepositRetrievalType.ConversionLarge] = this.federatedPegSettings.MinimumConfirmationsLargeDeposits; + this.retrievalTypeConfirmations = new RetrievalTypeConfirmations(this.network, new NodeDeployments(this.network, new ChainIndexer(this.network)), this.federatedPegSettings); } [Fact] @@ -110,7 +101,7 @@ public async Task GetMaturedBlocksReturnsDepositsAsync() this.consensusManager.Tip.Returns(tip); // Makes every block a matured block. - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); SerializableResult> depositsResult = await maturedBlocksProvider.RetrieveDepositsAsync(0); @@ -158,7 +149,7 @@ public async Task RetrieveDeposits_ReturnsDataToAdvanceNextMaturedBlockHeightAsy }); var depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federatedPegSettings, this.network, this.opReturnDataReader); - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); int nextMaturedBlockHeight = 1; for (int i = 1; i < this.blocks.Count; i++) @@ -219,7 +210,7 @@ public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario2_Async this.consensusManager.Tip.Returns(this.blocks.Last().ChainedHeader); var depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federatedPegSettings, this.network, this.opReturnDataReader); - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); SerializableResult> depositsResult = await maturedBlocksProvider.RetrieveDepositsAsync(5); @@ -273,7 +264,7 @@ public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario3_Async this.consensusManager.Tip.Returns(this.blocks.Last().ChainedHeader); var depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federatedPegSettings, this.network, this.opReturnDataReader); - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); SerializableResult> depositsResult = await maturedBlocksProvider.RetrieveDepositsAsync(5); @@ -329,7 +320,7 @@ public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario4_Async this.consensusManager.Tip.Returns(this.blocks.Last().ChainedHeader); var depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federatedPegSettings, this.network, this.opReturnDataReader); - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); SerializableResult> depositsResult = await maturedBlocksProvider.RetrieveDepositsAsync(10); @@ -386,7 +377,7 @@ public async Task RetrieveDeposits_ReturnsSmallAndNormalDeposits_Scenario5_Async this.consensusManager.Tip.Returns(this.blocks.Last().ChainedHeader); var depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federatedPegSettings, this.network, this.opReturnDataReader); - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); SerializableResult> depositsResult = await maturedBlocksProvider.RetrieveDepositsAsync(10); @@ -442,7 +433,7 @@ public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario6_Async() this.consensusManager.Tip.Returns(this.blocks.Last().ChainedHeader); var depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federatedPegSettings, this.network, this.opReturnDataReader); - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); SerializableResult> depositsResult = await maturedBlocksProvider.RetrieveDepositsAsync(10); @@ -504,7 +495,7 @@ public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario7_Async() this.consensusManager.Tip.Returns(this.blocks.Last().ChainedHeader); var depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federatedPegSettings, this.network, this.opReturnDataReader); - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); SerializableResult> depositsResult = await maturedBlocksProvider.RetrieveDepositsAsync(10); @@ -567,7 +558,7 @@ public async Task RetrieveDeposits_ReturnsLargeDeposits_Scenario8_Async() this.consensusManager.Tip.Returns(this.blocks.Last().ChainedHeader); var depositExtractor = new DepositExtractor(this.conversionRequestRepository, this.federatedPegSettings, this.network, this.opReturnDataReader); - var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.federatedPegSettings); + var maturedBlocksProvider = new MaturedBlocksProvider(this.consensusManager, depositExtractor, this.retrievalTypeConfirmations); SerializableResult> depositsResult = await maturedBlocksProvider.RetrieveDepositsAsync(10); diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index a3f08db5a1..e971bd3ae4 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -57,9 +57,12 @@ public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeploymen if (this.network.Name.StartsWith("Cirrus")) { - this.retrievalTypeConfirmations[DepositRetrievalType.Small] = CirrusLowConfirmations; - this.retrievalTypeConfirmations[DepositRetrievalType.Normal] = CirrusMediumConfirmation; - this.retrievalTypeConfirmations[DepositRetrievalType.Large] = CirrusHighConfirmations; + this.retrievalTypeConfirmations = new Dictionary + { + [DepositRetrievalType.Small] = CirrusLowConfirmations, + [DepositRetrievalType.Normal] = CirrusMediumConfirmation, + [DepositRetrievalType.Large] = CirrusHighConfirmations + }; if (federatedPegSettings.IsMainChain) { @@ -71,9 +74,12 @@ public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeploymen } else { - this.retrievalTypeConfirmations[DepositRetrievalType.Small] = LowConfirmations; - this.retrievalTypeConfirmations[DepositRetrievalType.Normal] = MediumConfirmations; - this.retrievalTypeConfirmations[DepositRetrievalType.Large] = HighConfirmations; + this.retrievalTypeConfirmations = new Dictionary() + { + [DepositRetrievalType.Small] = LowConfirmations, + [DepositRetrievalType.Normal] = MediumConfirmations, + [DepositRetrievalType.Large] = HighConfirmations + }; if (federatedPegSettings.IsMainChain) { From 269fe6f75d0df5fb2498a038109a8eeeaecbd19f Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 10 May 2022 17:26:42 +1000 Subject: [PATCH 6/8] Refactor --- .../SourceChain/RetrievalTypeConfirmations.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index e971bd3ae4..0c29655ba4 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -103,7 +103,11 @@ public int MaximumConfirmationsAtMaturityHeight(int maturityHeight) public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retrievalType) { - return (depositHeight < this.Release1300ActivationHeight) ? this.legacyRetrievalTypeConfirmations[retrievalType] : this.retrievalTypeConfirmations[retrievalType]; + // Keep everything maturity-height-centric. Otherwise the way we use MaximumConfirmationsAtMaturityHeight will have to change as well. + if (depositHeight + this.legacyRetrievalTypeConfirmations[retrievalType] < this.Release1300ActivationHeight) + return this.legacyRetrievalTypeConfirmations[retrievalType]; + + return this.retrievalTypeConfirmations[retrievalType]; } public int GetDepositMaturityHeight(int depositHeight, DepositRetrievalType retrievalType) From 6f846245001596ee2a608b6d8d0fef94246f1e0d Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 10 May 2022 17:29:51 +1000 Subject: [PATCH 7/8] Align maturity height comparisons exactly --- .../SourceChain/RetrievalTypeConfirmations.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index 0c29655ba4..7d2811e4a0 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -93,10 +93,10 @@ public RetrievalTypeConfirmations(Network network, NodeDeployments nodeDeploymen public int MaximumConfirmationsAtMaturityHeight(int maturityHeight) { - if (maturityHeight > this.Release1300ActivationHeight) - return this.retrievalTypeConfirmations.Values.Max(); + if (maturityHeight < this.Release1300ActivationHeight) + return this.legacyRetrievalTypeConfirmations.Values.Max(); - return this.legacyRetrievalTypeConfirmations.Values.Max(); + return this.retrievalTypeConfirmations.Values.Max(); } private int Release1300ActivationHeight => (this.nodeDeployments?.BIP9.ArraySize > 0) ? this.nodeDeployments.BIP9.ActivationHeightProviders[0 /* Release 1300 */].ActivationHeight : 0; From 29dd0dba0072719b78076c14e7b1348a58233369 Mon Sep 17 00:00:00 2001 From: quantumagi Date: Tue, 10 May 2022 17:35:57 +1000 Subject: [PATCH 8/8] Add comments --- .../SourceChain/RetrievalTypeConfirmations.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs index 7d2811e4a0..be84381cfe 100644 --- a/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs +++ b/src/Stratis.Features.FederatedPeg/SourceChain/RetrievalTypeConfirmations.cs @@ -24,12 +24,12 @@ public interface IRetrievalTypeConfirmations public class RetrievalTypeConfirmations : IRetrievalTypeConfirmations { - private const int LowConfirmations = 10; - private const int MediumConfirmations = 25; - private const int HighConfirmations = 50; - private const int CirrusLowConfirmations = 30; - private const int CirrusMediumConfirmation = 70; - private const int CirrusHighConfirmations = 140; + private const int LowConfirmations = 10; // 450 seconds - 7m30s + 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 CirrusHighConfirmations = 140; // 2240 seconds - 37m20s private readonly NodeDeployments nodeDeployments; private readonly Dictionary legacyRetrievalTypeConfirmations;