Skip to content
This repository has been archived by the owner on Aug 16, 2021. It is now read-only.

Fix Pos_Given_NodesAreSynced_When_ABigReorgHappens_Then_TheReorgIsIgnored #3291

Merged
merged 1 commit into from
Feb 27, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/Stratis.Bitcoin.IntegrationTests.Common/TestHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using NBitcoin;
Expand Down Expand Up @@ -184,6 +185,11 @@ public static void TriggerSync(CoreNode node)
connectedPeer.Behavior<ConsensusManagerBehavior>().ResyncAsync().GetAwaiter().GetResult();
}

/// <summary>
/// Determines whether or not the node has any connections.
/// </summary>
/// <param name="node">The node to check.</param>
/// <returns>Returns <c>true</c> if the node does not have any connected peers.</returns>
public static bool IsNodeConnected(CoreNode node)
{
return node.FullNode.ConnectionManager.ConnectedPeers.Any();
Expand Down Expand Up @@ -428,7 +434,7 @@ public static void Connect(CoreNode thisNode, CoreNode connectToNode)
}

/// <summary>
/// This connect method will only retry the connection if an RPC exception occurred.
/// This connect method will only retry the connection if an WebException occurred.
/// <para>
/// In cases where we expect the node to disconnect, this should be used.
/// </para>
Expand All @@ -446,10 +452,14 @@ public static void ConnectNoCheck(CoreNode thisNode, CoreNode connectToNode)
thisNode.CreateRPCClient().AddNode(connectToNode.Endpoint, true);
return true;
}
catch (Exception)
catch (WebException)
{
return false;
}
catch (Exception)
{
return true;
}
}, retryDelayInMiliseconds: 500, cancellationToken: cancellation.Token);
}

Expand Down
90 changes: 26 additions & 64 deletions src/Stratis.Bitcoin.IntegrationTests/NodeSyncTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using System.IO;
using System.Linq;
using NBitcoin;
using NBitcoin.BouncyCastle.Math;
using Stratis.Bitcoin.Connection;
using Stratis.Bitcoin.IntegrationTests.Common;
using Stratis.Bitcoin.IntegrationTests.Common.EnvironmentMockUpHelpers;
Expand All @@ -23,45 +22,14 @@ public NodeSyncTests()
this.posNetwork = new StratisRegTest();
}

private class StratisRegTestMaxReorg : StratisRegTest
public class StratisRegTestMaxReorg : StratisRegTest
{
public StratisRegTestMaxReorg()
{
this.Consensus = new NBitcoin.Consensus(
consensusFactory: base.Consensus.ConsensusFactory,
consensusOptions: base.Consensus.Options,
coinType: 105,
hashGenesisBlock: base.GenesisHash,
subsidyHalvingInterval: 210000,
majorityEnforceBlockUpgrade: 750,
majorityRejectBlockOutdated: 950,
majorityWindow: 1000,
buriedDeployments: base.Consensus.BuriedDeployments,
bip9Deployments: base.Consensus.BIP9Deployments,
bip34Hash: new uint256("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"),
ruleChangeActivationThreshold: 1916, // 95% of 2016
minerConfirmationWindow: 2016, // nPowTargetTimespan / nPowTargetSpacing
maxReorgLength: 10,
defaultAssumeValid: null, // turn off assumevalid for regtest.
maxMoney: long.MaxValue,
coinbaseMaturity: 10,
premineHeight: 2,
premineReward: Money.Coins(98000000),
proofOfWorkReward: Money.Coins(4),
powTargetTimespan: TimeSpan.FromSeconds(14 * 24 * 60 * 60), // two weeks
powTargetSpacing: TimeSpan.FromSeconds(10 * 60),
powAllowMinDifficultyBlocks: true,
posNoRetargeting: true,
powNoRetargeting: true,
powLimit: base.Consensus.PowLimit,
minimumChainWork: null,
isProofOfStake: true,
lastPowBlock: 12500,
proofOfStakeLimit: new BigInteger(uint256.Parse("00000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false)),
proofOfStakeLimitV2: new BigInteger(uint256.Parse("000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffff").ToBytes(false)),
proofOfStakeReward: Money.COIN);

this.Name = Guid.NewGuid().ToString();

Type consensusType = typeof(NBitcoin.Consensus);
consensusType.GetProperty("MaxReorgLength").SetValue(this.Consensus, (uint)10);
}
}

Expand Down Expand Up @@ -152,55 +120,49 @@ public void Pow_CanCoreSyncFromStratis()
}

[Fact]
[Trait("Unstable", "True")]
public void Pos_Given_NodesAreSynced_When_ABigReorgHappens_Then_TheReorgIsIgnored()
{
using (NodeBuilder builder = NodeBuilder.Create(this))
{
var stratisRegTestMaxReorg = new StratisRegTestMaxReorg();
CoreNode stratisMiner = builder.CreateStratisPosNode(stratisRegTestMaxReorg, nameof(stratisMiner)).WithDummyWallet().Start();
CoreNode stratisSyncer = builder.CreateStratisPosNode(stratisRegTestMaxReorg, nameof(stratisSyncer)).Start();
CoreNode stratisReorg = builder.CreateStratisPosNode(stratisRegTestMaxReorg, nameof(stratisReorg)).WithDummyWallet().Start();

TestHelper.MineBlocks(stratisMiner, 1);
CoreNode miner = builder.CreateStratisPosNode(stratisRegTestMaxReorg, nameof(miner)).WithDummyWallet().Start();
CoreNode syncer = builder.CreateStratisPosNode(stratisRegTestMaxReorg, nameof(syncer)).Start();
CoreNode reorg = builder.CreateStratisPosNode(stratisRegTestMaxReorg, nameof(reorg)).WithDummyWallet().Start();

// Wait for block repo for block sync to work
TestHelper.ConnectAndSync(stratisMiner, stratisReorg);
TestHelper.ConnectAndSync(stratisMiner, stratisSyncer);
TestHelper.MineBlocks(miner, 1);

// Create a reorg by mining on two different chains
TestHelper.Disconnect(stratisMiner, stratisReorg);
TestHelper.Disconnect(stratisMiner, stratisSyncer);
// Sync miner with syncer and reorg
TestHelper.ConnectAndSync(miner, reorg);
TestHelper.ConnectAndSync(miner, syncer);

TestHelper.MineBlocks(stratisMiner, 11);
TestHelper.MineBlocks(stratisReorg, 12);
// Create a reorg by mining on two different chains
TestHelper.Disconnect(miner, reorg);
TestHelper.Disconnect(miner, syncer);
TestHelper.MineBlocks(miner, 11);
TestHelper.MineBlocks(reorg, 12);

// Make sure the nodes are actually on different chains.
Assert.NotEqual(stratisMiner.FullNode.Chain.GetBlock(2).HashBlock, stratisReorg.FullNode.Chain.GetBlock(2).HashBlock);
Assert.NotEqual(miner.FullNode.Chain.GetBlock(2).HashBlock, reorg.FullNode.Chain.GetBlock(2).HashBlock);

TestHelper.ConnectAndSync(stratisSyncer, stratisMiner);
TestHelper.ConnectAndSync(miner, syncer);

// The hash before the reorg node is connected.
uint256 hashBeforeReorg = stratisMiner.FullNode.Chain.Tip.HashBlock;
uint256 hashBeforeReorg = miner.FullNode.Chain.Tip.HashBlock;

// Connect the reorg chain
TestHelper.Connect(stratisMiner, stratisReorg);
TestHelper.Connect(stratisSyncer, stratisReorg);

// Trigger nodes to sync
TestHelper.TriggerSync(stratisMiner);
TestHelper.TriggerSync(stratisReorg);
TestHelper.TriggerSync(stratisSyncer);
TestHelper.ConnectNoCheck(miner, reorg);
TestHelper.ConnectNoCheck(syncer, reorg);

// Wait for the synced chain to get headers updated.
TestHelper.WaitLoop(() => !stratisReorg.FullNode.ConnectionManager.ConnectedPeers.Any());
TestHelper.WaitLoop(() => !TestHelper.IsNodeConnected(reorg));

TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisMiner, stratisSyncer));
TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisMiner) == false);
TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReorg, stratisSyncer) == false);
TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(miner, syncer));
TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(reorg, miner) == false);
TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(reorg, syncer) == false);

// Check that a reorg did not happen.
Assert.Equal(hashBeforeReorg, stratisSyncer.FullNode.Chain.Tip.HashBlock);
Assert.Equal(hashBeforeReorg, syncer.FullNode.Chain.Tip.HashBlock);
}
}

Expand Down