예제 #1
0
        public async Task Engine_TimeoutTransfer()
        {
            await Engine.DefaultIdentity.WhenInitialized;

            decimal transferAmount = 1;

            var source = Engine.DefaultIdentity;

            await CreateDestinationAccount(transferAmount, source);

            var destination = source.Accounts[1];

            // Transfer
            OperationTaskflow flow = await Engine.CommitTransfer(source, destination, transferAmount);

            await flow.WhenAcknowledged;

            Simulation.Timeout(flow.Task);

            await flow.WhenCompleted;

            await WhenMessagesDelivered();

            // Destination
            Assert.IsFalse(destination.HasPendingChanges);

            // Source
            Assert.IsFalse(source.HasPendingChanges);
        }
예제 #2
0
        public async Task Monitor_NoTimeout()
        {
            var connection = new TestConnection();
            var task       = new OperationTask();
            var flow       = new OperationTaskflow(task);

            List <OperationEvent> received = new List <OperationEvent>();

            void callback(OperationEvent operationEvent)
            {
                received.Add(operationEvent);
            }

            var monitor = new OperationMonitor(
                flow,
                TimeSpan.FromMilliseconds(200),
                TimeSpan.FromMilliseconds(200),
                connection,
                callback);

            // Time to start monitoring
            await Task.Delay(100);

            monitor.Update(TaskProgress.Acknowledged);

            await Task.Delay(100);

            flow.Update(TaskProgress.Confirmed);

            await Task.Delay(300);

            Assert.AreEqual(0, received.Count);
            Assert.IsTrue(monitor.IsComplete);
        }
예제 #3
0
        public async Task <OperationTaskflow> ActivateIdentity(Identity identity, string secret, decimal expectedAmount)
        {
            Trace("Activate Identity");

            var task = new ActivateIdentityTask
            {
                SourceID = identity.AccountID,
                Amount   = expectedAmount,
                Secret   = secret,
            };

            var flow = new OperationTaskflow(task);

            try
            {
                flow.Task = await Connection.ActivateIdentity(task);

                flow.SetPending(this);

                Trace($"{identity.AccountID} | Waiting for activation");
            }
            catch
            {
                flow.Update(TaskProgress.Failed);
            }

            return(flow);
        }
예제 #4
0
        public void Monitor_AcknowledgeLost()
        {
            var connection = new TestConnection();
            var task       = new OperationTask();
            var flow       = new OperationTaskflow(task);

            AutoResetEvent signal = new AutoResetEvent(false);

            void callback(OperationEvent operationEvent)
            {
                signal.Set();
            }

            connection.NextStatus = new OperationStatus
            {
                Events     = new[] { new OperationEvent() },
                RetryAfter = TimeSpan.FromMilliseconds(100)
            };

            var monitor = new OperationMonitor(
                flow,
                TimeSpan.FromMilliseconds(100),
                TimeSpan.FromMilliseconds(2000),
                connection,
                callback);

            Assert.IsTrue(signal.WaitOne(200));
            Assert.IsTrue(signal.WaitOne(200));
            Assert.IsTrue(signal.WaitOne(200));

            monitor.Update(TaskProgress.Confirmed);

            Assert.IsFalse(signal.WaitOne(200));

            Assert.IsTrue(monitor.IsComplete);
        }
예제 #5
0
        private async Task TestTransfer(bool testDestination = true)
        {
            await Engine.DefaultIdentity.WhenInitialized;

            decimal transferAmount = 2;
            decimal networkFee     = Engine.DefaultOperationFee;

            var source = Engine.DefaultIdentity;

            await CreateDestinationAccount(transferAmount, source);

            var destination = source.Accounts[1];

            // Transfer
            decimal expectedSourceBalance      = source.Balance - transferAmount - networkFee;
            decimal expectedDestinationBalance = destination.Balance + transferAmount;

            OperationTaskflow flow = await Engine.CommitTransfer(source, destination, transferAmount);

            await flow.WhenAcknowledged;

            await WhenMessagesDelivered();

            // Source
            Assert.AreEqual(1, source.PendingChanges.Count());
            Assert.IsTrue(source.HasPendingChanges);
            Assert.IsTrue(destination.Entries.Count() == 1);

            var pending = source.PendingChanges[0];

            Assert.AreEqual(TokenStore.ChangeTopic.PendingTransfer, pending.Topic);
            Assert.AreEqual(-transferAmount - networkFee, pending.Amount);

            if (testDestination)
            {
                // Destination
                Assert.AreEqual(1, destination.PendingChanges.Count());
                Assert.IsTrue(destination.HasPendingChanges);
                Assert.AreEqual(1, destination.Entries.Count());

                pending = destination.PendingChanges[0];
                Assert.AreEqual(TokenStore.ChangeTopic.PendingTransfer, pending.Topic);
                Assert.AreEqual(transferAmount, pending.Amount);
            }

            Simulation.CreateBlock();

            await flow.WhenCompleted;

            await WhenMessagesDelivered();

            // Source
            Assert.AreEqual(expectedSourceBalance, source.Balance);

            Assert.AreEqual(0, source.PendingChanges.Count());
            Assert.IsFalse(source.HasPendingChanges);

            Assert.IsNotNull(source.Entries);
            Assert.AreEqual(2, source.Entries.Count());

            var entry = source.Entries[1];

            Assert.AreEqual(expectedSourceBalance, entry.Balance);

            Assert.IsNotNull(entry.Items);
            Assert.AreEqual(1, entry.Items.Count());

            var item = entry.Items[0];

            Assert.AreEqual(AccountEntryItemKind.Transfer, item.Kind);
            Assert.AreEqual(-transferAmount, item.Amount);

            if (testDestination)
            {
                // Destination
                Assert.AreEqual(expectedDestinationBalance, destination.Balance);

                Assert.AreEqual(0, destination.PendingChanges.Count());
                Assert.IsFalse(destination.HasPendingChanges);

                Assert.IsNotNull(destination.Entries);
                Assert.AreEqual(2, destination.Entries.Count());

                entry = destination.Entries[1];

                Assert.AreEqual(expectedDestinationBalance, entry.Balance);

                Assert.IsNotNull(entry.Items);
                Assert.AreEqual(1, entry.Items.Count());

                item = entry.Items[0];

                Assert.AreEqual(AccountEntryItemKind.Transfer, item.Kind);
                Assert.AreEqual(transferAmount, item.Amount);
            }
        }
예제 #6
0
        private void OnNetworkEvent(NetworkEvent networkEvent)
        {
            switch (networkEvent)
            {
            case OriginatePendingEvent originate:
            {
                var identity = Identities
                               .FirstOrDefault(i => i.AccountID == originate.ManagerID);

                if (identity != null)
                {
                    var found = identity.Accounts
                                .Where(a => a.AccountID == originate.AccountID)
                                .FirstOrDefault();

                    if (found == null)
                    {
                        var account = new Account(originate.Name, originate.AccountID)
                        {
                            Stereotype = originate.Stereotype,
                            DelegateID = originate.DelegateID,
                        };

                        identity.ExpectOrigination(
                            account,
                            originate.OperationID,
                            originate.ContraAccountID,
                            originate.Amount);

                        OperationTaskflow.Update(originate.OperationID, TaskProgress.Acknowledged);
                    }
                }
            }
            break;

            case OriginateEvent originate:
            {
                if (IsTooOld(originate.BlockIndex))
                {
                    return;
                }

                if (accounts.ContainsKey(originate.AccountID))
                {
                    return;
                }

                var identity = Identities
                               .FirstOrDefault(i => i.AccountID == originate.ManagerID);

                if (identity != null)
                {
                    if (identity.Accounts
                        .FirstOrDefault(a => a.AccountID == originate.AccountID) is Account account)
                    {
                        // OriginatePendingEvent received before
                        account.CloseOperation(originate.OperationID, originate.Entry);
                    }
                    else
                    {
                        // switched on later
                        account = new Account(originate.Name, originate.AccountID)
                        {
                            Stereotype = originate.Stereotype,
                            DelegateID = originate.DelegateID,
                        };

                        identity.AddAccount(account);

                        account.AddEntry(originate.Entry);
                    }

                    account.Balance = originate.Balance;

                    account.State = TokenStoreState.Online;

                    Cache(account);

                    OperationTaskflow.Update(originate.OperationID, TaskProgress.Confirmed);
                }
            }
            break;

            case OriginationTimeoutEvent opTimeout:
            {
                var identity = Identities
                               .FirstOrDefault(i => i.AccountID == opTimeout.ManagerID);

                if (identity != null)
                {
                    if (identity.Accounts
                        .FirstOrDefault(a => a.AccountID == opTimeout.AccountID) is Account account)
                    {
                        account.CloseOperation(opTimeout.OperationID);
                        account.State = TokenStoreState.UnheardOf;
                    }

                    OperationTaskflow.Update(opTimeout.OperationID, TaskProgress.Timeout);
                }
            }
            break;

            case BalanceChangedEvent changeBalance:
            {
                if (IsTooOld(changeBalance.BlockIndex))
                {
                    return;
                }

                if (accounts.TryGetValue(changeBalance.AccountID, out TokenStore account))
                {
                    account.Balance = changeBalance.Balance;
                    account.UpdateState(changeBalance.State);
                    account.CloseOperation(changeBalance.OperationID, changeBalance.Entry);
                }

                OperationTaskflow.Update(changeBalance.OperationID, TaskProgress.Confirmed);
            }
            break;

            case TransactionPendingEvent transactionPending:
            {
                if (accounts.TryGetValue(transactionPending.AccountID, out TokenStore account))
                {
                    if (account.ExpectOperation(
                            transactionPending.OperationID,
                            transactionPending.ContraAccountID,
                            transactionPending.Amount))
                    {
                        account.State = TokenStoreState.Changing;

                        OperationTaskflow.Update(transactionPending.OperationID, TaskProgress.Acknowledged);
                    }
                }
            }
            break;

            case TransactionTimeoutEvent opTimeout:
            {
                Debug.Assert(opTimeout.AccountID != null);

                if (accounts.TryGetValue(opTimeout.AccountID, out TokenStore account))
                {
                    account.CloseOperation(opTimeout.OperationID);

                    OperationTaskflow.Update(opTimeout.OperationID, TaskProgress.Timeout);
                }
            }
            break;

            case ActivationPendingEvent activationPending:
            {
                if (accounts.TryGetValue(activationPending.IdentityID, out TokenStore account))
                {
                    if (account.ExpectOperation(
                            activationPending.OperationID,
                            null,
                            activationPending.Amount))
                    {
                        account.State = TokenStoreState.Changing;

                        OperationTaskflow.Update(activationPending.OperationID, TaskProgress.Acknowledged);
                    }
                }
            }
            break;

            case ActivationTimeoutEvent opTimeout:
            {
                Debug.Assert(opTimeout.IdentityID != null);

                if (accounts.TryGetValue(opTimeout.IdentityID, out TokenStore account))
                {
                    account.CloseOperation(opTimeout.OperationID);

                    OperationTaskflow.Update(opTimeout.OperationID, TaskProgress.Timeout);
                }
            }
            break;

            case ServiceEvent svc:
            {
                OnServiceEvent(svc);

                ServiceEventReceived?.Invoke(this, svc);
            }
            break;

            default:
                break;
            }
        }