public void LeaderChange()
        {
            using (SidechainTestContext context = new SidechainTestContext())
            {
                context.StartAndConnectNodes();

                IFederationGatewaySettings federationGatewaySettings = new FederationGatewaySettings(context.FedSide1.FullNode.Settings);
                ILeaderProvider            leaderProvider            = new LeaderProvider(federationGatewaySettings);

                PubKey currentLeader = leaderProvider.CurrentLeaderKey;

                int tipBefore = context.FedSide1.GetTip().Height;

                // TODO check blocks get mined and make sure the block notification will change
                // the leader.
                TestHelper.WaitLoop(
                    () =>
                {
                    return(context.FedSide1.GetTip().Height >= tipBefore + 5);
                }
                    );

                leaderProvider.CurrentLeaderKey.Should().NotBe(currentLeader);
            }
        }
        public async Task MainChain_To_SideChain_Transfer_And_Back()
        {
            using (var context = new SidechainTestContext())
            {
                // Set everything up
                context.StartAndConnectNodes();
                context.EnableSideFedWallets();
                context.EnableMainFedWallets();

                // Fund a main chain node
                TestHelper.MineBlocks(context.MainUser, (int)context.MainChainNetwork.Consensus.CoinbaseMaturity + (int)context.MainChainNetwork.Consensus.PremineHeight);
                TestHelper.WaitForNodeToSync(context.MainUser, context.FedMain1);
                Assert.True(context.MainUser.GetBalance() > context.MainChainNetwork.Consensus.PremineReward);

                // Let sidechain progress to point where fed has the premine
                TestBase.WaitLoop(() => context.SideUser.FullNode.ChainIndexer.Height >= context.SideUser.FullNode.Network.Consensus.PremineHeight);
                TestHelper.WaitForNodeToSync(context.SideUser, context.FedSide1);
                Block       block    = context.SideUser.FullNode.ChainIndexer.GetHeader((int)context.SideChainNetwork.Consensus.PremineHeight).Block;
                Transaction coinbase = block.Transactions[0];
                Assert.Single(coinbase.Outputs);
                Assert.Equal(context.SideChainNetwork.Consensus.PremineReward, coinbase.Outputs[0].Value);
                Assert.Equal(context.scriptAndAddresses.payToMultiSig.PaymentScript, coinbase.Outputs[0].ScriptPubKey);

                // Send to sidechain
                decimal transferValueCoins = 25;
                var     transferValue      = new Money(transferValueCoins, MoneyUnit.BTC);
                string  sidechainAddress   = context.SideUser.GetUnusedAddress();
                await context.DepositToSideChain(context.MainUser, transferValueCoins, sidechainAddress);

                TestBase.WaitLoop(() => context.FedMain1.CreateRPCClient().GetRawMempool().Length == 1);
                TestHelper.MineBlocks(context.FedMain1, 15);

                var source = new CancellationTokenSource(15_000);
                TestBase.WaitLoop(() => context.SideUser.GetBalance() == transferValue, cancellationToken: source.Token);

                // Sidechain user has balance - transfer complete.
                Assert.Equal(transferValue, context.SideUser.GetBalance());

                await Task.Delay(5_000).ConfigureAwait(false);

                // Send funds back to the main chain
                string mainchainAddress       = context.MainUser.GetUnusedAddress();
                Money  currentMainUserBalance = context.MainUser.GetBalance();

                await context.WithdrawToMainChain(context.SideUser, 24, mainchainAddress);

                int currentSideHeight = context.SideUser.FullNode.ChainIndexer.Tip.Height;
                // Mine just enough to get past min deposit and allow time for fed to work
                TestBase.WaitLoop(() => context.SideUser.FullNode.ChainIndexer.Height >= currentSideHeight + 7);

                // Should unlock funds back on the main chain
                TestBase.WaitLoop(() => context.FedMain1.CreateRPCClient().GetRawMempool().Length == 1);
                TestHelper.MineBlocks(context.FedMain1, 1);
                Assert.Equal(currentMainUserBalance + new Money(24, MoneyUnit.BTC), context.MainUser.GetBalance());
            }
        }
        public void StartBothChainsWithWallets()
        {
            using (var context = new SidechainTestContext())
            {
                context.StartAndConnectNodes();

                context.EnableSideFedWallets();
                context.EnableMainFedWallets();
            }
        }
        public async Task ParallelWithdrawalsToSidechain()
        {
            using (var context = new SidechainTestContext())
            {
                // Set everything up
                context.StartAndConnectNodes();
                context.EnableSideFedWallets();
                context.EnableMainFedWallets();

                // Fund a main chain node
                TestHelper.MineBlocks(context.MainUser, (int)context.MainChainNetwork.Consensus.CoinbaseMaturity + (int)context.MainChainNetwork.Consensus.PremineHeight);
                TestHelper.WaitForNodeToSync(context.MainUser, context.FedMain1);
                Assert.True(context.MainUser.GetBalance() > context.MainChainNetwork.Consensus.PremineReward);

                // Let sidechain progress to point where fed has the premine
                await context.FedSide1.MineBlocksAsync((int)context.SideChainNetwork.Consensus.PremineHeight);

                TestHelper.WaitForNodeToSync(context.SideUser, context.FedSide1);
                Block       block    = context.SideUser.FullNode.ChainIndexer.GetHeader((int)context.SideChainNetwork.Consensus.PremineHeight).Block;
                Transaction coinbase = block.Transactions[0];
                Assert.Equal(FederatedPegBlockDefinition.FederationWalletOutputs, coinbase.Outputs.Count);

                // Send multiple deposits to sidechain
                decimal transferValueCoins = 10;
                string  sidechainAddress   = context.SideUser.GetUnusedAddress();

                // Lets send 3 deposits all exactly the same
                const int toSend = 3;
                for (int i = 0; i < toSend; i++)
                {
                    await context.DepositToSideChain(context.MainUser, transferValueCoins, sidechainAddress);
                }

                TestBase.WaitLoop(() => context.FedMain1.CreateRPCClient().GetRawMempool().Length == toSend);

                // Mine enough blocks to make the deposits mature on the main chain
                TestHelper.MineBlocks(context.FedMain1, 6);

                // Wait until our sidechain nodes have all fully signed the transactions.
                ICrossChainTransferStore fedSideStore = context.FedSide1.FullNode.NodeService <ICrossChainTransferStore>();
                TestBase.WaitLoop(() =>
                {
                    ICrossChainTransfer[] fullySignedTransactions = fedSideStore.GetTransfersByStatus(new[] { CrossChainTransferStatus.FullySigned });
                    return(fullySignedTransactions.Length == toSend);
                });

                // Mine one more block on the main chain to trigger leader selection on sidechain
                TestHelper.MineBlocks(context.FedMain1, 1);

                // Wait for all the withdrawal transactions to reach the mempool on the sidechain
                TestBase.WaitLoop(() =>
                {
                    int inMempool = context.FedSide1.CreateRPCClient().GetRawMempool().Length;
                    return(inMempool == toSend);
                });

                await context.FedSide2.MineBlocksAsync(1);

                TestHelper.WaitForNodeToSync(context.SideUser, context.FedSide2);

                Block sideBlock = context.SideUser.FullNode.ChainIndexer.Tip.Block;
                Assert.Equal(toSend + 1, sideBlock.Transactions.Count);
            }
        }