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); }
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); }
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); }
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); }
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); } }
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; } }