public async Task OrderQuantumTest(KeyPair clientKeyPair, KeyPair alphaKeyPair, ConnectionState state, Type excpectedException)
        {
            Global.AppState.State = ApplicationState.Ready;

            var clientConnection = new AuditorWebSocketConnection(new FakeWebSocket(), null)
            {
                ConnectionState = state
            };
            var account       = Global.AccountStorage.GetAccount(clientKeyPair);
            var orderEnvelope = new OrderRequest
            {
                Account = account?.Account.Id ?? 0
            }.CreateEnvelope();

            orderEnvelope.Sign(clientKeyPair);

            var orderQuantumEnvelope = new RequestQuantum
            {
                RequestEnvelope = orderEnvelope
            }.CreateEnvelope();

            orderQuantumEnvelope.Sign(alphaKeyPair);


            await AssertMessageHandling(clientConnection, orderQuantumEnvelope, excpectedException);
        }
        public async Task AccountDataRequestTest(int nonce, Type excpectedException)
        {
            context.AppState.State = ApplicationState.Ready;
            var accountWrapper = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);
            var order          = new AccountDataRequest
            {
                Account        = accountWrapper.Account.Id,
                RequestId      = nonce,
                AccountWrapper = accountWrapper
            };

            var envelope = order.CreateEnvelope();

            envelope.Sign(TestEnvironment.Client1KeyPair);

            if (!context.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope();
                envelope.Sign(TestEnvironment.AlphaKeyPair);
            }

            var res = await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException == null)
            {
                Assert.IsInstanceOf <Models.AccountDataResponse>(res);
            }
        }
        public async Task OrderQuantumTest(KeyPair clientKeyPair, KeyPair alphaKeyPair, ConnectionState state, Type excpectedException)
        {
            context.AppState.State = ApplicationState.Ready;

            var clientConnection = new AuditorWebSocketConnection(context, new FakeAuditorConnectionInfo(new FakeWebSocket()))
            {
                ConnectionState = state
            };
            var account       = context.AccountStorage.GetAccount(clientKeyPair);
            var orderEnvelope = new OrderRequest
            {
                Account = account?.Account.Id ?? 0
            }.CreateEnvelope().Sign(clientKeyPair);

            var orderQuantumEnvelope = new RequestQuantum
            {
                RequestEnvelope = orderEnvelope
            }.CreateEnvelope();

            orderQuantumEnvelope.Sign(alphaKeyPair);
            using var writer = new XdrBufferWriter();
            var inMessage = orderQuantumEnvelope.ToIncomingMessage(writer);


            await AssertMessageHandling(clientConnection, inMessage, excpectedException);
        }
 void ValidateAccountRequestSignature(RequestQuantum request)
 {
     if (!(request.RequestEnvelope.IsSignedBy(request.RequestMessage.AccountWrapper.Account.Pubkey) &&
           request.RequestEnvelope.AreSignaturesValid()))
     {
         throw new UnauthorizedAccessException("Request quantum has invalid signature.");
     }
 }
예제 #5
0
        /// <summary>
        /// Process customer's order request.
        /// </summary>
        /// <param name="orderRequest">Order request quantum</param>
        /// <returns></returns>
        public void ExecuteOrder(EffectProcessorsContainer effectsContainer)
        {
            RequestQuantum orderRequestQuantum = (RequestQuantum)effectsContainer.Envelope.Message;
            var            orderRequest        = (OrderRequest)orderRequestQuantum.RequestEnvelope.Message;
            var            updates             = new OrderMatcher(orderRequest, effectsContainer).Match();

            awaitedUpdates?.Add(updates);
        }
        void ValidateAccountRequestRate(RequestQuantum request)
        {
            var account = request.RequestMessage.AccountWrapper;

            if (!account.RequestCounter.IncRequestCount(request.Timestamp, out string error))
            {
                throw new TooManyRequestsException($"Request limit reached for account {account.Account.Pubkey}.");
            }
        }
예제 #7
0
        MessageEnvelope GetQuantumEnvelope(MessageEnvelope envelope)
        {
            var quantumEnvelope = envelope;

            if (Global.IsAlpha && !(envelope.Message is Quantum))//we need to wrap client request
            {
                quantumEnvelope = new RequestQuantum {
                    RequestEnvelope = envelope
                }
            }
        public async Task ScamQuantaTest(bool useFakeClient, bool useFakeAlpha, bool invalidBalance)
        {
            var environment = new IntegrationTestEnvironment();

            await environment.PrepareConstellation(3, 1);

            var clientPk      = environment.Clients.First();
            var client        = environment.AlphaWrapper.Context.AccountStorage.GetAccount(clientPk);
            var quantaStorage = environment.AlphaWrapper.Context.QuantumStorage;

            EnvironmentHelper.SetTestEnvironmentVariable();

            var amount = invalidBalance
                ? client.Account.Balances[0].Amount + 1
                : environment.AlphaWrapper.Context.Constellation.MinAllowedLotSize + 1;
            var sqamRequest = new OrderRequest
            {
                Account        = client.Id,
                AccountWrapper = client,
                Amount         = amount,
                Price          = 1,
                Asset          = 1,
                RequestId      = 1,
                Side           = OrderSide.Buy
            }.CreateEnvelope().Sign(useFakeClient ? KeyPair.Random() : clientPk);

            var apex           = quantaStorage.CurrentApex + 1;
            var requestQuantum = new RequestQuantum
            {
                Apex            = quantaStorage.CurrentApex + 1,
                EffectsHash     = new byte[] { },
                PrevHash        = quantaStorage.LastQuantumHash,
                RequestEnvelope = sqamRequest,
                Timestamp       = DateTime.UtcNow.Ticks
            }.CreateEnvelope().Sign(useFakeAlpha ? KeyPair.Random() : environment.AlphaWrapper.Settings.KeyPair);

            quantaStorage.AddQuantum(requestQuantum, requestQuantum.ComputeMessageHash());

            var expectedState = useFakeClient || useFakeAlpha || invalidBalance ? ApplicationState.Failed : ApplicationState.Ready;

            if (expectedState == ApplicationState.Ready)
            {
                await environment.AssertConstellationApex(apex, TimeSpan.FromSeconds(10));
            }

            await Task.WhenAll(environment.AuditorWrappers.Select(a => IntegrationTestEnvironmentExtensions.AssertState(a.Startup, expectedState, TimeSpan.FromSeconds(10))));
        }
예제 #9
0
        public void OrderbookPerformanceTest(int iterations, bool useNormalDistribution = false)
        {
            var rnd = new Random();

            var testTradeResults = new Dictionary <RequestQuantum, EffectProcessorsContainer>();

            for (var i = 1; i < iterations; i++)
            {
                var price          = useNormalDistribution ? rnd.NextNormallyDistributed() + 50 : rnd.NextDouble() * 100;
                var accountWrapper = context.AccountStorage.GetAccount(account1.Account.Pubkey);
                var trade          = new RequestQuantum
                {
                    Apex            = i,
                    RequestEnvelope = new MessageEnvelope
                    {
                        Message = new OrderRequest
                        {
                            Account        = accountWrapper.Account.Id,
                            RequestId      = i,
                            Amount         = rnd.Next(1, 20),
                            Asset          = 1,
                            Price          = Math.Round(price * 10) / 10,
                            Side           = rnd.NextDouble() >= 0.5 ? OrderSide.Buy : OrderSide.Sell,
                            AccountWrapper = context.AccountStorage.GetAccount(account1.Account.Pubkey)
                        },
                        Signatures = new List <Ed25519Signature>()
                    },
                    Timestamp = DateTime.UtcNow.Ticks
                };
                var diffObject = new DiffObject();
                var conterOrderEffectsContainer = new EffectProcessorsContainer(context, trade.CreateEnvelope(), diffObject);
                testTradeResults.Add(trade, conterOrderEffectsContainer);
            }

            PerfCounter.MeasureTime(() =>
            {
                foreach (var trade in testTradeResults)
                {
                    context.Exchange.ExecuteOrder(trade.Value);
                }
            }, () =>
            {
                var market = context.Exchange.GetMarket(1);
                return($"{iterations} iterations, orderbook size: {market.Bids.Count} bids,  {market.Asks.Count} asks, {market.Bids.GetBestPrice().ToString("G3")}/{market.Asks.GetBestPrice().ToString("G3")} spread.");
            });
        }
        public async Task AccountRequestRateLimitTest(KeyPair clientKeyPair, int?requestLimit)
        {
            context.AppState.State = ApplicationState.Ready;

            var account = context.AccountStorage.GetAccount(clientKeyPair);

            if (requestLimit.HasValue)
            {
                account.Account.RequestRateLimits = new RequestRateLimits {
                    HourLimit = (uint)requestLimit.Value, MinuteLimit = (uint)requestLimit.Value
                }
            }
            ;

            var minuteLimit     = (account.Account.RequestRateLimits ?? context.Constellation.RequestRateLimits).MinuteLimit;
            var minuteIterCount = minuteLimit + 1;

            for (var i = 0; i < minuteIterCount; i++)
            {
                var envelope = new AccountDataRequest
                {
                    Account        = account.Account.Id,
                    RequestId      = i + 1,
                    AccountWrapper = account
                }.CreateEnvelope();
                envelope.Sign(clientKeyPair);
                if (!context.IsAlpha)
                {
                    var quantum = new RequestQuantum {
                        Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                    };
                    envelope = quantum.CreateEnvelope();
                    envelope.Sign(TestEnvironment.AlphaKeyPair);
                }

                if (i + 1 > minuteLimit)
                {
                    await AssertQuantumHandling(envelope, typeof(TooManyRequestsException));
                }
                else
                {
                    await AssertQuantumHandling(envelope, null);
                }
            }
        }
예제 #11
0
        public async Task WithdrawalQuantumTest(double amount, bool hasWithdrawal, bool useFakeSigner, Type excpectedException)
        {
            var outputStream = new XdrDataOutputStream();
            var txBuilder    = new TransactionBuilder(new AccountResponse(TestEnvironment.Client1KeyPair.AccountId, 1));

            txBuilder.SetFee(10_000);

            txBuilder.AddOperation(new PaymentOperation.Builder(TestEnvironment.Client1KeyPair, new AssetTypeNative(), (amount / AssetsHelper.StroopsPerAsset).ToString("0.##########", CultureInfo.InvariantCulture)).SetSourceAccount(TestEnvironment.AlphaKeyPair).Build());
            txBuilder.AddTimeBounds(new stellar_dotnet_sdk.TimeBounds(maxTime: DateTimeOffset.UtcNow.AddSeconds(60)));
            var tx = txBuilder.Build();

            stellar_dotnet_sdk.xdr.Transaction.Encode(outputStream, tx.ToXdrV1());

            var account    = Global.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);
            var withdrawal = new WithdrawalRequest
            {
                Account        = account.Account.Id,
                RequestId      = 1,
                TransactionXdr = outputStream.ToArray(),
                AccountWrapper = account
            };

            var envelope = withdrawal.CreateEnvelope();

            envelope.Sign(useFakeSigner ? TestEnvironment.Client2KeyPair : TestEnvironment.Client1KeyPair);

            if (!Global.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = Global.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope();
                envelope.Sign(TestEnvironment.AlphaKeyPair);
            }

            var result = await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException == null)
            {
                Assert.IsTrue(account.HasPendingWithdrawal);

                Assert.IsTrue(account.Account.GetBalance(0).Liabilities == amount);
            }
        }
        public async Task OrderQuantumTest(int nonce, int asset, int amount, OrderSide side, Type excpectedException)
        {
            var accountWrapper = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);
            var order          = new OrderRequest
            {
                Account        = accountWrapper.Account.Id,
                RequestId      = nonce,
                Amount         = amount,
                Asset          = asset,
                Price          = 100,
                Side           = side,
                AccountWrapper = accountWrapper
            };

            var envelope = order.CreateEnvelope();

            envelope.Sign(TestEnvironment.Client1KeyPair);

            if (!context.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope();
                envelope.Sign(TestEnvironment.AlphaKeyPair);
            }

            var res = await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException == null)
            {
                var currentMarket = context.Exchange.GetMarket(asset);
                Assert.IsTrue(currentMarket != null);

                var requests = side == OrderSide.Buy ? currentMarket.Bids : currentMarket.Asks;
                Assert.AreEqual(1, requests.Count);
            }
        }
        public async Task PaymentQuantumTest(long amount, bool useFakeSigner, Type excpectedException)
        {
            var account = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);

            var withdrawal = new PaymentRequest
            {
                Account        = account.Account.Id,
                RequestId      = 1,
                Asset          = 0,
                Destination    = TestEnvironment.Client2KeyPair,
                Amount         = amount,
                AccountWrapper = account
            };

            var envelope = withdrawal.CreateEnvelope();

            envelope.Sign(useFakeSigner ? TestEnvironment.Client2KeyPair : TestEnvironment.Client1KeyPair);

            if (!context.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope();
                envelope.Sign(TestEnvironment.AlphaKeyPair);
            }

            var expextedBalance = account.Account.GetBalance(0).Amount - amount;

            await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException == null)
            {
                Assert.AreEqual(account.Account.GetBalance(0).Amount, expextedBalance);
            }
        }
        public async Task OrderCancellationQuantumTest(int asset, int amount, OrderSide side, int apexMod, bool useFakeSigner, Type excpectedException)
        {
            var acc = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);

            var order = new OrderRequest
            {
                Account        = acc.Account.Id,
                RequestId      = 1,
                Amount         = amount,
                Asset          = asset,
                Price          = 100,
                Side           = side,
                AccountWrapper = acc
            };

            var envelope = order.CreateEnvelope().Sign(useFakeSigner ? TestEnvironment.Client2KeyPair : TestEnvironment.Client1KeyPair);

            if (!context.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope().Sign(TestEnvironment.AlphaKeyPair);
            }

            var submitResult = await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException != null)
            {
                return;
            }

            var apex = ((Quantum)submitResult.OriginalMessage.Message).Apex + apexMod;

            var orderCancellation = new OrderCancellationRequest
            {
                Account        = acc.Account.Id,
                RequestId      = 2,
                OrderId        = OrderIdConverter.Encode((ulong)apex, asset, side),
                AccountWrapper = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair)
            };

            envelope = orderCancellation.CreateEnvelope().Sign(TestEnvironment.Client1KeyPair);

            if (!context.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope().Sign(TestEnvironment.AlphaKeyPair);
            }

            var cancelResult = await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException != null)
            {
                return;
            }
            var currentMarket = context.Exchange.GetMarket(asset);

            Assert.IsTrue(currentMarket != null);

            var requests = side == OrderSide.Buy ? currentMarket.Bids : currentMarket.Asks;

            Assert.AreEqual(requests.Count, 0);
        }
        public async Task TxCommitQuantumTest(int cursor, int amount, int asset, Type excpectedException)
        {
            context.AppState.State = ApplicationState.Ready;

            long apex = context.QuantumStorage.CurrentApex;

            var client1StartBalanceAmount = (long)0;

            var account1 = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair).Account;

            var clientAccountBalance = account1.GetBalance(asset);

            var withdrawalDest = KeyPair.Random();
            var txHash         = new byte[] { };

            if (clientAccountBalance != null && amount > 0)
            {
                client1StartBalanceAmount = clientAccountBalance.Amount;


                context.Constellation.TryFindAssetSettings(asset, out var assetSettings);

                var account   = new stellar_dotnet_sdk.Account(TestEnvironment.Client1KeyPair.AccountId, 1);
                var txBuilder = new TransactionBuilder(account);
                txBuilder.SetFee(10_000);
                txBuilder.AddTimeBounds(new stellar_dotnet_sdk.TimeBounds(DateTimeOffset.UtcNow, new TimeSpan(0, 5, 0)));
                txBuilder.AddOperation(
                    new PaymentOperation.Builder(withdrawalDest, assetSettings.ToAsset(), Amount.FromXdr(amount).ToString())
                    .SetSourceAccount((KeyPair)context.Constellation.Vault)
                    .Build()
                    );
                var tx = txBuilder.Build();
                txHash = tx.Hash();

                var txV1     = tx.ToXdrV1();
                var txStream = new XdrDataOutputStream();
                stellar_dotnet_sdk.xdr.Transaction.Encode(txStream, txV1);

                var accountWrapper = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);

                var withdrawal = new WithdrawalRequest
                {
                    Account        = accountWrapper.Account.Id,
                    TransactionXdr = txStream.ToArray(),
                    RequestId      = DateTime.UtcNow.Ticks,
                    AccountWrapper = accountWrapper
                };

                MessageEnvelope quantum = withdrawal.CreateEnvelope();
                quantum.Sign(TestEnvironment.Client1KeyPair);
                if (!context.IsAlpha)
                {
                    quantum = new RequestQuantum {
                        Apex = ++apex, RequestEnvelope = quantum, Timestamp = DateTime.UtcNow.Ticks
                    }.CreateEnvelope();
                    quantum.Sign(TestEnvironment.AlphaKeyPair);
                }
                //create withdrawal
                await context.QuantumHandler.HandleAsync(quantum);
            }

            var depositAmount = new Random().Next(10, 1000);

            var ledgerNotification = new TxNotification
            {
                TxCursor = (uint)cursor,
                Payments = new List <PaymentBase>
                {
                    new Deposit
                    {
                        Amount      = depositAmount,
                        Destination = TestEnvironment.Client1KeyPair,
                        Asset       = asset
                    },
                    new Withdrawal
                    {
                        TransactionHash = txHash,
                        PaymentResult   = PaymentResults.Success
                    }
                }
            };
            var ledgerNotificationEnvelope = ledgerNotification.CreateEnvelope();

            ledgerNotificationEnvelope.Sign(TestEnvironment.Auditor1KeyPair);

            var ledgerCommitEnv = new TxCommitQuantum
            {
                Source = ledgerNotificationEnvelope,
                Apex   = ++apex
            }.CreateEnvelope();

            if (!context.IsAlpha)
            {
                var msg = ((TxCommitQuantum)ledgerCommitEnv.Message);
                msg.Timestamp   = DateTime.UtcNow.Ticks;
                ledgerCommitEnv = msg.CreateEnvelope().Sign(TestEnvironment.AlphaKeyPair);
            }

            await AssertQuantumHandling(ledgerCommitEnv, excpectedException);

            if (excpectedException == null)
            {
                Assert.AreEqual(context.TxCursorManager.TxCursor, ledgerNotification.TxCursor);

                Assert.AreEqual(account1.GetBalance(asset).Liabilities, 0);
                Assert.AreEqual(account1.GetBalance(asset).Amount, client1StartBalanceAmount - amount + depositAmount); //acc balance + deposit - withdrawal
            }
        }
예제 #16
0
        private void ExecuteWithOrderbook(int iterations, bool useNormalDistribution, Action <Action> executor)
        {
            var rnd = new Random();

            var market = Global.Exchange.GetMarket(1);

            var testTradeResults = new Dictionary <RequestQuantum, EffectProcessorsContainer>();

            for (var i = 1; i < iterations; i++)
            {
                var price   = useNormalDistribution ? rnd.NextNormallyDistributed() + 50 : rnd.NextDouble() * 100;
                var request = new OrderRequest
                {
                    RequestId = i,
                    Amount    = rnd.Next(1, 20),
                    Asset     = 1,
                    Price     = Math.Round(price * 27) / 13
                };
                if (rnd.NextDouble() >= 0.5)
                {
                    request.Account        = account1.Id;
                    request.AccountWrapper = account1;
                    request.Side           = OrderSide.Buy;
                }
                else
                {
                    request.Account        = account2.Id;
                    request.AccountWrapper = account2;
                    request.Side           = OrderSide.Sell;
                }

                var trade = new RequestQuantum
                {
                    Apex            = i,
                    RequestEnvelope = new MessageEnvelope
                    {
                        Message    = request,
                        Signatures = new List <Ed25519Signature>()
                    },
                    Timestamp = DateTime.UtcNow.Ticks
                };
                var diffObject = new DiffObject();
                var conterOrderEffectsContainer = new EffectProcessorsContainer(trade.CreateEnvelope(), diffObject);
                testTradeResults.Add(trade, conterOrderEffectsContainer);
            }

            var xlmStartBalance   = account1.Account.GetBalance(0).Amount + account2.Account.GetBalance(0).Amount;
            var assetStartBalance = account1.Account.GetBalance(1).Amount + account2.Account.GetBalance(1).Amount;

            executor(() =>
            {
                foreach (var trade in testTradeResults)
                {
                    Global.Exchange.ExecuteOrder(trade.Value);
                }
            });

            //cleanup orders
            foreach (var account in new[] { account1, account2 })
            {
                var activeOrders = Global.Exchange.OrderMap.GetAllAccountOrders(account);
                foreach (var order in activeOrders)
                {
                    var decodedOrderId = OrderIdConverter.Decode(order.OrderId);

                    new OrderRemovedEffectProccessor(new OrderRemovedEffect
                    {
                        OrderId        = order.OrderId,
                        Amount         = order.Amount,
                        QuoteAmount    = order.QuoteAmount,
                        Price          = order.Price,
                        Asset          = decodedOrderId.Asset,
                        AccountWrapper = account
                    }, market.GetOrderbook(decodedOrderId.Side)).CommitEffect();
                }
            }
            Assert.AreEqual(xlmStartBalance, account1.Account.GetBalance(0).Amount + account2.Account.GetBalance(0).Amount);
            Assert.AreEqual(assetStartBalance, account1.Account.GetBalance(1).Amount + account2.Account.GetBalance(1).Amount);
            Assert.AreEqual(0, account1.Account.GetBalance(0).Liabilities);
            Assert.AreEqual(0, account1.Account.GetBalance(1).Liabilities);
            Assert.AreEqual(0, account2.Account.GetBalance(0).Liabilities);
            Assert.AreEqual(0, account2.Account.GetBalance(1).Liabilities);
        }
        public async Task WithdrawalCleanupQuantumTest(double amount, bool useFakeHash, Type excpectedException)
        {
            var outputStream = new XdrDataOutputStream();
            var txBuilder    = new TransactionBuilder(new AccountResponse(TestEnvironment.Client1KeyPair.AccountId, 1));

            txBuilder.SetFee(10_000);

            txBuilder.AddOperation(new PaymentOperation.Builder(TestEnvironment.Client1KeyPair, new AssetTypeNative(), (amount / AssetsHelper.StroopsPerAsset).ToString("0.##########", CultureInfo.InvariantCulture)).SetSourceAccount(TestEnvironment.AlphaKeyPair).Build());
            txBuilder.AddTimeBounds(new stellar_dotnet_sdk.TimeBounds(maxTime: DateTimeOffset.UtcNow.AddSeconds(60)));
            var tx = txBuilder.Build();

            stellar_dotnet_sdk.xdr.Transaction.Encode(outputStream, tx.ToXdrV1());

            var account = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);

            var acc        = context.AccountStorage.GetAccount(TestEnvironment.Client1KeyPair);
            var withdrawal = new WithdrawalRequest
            {
                Account        = acc.Account.Id,
                RequestId      = 1,
                TransactionXdr = outputStream.ToArray(),
                AccountWrapper = account
            };

            var envelope = withdrawal.CreateEnvelope();

            envelope.Sign(TestEnvironment.Client1KeyPair);

            if (!context.IsAlpha)
            {
                var quantum = new RequestQuantum {
                    Apex = context.QuantumStorage.CurrentApex + 1, RequestEnvelope = envelope, Timestamp = DateTime.UtcNow.Ticks
                };
                envelope = quantum.CreateEnvelope();
                envelope.Sign(TestEnvironment.AlphaKeyPair);
            }

            var result = await AssertQuantumHandling(envelope, null);

            if (result.Status != ResultStatusCodes.Success)
            {
                throw new Exception("Withdrawal creation failed.");
            }

            var cleanup = new WithrawalsCleanupQuantum
            {
                ExpiredWithdrawal = useFakeHash ? new byte[] { } : tx.Hash(),
                Apex = context.QuantumStorage.CurrentApex + 1
            };

            envelope = cleanup.CreateEnvelope();


            if (!context.IsAlpha)
            {
                cleanup.Timestamp = DateTime.UtcNow.Ticks;
                envelope          = cleanup.CreateEnvelope();
                envelope.Sign(TestEnvironment.AlphaKeyPair);
            }

            await AssertQuantumHandling(envelope, excpectedException);

            if (excpectedException == null)
            {
                Assert.IsTrue(!account.HasPendingWithdrawal);

                Assert.AreEqual(account.Account.GetBalance(0).Liabilities, 0);
            }
        }
        public async Task AlphaRestartWithQuantaDelayTest(bool invalidHash, bool invalidClientSignature, bool invalidAlphaSignature)
        {
            var environment = new IntegrationTestEnvironment();

            await environment.PrepareConstellation(1, 3);

            var lastApex = environment.AlphaWrapper.Context.QuantumStorage.CurrentApex;
            var lastHash = environment.AlphaWrapper.Context.QuantumStorage.LastQuantumHash;

            var clientPk = environment.Clients.First();
            var client   = environment.AlphaWrapper.Context.AccountStorage.GetAccount(clientPk);

            //wait while all auditors will process all available quanta
            await environment.AssertConstellationApex(lastApex, TimeSpan.FromSeconds(5));

            //generate quantum that will not be processed by Alpha
            var request = new AccountDataRequest
            {
                Account        = client.Id,
                RequestId      = DateTime.UtcNow.Ticks,
                AccountWrapper = client
            }
            .CreateEnvelope()
            .Sign(clientPk);

            var quantum = new RequestQuantum
            {
                Apex            = lastApex + 1,
                PrevHash        = lastHash,
                RequestEnvelope = request,
                Timestamp       = DateTime.UtcNow.Ticks
            };
            var quantumEnvelope = quantum
                                  .CreateEnvelope();

            var result = await environment.ProcessQuantumIsolated(quantumEnvelope);

            quantum.EffectsHash = result.effectsHash;
            quantumEnvelope.Sign(environment.AlphaWrapper.Settings.KeyPair);

            await environment.AlphaWrapper.Shutdown();

            await Task.WhenAll(environment.AuditorWrappers.Select(a => IntegrationTestEnvironmentExtensions.AssertState(a.Startup, ApplicationState.Running, TimeSpan.FromSeconds(10))));

            //handle quantum
            await Task.WhenAll(environment.AuditorWrappers.Select(a =>
            {
                var rawQuantum      = quantumEnvelope.ToByteArray();
                var auditorsQuantum = XdrConverter.Deserialize <MessageEnvelope>(rawQuantum);
                return(a.Context.QuantumHandler.HandleAsync(auditorsQuantum));
            }));

            //change quantum
            environment.AuditorWrappers.ForEach(a =>
            {
                a.Context.QuantumStorage.GetQuantaBacth(lastApex + 1, 1, out var quanta);
                var quantum = quanta.First();
                if (invalidHash)
                {
                    ((Quantum)quantum.Message).Timestamp = DateTime.UtcNow.Ticks;
                }
                if (invalidClientSignature)
                {
                    var request = (RequestQuantum)quantum.Message;
                    request.RequestEnvelope.Signatures.Clear();
                    request.RequestEnvelope.Sign(KeyPair.Random());
                }
                if (invalidAlphaSignature)
                {
                    quantum.Signatures.Clear();
                    quantum.Sign(KeyPair.Random());
                }
            });

            await environment.AlphaWrapper.Run();

            var expectedState = invalidHash || invalidClientSignature || invalidAlphaSignature ? ApplicationState.Failed : ApplicationState.Ready;

            await IntegrationTestEnvironmentExtensions.AssertState(environment.AlphaWrapper.Startup, expectedState, TimeSpan.FromSeconds(30));

            if (expectedState == ApplicationState.Failed)
            {
                return;
            }

            await Task.WhenAll(environment.AuditorWrappers.Select(a => IntegrationTestEnvironmentExtensions.AssertState(a.Startup, ApplicationState.Ready, TimeSpan.FromSeconds(10))));

            await environment.AssertConstellationApex(lastApex + 1, TimeSpan.FromSeconds(5));
        }