public void EscrowGetRedeemedIfTimeout()
        {
            using (var server = TumblerServerTester.Create())
            {
                server.AliceNode.FindBlock(1);
                server.SyncNodes();
                server.TumblerNode.FindBlock(1);
                server.SyncNodes();
                server.BobNode.FindBlock(103);
                server.SyncNodes();

                var machine = server.ClientContext.PaymentMachineState;
                machine.Update();
                var cycle = machine.ClientChannelNegotiation.GetCycle();

                MineTo(server.AliceNode, cycle, CyclePhase.ClientChannelEstablishment);
                server.SyncNodes();

                machine.Update();

                //Wait the client escrow is confirmed
                server.AliceNode.FindBlock(2);
                server.SyncNodes();

                //Server does not track anything until Alice gives proof of the escrow
                Assert.Equal(0, server.ServerContext.BlockExplorer
                             .GetTransactions(machine.SolverClientSession.EscrowedCoin.ScriptPubKey, false)
                             .Count());

                machine.Update();

                //Server is now tracking Alice's escrow
                Assert.Equal(1, server.ServerContext.BlockExplorer
                             .GetTransactions(machine.SolverClientSession.EscrowedCoin.ScriptPubKey, false)
                             .Count());

                MineTo(server.AliceNode, cycle, CyclePhase.TumblerChannelEstablishment);
                server.SyncNodes();

                machine.Update();

                //Client Refund broadcasted exactly when we are at ClientCashoutPhase + SafetyPeriodDuration
                MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, offset: cycle.SafetyPeriodDuration - 1);
                var broadcasted = server.ClientContext.TrustedBroadcastService.TryBroadcast();
                Assert.Equal(0, broadcasted.Length);
                MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, offset: cycle.SafetyPeriodDuration);
                broadcasted = server.ClientContext.TrustedBroadcastService.TryBroadcast();
                Assert.Equal(1, broadcasted.Length);
                ////////////////////////////////////////////////////////////////////////////////

                //Tumbler Refund broadcasted exactly when we are at ClientCashoutPhase + SafetyPeriodDuration
                MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, end: true, offset: cycle.SafetyPeriodDuration - 1);
                broadcasted = server.ServerContext.TrustedBroadcastService.TryBroadcast();
                Assert.Equal(0, broadcasted.Length);
                MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, end: true, offset: cycle.SafetyPeriodDuration);
                broadcasted = server.ServerContext.TrustedBroadcastService.TryBroadcast();
                Assert.Equal(1, broadcasted.Length);
                ////////////////////////////////////////////////////////////////////////////////
            }
        }
        public void TestCRUDDBReeze()
        {
            using (var server = TumblerServerTester.Create())
            {
                var repo = server.ServerContext.GetService <TumblerServer.Services.IRepository>();
                repo.UpdateOrInsert("a", "b", "c", (o, n) => n);
                var result = repo.Get <string>("a", "b");
                Assert.Equal("c", result);
                repo.UpdateOrInsert("a", "b", "d", (o, n) => n);
                result = repo.Get <string>("a", "b");
                Assert.Equal("d", result);
                repo.UpdateOrInsert("a", "c", "c", (o, n) => n);
                Assert.Equal(2, repo.List <string>("a").Length);
                repo.Delete <string>("a", "c");
                Assert.Equal(1, repo.List <string>("a").Length);
                repo.UpdateOrInsert("a", "c", "c", (o, n) => n);
                repo.Delete("a");
                Assert.Equal(0, repo.List <string>("a").Length);

                var dbreezeRepo = (TumblerServer.Services.DBreezeRepository)repo;

                var    beforeOpened = dbreezeRepo.OpenedEngine;
                Random r            = new Random();
                for (int i = 0; i < 100; i++)
                {
                    var p = r.Next(0, 100);
                    repo.UpdateOrInsert(p.ToString(), "eh", "q", (a, b) => a);
                    Assert.True(dbreezeRepo.OpenedEngine <= dbreezeRepo.MaxOpenedEngine);
                }
            }
        }
 public void CanGetParameters()
 {
     using (var server = TumblerServerTester.Create())
     {
         var client     = server.CreateTumblerClient();
         var parameters = client.GetTumblerParameters();
         Assert.NotNull(parameters.ServerKey);
         Assert.NotEqual(0, parameters.RealTransactionCount);
         Assert.NotEqual(0, parameters.FakeTransactionCount);
         Assert.NotNull(parameters.FakeFormat);
         Assert.True(parameters.FakeFormat != uint256.Zero);
     }
 }
Beispiel #4
0
 public void TestCRUDDBReeze()
 {
     using (var server = TumblerServerTester.Create())
     {
         var repo = server.ServerContext.GetService <TumblerServer.Services.IRepository>();
         repo.UpdateOrInsert("a", "b", "c", (o, n) => n);
         var result = repo.Get <string>("a", "b");
         Assert.Equal("c", result);
         repo.UpdateOrInsert("a", "b", "d", (o, n) => n);
         result = repo.Get <string>("a", "b");
         Assert.Equal("d", result);
         repo.UpdateOrInsert("a", "c", "c", (o, n) => n);
         Assert.Equal(2, repo.List <string>("a").Length);
         repo.Delete <string>("a", "c");
         Assert.Equal(1, repo.List <string>("a").Length);
         repo.UpdateOrInsert("a", "c", "c", (o, n) => n);
         repo.Delete("a");
         Assert.Equal(0, repo.List <string>("a").Length);
     }
 }
        public void CanUseTracker()
        {
            using (var server = TumblerServerTester.Create())
            {
                var tracker = server.ServerRuntime.Tracker;

                var address  = new Key().ScriptPubKey;
                var address2 = new Key().ScriptPubKey;
                var address3 = new Key().ScriptPubKey;

                var h = new Transaction().GetHash();
                tracker.AddressCreated(1, TransactionType.ClientEscrow, address, CorrelationId.Zero);
                tracker.AddressCreated(1, TransactionType.ClientFulfill, address2, CorrelationId.Zero);
                tracker.TransactionCreated(1, TransactionType.ClientEscrow, h, CorrelationId.Zero);

                Assert.Empty(tracker.GetRecords(2));
                Assert.Equal(3, tracker.GetRecords(1).Length);

                Assert.NotNull(tracker.Search(address));
                Assert.True(tracker.Search(address3).Length == 0);
            }
        }
        public void CanGenerateAddress()
        {
            using (var server = TumblerServerTester.Create())
            {
                var key = new ExtKey().GetWif(Network.RegTest);
                var w   = new ClientDestinationWallet(key.Neuter(), new KeyPath("0/1"), server.ClientRuntime.Repository, server.ClientRuntime.Network);

                var k1 = w.GetNewDestination();
                var k2 = w.GetNewDestination();
                Assert.Equal(new KeyPath("0/1/0"), w.GetKeyPath(k1));
                Assert.Equal(new KeyPath("0/1/1"), w.GetKeyPath(k2));
                Assert.Null(w.GetKeyPath(new Key().ScriptPubKey));

                var wrpc = new RPCDestinationWallet(server.AliceNode.CreateRPCClient());

                k1 = wrpc.GetNewDestination();
                k2 = wrpc.GetNewDestination();

                Assert.Equal(new KeyPath("0'/0'/1'"), wrpc.GetKeyPath(k1));
                Assert.Equal(new KeyPath("0'/0'/2'"), wrpc.GetKeyPath(k2));
                Assert.Null(w.GetKeyPath(new Key().ScriptPubKey));
            }
        }
        public void CanCompleteCycleWithMachineState()
        {
            using (var server = TumblerServerTester.Create())
            {
                server.AliceNode.FindBlock(1);
                server.SyncNodes();
                server.TumblerNode.FindBlock(1);
                server.SyncNodes();
                server.BobNode.FindBlock(103);
                server.SyncNodes();

                var machine = server.ClientContext.PaymentMachineState;
                machine.Update();
                var cycle = machine.ClientChannelNegotiation.GetCycle();

                MineTo(server.AliceNode, cycle, CyclePhase.ClientChannelEstablishment);
                server.SyncNodes();


                var escrow1 = machine.AliceClient.RequestTumblerEscrowKey(machine.StartCycle);
                var escrow2 = machine.AliceClient.RequestTumblerEscrowKey(machine.StartCycle);
                Assert.Equal(0, escrow1.KeyIndex);
                Assert.Equal(1, escrow2.KeyIndex);
                machine.Update();

                //Wait the client escrow is confirmed
                server.AliceNode.FindBlock(2);
                server.SyncNodes();

                //Server does not track anything until Alice gives proof of the escrow
                Assert.Equal(0, server.ServerContext.BlockExplorer
                             .GetTransactions(machine.SolverClientSession.EscrowedCoin.ScriptPubKey, false)
                             .Count());

                machine.Update();

                //Server is now tracking Alice's escrow
                Assert.Equal(1, server.ServerContext.BlockExplorer
                             .GetTransactions(machine.SolverClientSession.EscrowedCoin.ScriptPubKey, false)
                             .Count());

                MineTo(server.AliceNode, cycle, CyclePhase.TumblerChannelEstablishment);
                server.SyncNodes();

                machine.Update();

                MineTo(server.TumblerNode, cycle, CyclePhase.PaymentPhase);
                server.SyncNodes();

                machine.Update();

                MineTo(server.TumblerNode, cycle, CyclePhase.PaymentPhase, true);
                server.SyncNodes();

                //Offer + Fulfill should be broadcasted
                var transactions = server.ServerContext.TrustedBroadcastService.TryBroadcast();
                Assert.Equal(2, transactions.Length);

                //Offer got malleated
                server.TumblerNode.Malleate(transactions[0].GetHash());
                var block = server.TumblerNode.FindBlock(1).First();
                Assert.Equal(2, block.Transactions.Count);                 //Offer get mined
                server.SyncNodes();

                //Fulfill get resigned and broadcasted
                transactions = server.ServerContext.TrustedBroadcastService.TryBroadcast();
                Assert.Equal(1, transactions.Length);
                block = server.TumblerNode.FindBlock(1).First();
                Assert.Equal(2, block.Transactions.Count);                 //Fulfill get mined

                MineTo(server.TumblerNode, cycle, CyclePhase.ClientCashoutPhase);
                server.SyncNodes();
                machine.Update();

                transactions = server.ClientContext.UntrustedBroadcaster.TryBroadcast();
                Assert.Equal(1, transactions.Length);
                block = server.AliceNode.FindBlock().First();
                //Should contains client cashout
                Assert.Equal(2, block.Transactions.Count);

                //Just a sanity tests, this one contains escrow redeem and offer redeem, both of which should not be available now
                transactions = server.ClientContext.UntrustedBroadcaster.TryBroadcast();
                Assert.Equal(0, transactions.Length);
            }
        }
        void EscrowGetRedeemedIfTimeoutCore(bool fulfill)
        {
            using (var server = TumblerServerTester.Create())
            {
                var machine = server.CreateStateMachine();
                machine.Update();
                var cycle = machine.ClientChannelNegotiation.GetCycle();

                server.MineTo(server.AliceNode, cycle, CyclePhase.ClientChannelEstablishment);


                machine.Update();

                //Wait the client escrow is confirmed
                server.AliceNode.FindBlock(2);
                server.SyncNodes();

                //Server does not track anything until Alice gives proof of the escrow
                Assert.Empty(server.ServerRuntime.Services.BlockExplorerService
                             .GetTransactionsAsync(machine.SolverClientSession.EscrowedCoin.ScriptPubKey, false)
                             .GetAwaiter().GetResult()
                             );

                machine.Update();

                WaitStatus(machine, PaymentStateMachineStatus.TumblerVoucherObtained);
                //Server is now tracking Alice's escrow
                Assert.Single(server.ServerRuntime.Services.BlockExplorerService
                              .GetTransactionsAsync(machine.SolverClientSession.EscrowedCoin.ScriptPubKey, false)
                              .GetAwaiter().GetResult()
                              );

                server.MineTo(server.AliceNode, cycle, CyclePhase.TumblerChannelEstablishment);
                machine.Update();

                Assert.Equal(PaymentStateMachineStatus.TumblerChannelCreating, machine.Status);

                //Wait escrow broadcasted
                Thread.Sleep(1000);

                //Make sure the tumbler escrow is broadcasted an mined
                var broadcasted = server.ServerRuntime.Services.BroadcastService.TryBroadcast();
                Assert.Single(broadcasted);
                server.ServerRuntime.Tracker.AssertKnown(TransactionType.TumblerEscrow, broadcasted[0].GetHash());

                machine.Update();
                Assert.Equal(PaymentStateMachineStatus.TumblerChannelCreated, machine.Status);

                server.MineTo(server.TumblerNode, cycle, CyclePhase.TumblerChannelEstablishment, end: true, offset: -1);
                machine.Update();

                if (!fulfill)
                {
                    //Client Refund broadcasted exactly when we are at ClientCashoutPhase + SafetyPeriodDuration
                    server.MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, offset: cycle.SafetyPeriodDuration - 1);
                    broadcasted = server.ClientRuntime.Services.TrustedBroadcastService.TryBroadcast();
                    Assert.Empty(broadcasted);
                    server.MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, offset: cycle.SafetyPeriodDuration);
                    broadcasted = server.ClientRuntime.Services.TrustedBroadcastService.TryBroadcast();
                    Assert.Single(broadcasted);
                    server.ClientRuntime.Tracker.AssertKnown(TransactionType.ClientRedeem, broadcasted[0].GetHash());
                    ////////////////////////////////////////////////////////////////////////////////

                    //Tumbler Refund broadcasted exactly when we are at end of ClientCashoutPhase + SafetyPeriodDuration
                    server.MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, end: true, offset: cycle.SafetyPeriodDuration - 1);
                    broadcasted = server.ServerRuntime.Services.TrustedBroadcastService.TryBroadcast();
                    Assert.Empty(broadcasted);
                    server.MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, end: true, offset: cycle.SafetyPeriodDuration);
                    broadcasted = server.ServerRuntime.Services.TrustedBroadcastService.TryBroadcast();
                    Assert.Single(broadcasted);
                    server.ServerRuntime.Tracker.AssertKnown(TransactionType.TumblerRedeem, broadcasted[0].GetHash());
                    ////////////////////////////////////////////////////////////////////////////////
                }
                else
                {
                    server.ServerRuntime.Cooperative = false;
                    server.ServerRuntime.NoFulFill   = true;
                    server.ClientRuntime.Cooperative = false;

                    //The tumbler plan offer for end of payment period, but not the fulfill
                    server.MineTo(server.AliceNode, cycle, CyclePhase.PaymentPhase);
                    machine.Update();
                    WaitStatus(machine, PaymentStateMachineStatus.UncooperativeTumbler);
                    //The tumbler should now broadcast the offer, but not the fulfill
                    server.MineTo(server.TumblerNode, cycle, CyclePhase.PaymentPhase, true);
                    broadcasted = server.ServerRuntime.Services.TrustedBroadcastService.TryBroadcast();
                    Assert.Single(broadcasted);
                    server.ServerRuntime.Tracker.AssertKnown(TransactionType.ClientOffer, broadcasted[0].GetHash());
                    server.TumblerNode.FindBlock(1);


                    //Client Offer Refund broadcasted exactly when we are at ClientCashoutPhase + SafetyPeriodDuration
                    server.MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, offset: cycle.SafetyPeriodDuration - 1);
                    broadcasted = server.ClientRuntime.Services.TrustedBroadcastService.TryBroadcast();
                    Assert.Empty(broadcasted);
                    server.MineTo(server.AliceNode, cycle, CyclePhase.ClientCashoutPhase, offset: cycle.SafetyPeriodDuration);
                    broadcasted = server.ClientRuntime.Services.TrustedBroadcastService.TryBroadcast();
                    Assert.Single(broadcasted);
                    server.ClientRuntime.Tracker.AssertKnown(TransactionType.ClientOfferRedeem, broadcasted[0].GetHash());
                    ////////////////////////////////////////////////////////////////////////////////
                }
            }
        }
 public void TestStandard()
 {
     using (var server = TumblerServerTester.Create("TestStandard", true))
     {
     }
 }
        private void CanCompleteCycleWithMachineStateCore(bool cooperativeClient, bool cooperativeTumbler)
        {
            using (var server = TumblerServerTester.Create())
            {
                server.ServerRuntime.Cooperative = cooperativeTumbler;
                server.ClientRuntime.Cooperative = cooperativeClient;

                var machine = server.CreateStateMachine();

                var serverTracker = server.ServerRuntime.Tracker;
                var clientTracker = machine.Tracker;


                machine.Update();
                var cycle = machine.ClientChannelNegotiation.GetCycle();

                server.MineTo(server.AliceNode, cycle, CyclePhase.ClientChannelEstablishment);

                var alice = machine.Runtime.CreateTumblerClient(machine.StartCycle, Identity.Alice);

                var escrow1 = alice.RequestTumblerEscrowKey();
                var escrow2 = alice.RequestTumblerEscrowKey();
                Assert.Equal(0, escrow1.KeyIndex);
                Assert.Equal(1, escrow2.KeyIndex);
                machine.Update();

                Assert.NotEqual(uint160.Zero, machine.SolverClientSession.Id);
                Assert.NotNull(machine.SolverClientSession.Id);
                clientTracker.AssertKnown(TransactionType.ClientEscrow, machine.SolverClientSession.EscrowedCoin.ScriptPubKey);
                clientTracker.AssertKnown(TransactionType.ClientEscrow, machine.SolverClientSession.EscrowedCoin.Outpoint.Hash);

                //Wait the client escrow is confirmed
                server.AliceNode.FindBlock(2);
                server.SyncNodes();

                //Server does not track anything until Alice gives proof of the escrow
                Assert.Empty(server.ServerRuntime.Services.BlockExplorerService
                             .GetTransactionsAsync(machine.SolverClientSession.EscrowedCoin.ScriptPubKey, false)
                             .GetAwaiter().GetResult()
                             );
                serverTracker.AssertNotKnown(machine.SolverClientSession.EscrowedCoin.ScriptPubKey);

                machine.Update();

                WaitStatus(machine, PaymentStateMachineStatus.TumblerVoucherObtained);
                //Server is now tracking Alice's escrow
                Assert.Single(server.ServerRuntime.Services.BlockExplorerService
                              .GetTransactionsAsync(machine.SolverClientSession.EscrowedCoin.ScriptPubKey, false)
                              .GetAwaiter().GetResult()
                              );
                serverTracker.AssertKnown(TransactionType.ClientEscrow, machine.SolverClientSession.EscrowedCoin.ScriptPubKey);
                serverTracker.AssertKnown(TransactionType.ClientEscrow, machine.SolverClientSession.EscrowedCoin.Outpoint.Hash);
                //

                server.MineTo(server.AliceNode, cycle, CyclePhase.TumblerChannelEstablishment);
                machine.Update();

                Assert.Equal(PaymentStateMachineStatus.TumblerChannelCreating, machine.Status);
                //Wait escrow broadcasted
                Thread.Sleep(1000);
                server.TumblerNode.Generate(1);
                machine.Update();
                Assert.Equal(PaymentStateMachineStatus.TumblerChannelCreated, machine.Status);

                Assert.NotEqual(uint160.Zero, machine.PromiseClientSession.Id);
                Assert.NotEqual(machine.SolverClientSession.Id, machine.PromiseClientSession.Id);
                Assert.NotNull(machine.PromiseClientSession.Id);
                serverTracker.AssertKnown(TransactionType.TumblerEscrow, machine.PromiseClientSession.EscrowedCoin.ScriptPubKey);
                serverTracker.AssertKnown(TransactionType.TumblerEscrow, machine.PromiseClientSession.EscrowedCoin.Outpoint.Hash);
                clientTracker.AssertKnown(TransactionType.TumblerEscrow, machine.PromiseClientSession.EscrowedCoin.ScriptPubKey);
                clientTracker.AssertKnown(TransactionType.TumblerEscrow, machine.PromiseClientSession.EscrowedCoin.Outpoint.Hash);

                server.MineTo(server.TumblerNode, cycle, CyclePhase.PaymentPhase);
                machine.Update();
                Assert.Equal(PaymentStateMachineStatus.ProcessingPayment, machine.Status);

                Block block = null;
                if (cooperativeTumbler)
                {
                    Assert.False(machine.ShouldStayConnected());
                    WaitStatus(machine, PaymentStateMachineStatus.PuzzleSolutionObtained);
                    Assert.True(machine.ShouldStayConnected());
                    if (cooperativeClient)
                    {
                        //Escape should be mined
                        Thread.Sleep(1000);
                        block = server.TumblerNode.FindBlock(1).First();
                        Assert.Equal(2, block.Transactions.Count);

                        serverTracker.AssertKnown(TransactionType.ClientEscape, block.Transactions[1].GetHash());
                        serverTracker.AssertKnown(TransactionType.ClientEscape, block.Transactions[1].Outputs[0].ScriptPubKey);
                    }
                }
                else
                {
                    if (!cooperativeTumbler)
                    {
                        WaitStatus(machine, PaymentStateMachineStatus.UncooperativeTumbler);
                        Assert.Equal(PaymentStateMachineStatus.UncooperativeTumbler, machine.Status);
                    }
                }

                server.MineTo(server.TumblerNode, cycle, CyclePhase.PaymentPhase, true);

                Transaction[] transactions = null;
                if (!cooperativeClient || !cooperativeTumbler)
                {
                    //Offer + Fulfill should be broadcasted
                    transactions = server.ServerRuntime.Services.TrustedBroadcastService.TryBroadcast();
                    Assert.Equal(2, transactions.Length);

                    //Sanity check if trusted broadcaster know about this
                    var knownTx = server.ServerRuntime.Services.TrustedBroadcastService.GetKnownTransaction(transactions[0].GetHash());
                    Assert.NotNull(knownTx);
                    Assert.Equal(transactions[0].GetHash(), knownTx.Transaction.GetHash());

                    var unmalleatedTransactions = transactions;

                    serverTracker.AssertKnown(TransactionType.ClientOffer, transactions[0].GetHash());
                    serverTracker.AssertKnown(TransactionType.ClientFulfill, transactions[1].GetHash());
                }

                server.MineTo(server.TumblerNode, cycle, CyclePhase.ClientCashoutPhase);

                machine.Update();

                if (cooperativeTumbler)
                {
                    //Received the solution out of blockchain, so the transaction should have been planned in advance
                    transactions = server.ClientRuntime.Services.TrustedBroadcastService.TryBroadcast();
                }
                else
                {
                    //Received the solution from the blockchain, the transaction has not been planned in advance
                    transactions = server.ClientRuntime.Services.BroadcastService.TryBroadcast();
                }
                Assert.Single(transactions);
                Assert.True(machine.ShouldStayConnected());
                block = server.AliceNode.FindBlock().First();
                //Should contains TumblerCashout
                Assert.Equal(2, block.Transactions.Count);
                //Not enough confirmation, should have as much as safe period
                Assert.True(machine.ShouldStayConnected());
                server.AliceNode.FindBlock().First();
                Assert.False(machine.ShouldStayConnected());

                clientTracker.AssertKnown(TransactionType.TumblerCashout, block.Transactions[1].GetHash());
                clientTracker.AssertKnown(TransactionType.TumblerCashout, block.Transactions[1].Outputs[0].ScriptPubKey);

                //Just a sanity tests, this one contains escrow redeem and offer redeem, both of which should not be available now
                transactions = server.ClientRuntime.Services.BroadcastService.TryBroadcast();
                Assert.Empty(transactions);


                var allTransactions = server.AliceNode.CreateNodeClient().GetBlocks().SelectMany(b => b.Transactions).ToDictionary(t => t.GetHash());
                var expectedRate    = new FeeRate(100, 1);

                foreach (var txId in new[]
                {
                    TransactionType.ClientEscape,
                    TransactionType.TumblerEscrow,
                    TransactionType.TumblerRedeem,
                    TransactionType.ClientOffer,
                    TransactionType.ClientFulfill,
                }.SelectMany(r => serverTracker.GetRecords(cycle.Start).Where(t => t.RecordType == RecordType.Transaction && t.TransactionType == r)))
                {
                    if (txId != null)
                    {
                        var tx = allTransactions.TryGet(txId.TransactionId) ??
                                 server.ServerRuntime.Services.TrustedBroadcastService.GetKnownTransaction(txId.TransactionId)?.Transaction ?? server.ServerRuntime.Services.BroadcastService.GetKnownTransaction(txId.TransactionId);
                        if (tx != null)
                        {
                            AssertRate(allTransactions, expectedRate, tx);
                        }
                    }
                }


                expectedRate = new FeeRate(50, 1);
                foreach (var txId in new[]
                {
                    TransactionType.ClientEscrow,
                    TransactionType.ClientRedeem,
                    TransactionType.ClientOfferRedeem,
                    TransactionType.TumblerCashout
                }.SelectMany(r => clientTracker.GetRecords(cycle.Start).Where(t => t.RecordType == RecordType.Transaction && t.TransactionType == r)))
                {
                    if (txId != null)
                    {
                        var tx = allTransactions.TryGet(txId.TransactionId) ??
                                 server.ClientRuntime.Services.TrustedBroadcastService.GetKnownTransaction(txId.TransactionId)?.Transaction ?? server.ClientRuntime.Services.BroadcastService.GetKnownTransaction(txId.TransactionId);
                        if (tx != null &&
                            tx.Inputs[0].PrevOut.Hash != uint256.Zero)                                 //Client offer redeem, as it is broadcasted to the trusted broadcaster before offer is known
                        {
                            AssertRate(allTransactions, expectedRate, tx);
                        }
                    }
                }
            }
        }