Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,13 @@ public IActionResult LockedInDeployments()

List<ThresholdStateModel> 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)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,24 @@ public class ThresholdActivationModel
/// Activation height.
/// </summary>
[JsonProperty(PropertyName = "activationHeight")]
public int activationHeight { get; set; }
public int ActivationHeight { get; set; }

/// <summary>
/// Number of blocks with flags set that led to the deployment being locked in.
/// </summary>
[JsonProperty(PropertyName = "votes")]
public int Votes { get; set; }

/// <summary>
/// The height at which the deployment was locked-in.
/// </summary>
[JsonProperty(PropertyName = "lockedInHeight")]
public int? LockedInHeight { get; set; }

/// <summary>
/// The timestamp of the blocked at the "lockedInHeight".
/// </summary>
[JsonProperty(PropertyName = "lockedInTimestamp")]
public long? LockedInTimestamp { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public List<ThresholdStateModel> 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
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<IAsyncProvider>(),
Expand Down Expand Up @@ -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<IAsyncProvider>(),
Expand Down Expand Up @@ -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<IAsyncProvider>(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Accesses the consensus controller on the counter-chain.
/// <para>
/// In a production/live scenario the sidechain and mainnet federation nodes should run on the same machine.
/// </para>
/// </summary>
/// <param name="counterChainSettings">The counter-chain settings.</param>
/// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/> implementation.</param>
public CounterChainConsensusClient(ICounterChainSettings counterChainSettings, IHttpClientFactory httpClientFactory)
: base(httpClientFactory, counterChainSettings.CounterChainApiPort, "Consensus", $"http://{counterChainSettings.CounterChainApiHost}")
{
}

public Task<List<ThresholdActivationModel>> GetLockedInDeployments(CancellationToken cancellation = default)
{
return this.SendGetRequestAsync<List<ThresholdActivationModel>>("lockedindeployments", null, cancellation);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -28,69 +30,53 @@ 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;
private readonly Dictionary<DepositRetrievalType, int> legacyRetrievalTypeConfirmations;
private readonly Dictionary<DepositRetrievalType, int> retrievalTypeConfirmations;
private readonly Network network;
private readonly IMaturedBlocksSyncManager maturedBlocksSyncManager;
private readonly ICounterChainSettings counterChainSettings;
private readonly IFederatedPegSettings federatedPegSettings;

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.federatedPegSettings = federatedPegSettings;
this.legacyRetrievalTypeConfirmations = new Dictionary<DepositRetrievalType, int>
{
[DepositRetrievalType.Small] = federatedPegSettings.MinimumConfirmationsSmallDeposits,
[DepositRetrievalType.Normal] = federatedPegSettings.MinimumConfirmationsNormalDeposits,
[DepositRetrievalType.Large] = federatedPegSettings.MinimumConfirmationsLargeDeposits
};

this.retrievalTypeConfirmations = new Dictionary<DepositRetrievalType, int>()
{
[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, int>
{
[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, int>()
{
[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;
}
}

/// <inheritdoc/>
public int MaximumConfirmationsAtMaturityHeight(int maturityHeight)
{
if (maturityHeight < this.Release1300ActivationHeight)
Expand All @@ -99,8 +85,7 @@ 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;

/// <inheritdoc/>
public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retrievalType)
{
// Keep everything maturity-height-centric. Otherwise the way we use MaximumConfirmationsAtMaturityHeight will have to change as well.
Expand All @@ -110,14 +95,31 @@ public int GetDepositConfirmations(int depositHeight, DepositRetrievalType retri
return this.retrievalTypeConfirmations[retrievalType];
}

/// <inheritdoc/>
public int GetDepositMaturityHeight(int depositHeight, DepositRetrievalType retrievalType)
{
return depositHeight + GetDepositConfirmations(depositHeight, retrievalType);
}

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