public void ReportConsolidationValidated(WalletConsolidationTransferEventEntry consolidation, bool validation) { _logger.LogInformation( $"Validation of {consolidation.TransferQty} {consolidation.CoinSymbol} consolidation {(validation ? "successful" : "failed")}"); EventHistoryRepository.Events().FindOneAndUpdate( eventEntry => eventEntry.Id.Equals(consolidation.Id), Builders <EventEntry> .Update.Set( eventEntry => ((WalletConsolidationTransferEventEntry)eventEntry).Valid, validation ) ); }
public void ReportConsolidationExecuted(WalletConsolidationTransferEventEntry consolidation) { _logger.LogInformation( $"Reported a new executed consolidation of {consolidation.TransferQty} {consolidation.CoinSymbol}, target becomes {consolidation.NewTargetPublicKeyBalance}"); EventHistoryRepository.Events().FindOneAndUpdate( eventEntry => eventEntry.Id.Equals(consolidation.Id), Builders <EventEntry> .Update.Set( eventEntry => ((WalletConsolidationTransferEventEntry)eventEntry).Executed, true ) ); }
public void ProcessEvent(WalletConsolidationTransferEventEntry eventEntry) { if (eventEntry.Valid != null) { // Consolidation was either invalid anyway, or it's already validated return; } // Validate the consolidation's withdrawal - Wallet Server actively waits for this! var currentVersionNumberEvents = _eventHistoryService.FindByVersionNumber(eventEntry.VersionNumber) .ToList(); var consolidationList = currentVersionNumberEvents.FindAll(e => e is WalletConsolidationTransferEventEntry); var withdrawalList = currentVersionNumberEvents.FindAll(e => e is WalletWithdrawalEventEntry); if (withdrawalList.Count == 0) { throw new Exception("Standalone consolidation not implemented yet"); } var withdrawal = (WalletWithdrawalEventEntry)withdrawalList.Single(); bool valid; if (withdrawal.Validated.HasValue) { valid = withdrawal.Validated.Value; } else { var(balance, reservedBalance) = _userService .GetBalanceAndReservedBalance(withdrawal.User, withdrawal.AccountId, withdrawal.CoinSymbol); if (!IsValidWithdrawal(withdrawal, balance)) { valid = false; // Withdrawal is invalid _eventHistoryService.ReportWithdrawalValidation(withdrawal, valid); } else { valid = true; _eventHistoryService.ReportWithdrawalValidation(withdrawal, valid); } } foreach (var consolidationEntry in consolidationList) { var consolidation = (WalletConsolidationTransferEventEntry)consolidationEntry; _eventHistoryService.ReportConsolidationValidated(consolidation, valid); } }
private void ProcessEvent(WalletConsolidationTransferEventEntry eventEntry) { while (eventEntry.Valid == null) { _logger.LogWarning( $"Consolidation event {eventEntry.Id} is not validated by Trading Service yet, waiting..."); Task.Delay(1000).Wait(); eventEntry = (WalletConsolidationTransferEventEntry)_eventHistoryService.FindById(eventEntry.Id); } if (eventEntry.Valid == false) { return; } var oldBalanceSrc = GetCurrentlyCachedBalance(eventEntry.TransferSourcePublicKey).Result; var newBalanceSrc = oldBalanceSrc - eventEntry.TransferQty - eventEntry.TransferFee; var oldBalanceTarget = GetCurrentlyCachedBalance(eventEntry.TransferTargetPublicKey).Result; var newBalanceTarget = oldBalanceTarget + eventEntry.TransferQty; if (!eventEntry.Executed) { if (newBalanceSrc == eventEntry.NewSourcePublicKeyBalance && newBalanceTarget == eventEntry.NewTargetPublicKeyBalance) { // Sanity check successful, we can execute the consolidation // We start by expecting the target deposit, not yielding it to user as his own deposit _knownPublicKeyBalances[eventEntry.TransferTargetPublicKey] = newBalanceTarget; // We can unlock source for deposit, and immediately lock it back until withdrawal gets processed, // but we instead expect to be locked throw a fatal error in case withdrawal goes wrong // _knownPublicKeyBalances[eventEntry.TransferSourcePublicKey] = eventEntry.NewSourcePublicKeyBalance; // _lockedPublicKeyBalances[eventEntry.TransferSourcePublicKey] += // eventEntry.TransferQty + eventEntry.TransferFee; var success = Withdraw( eventEntry.TransferSourcePublicKey, eventEntry.TransferTargetPublicKey, eventEntry.TransferQty ).Result; if (!success) { throw new Exception( $"Consolidation event id {eventEntry.Id} failed to execute the withdrawal. {GetType().Name} has to stop processing further events, requiring administrator to solve this issue"); } EnsureWithdrawn( eventEntry.TransferSourcePublicKey, eventEntry.NewSourcePublicKeyBalance, eventEntry.TransferQty + eventEntry.TransferFee, eventEntry.VersionNumber); // We could unlock the source for deposit if we locked it before withdrawal // _lockedPublicKeyBalances[eventEntry.TransferSourcePublicKey] -= // eventEntry.TransferQty + eventEntry.TransferFee; } else if (GetBalance(eventEntry.TransferSourcePublicKey).Result == eventEntry.NewSourcePublicKeyBalance && GetBalance(eventEntry.TransferTargetPublicKey).Result == eventEntry.NewTargetPublicKeyBalance) { // Already seems to be consolidated properly // If the GetBalance call fails, we consider this a fatal error anyway, as else branch does it too _logger.LogError( "Consolidation event already seems to have been successfully finished, even though it was unexpected. Marking the event as executed"); } else { throw new Exception( $"{GetType().Name} detected fatal event inconsistency of expected wallet balances during consolidation event id {eventEntry.Id} processing. Expecting source {oldBalanceSrc} -> {newBalanceSrc} to reach {eventEntry.NewSourcePublicKeyBalance}, target {oldBalanceTarget} -> {newBalanceTarget} to reach {eventEntry.NewTargetPublicKeyBalance}"); } _eventHistoryService.ReportConsolidationExecuted(eventEntry); } // If the transfer was already executed, simply assert expected values and switch cached balances else { if (newBalanceSrc != eventEntry.NewSourcePublicKeyBalance || newBalanceTarget != eventEntry.NewTargetPublicKeyBalance) { throw new Exception( $"{GetType().Name} detected fatal event inconsistency of expected wallet balances during consolidation event id {eventEntry.Id} processing. Expecting source {oldBalanceSrc} to become {newBalanceSrc} and reach {eventEntry.NewSourcePublicKeyBalance}, target {oldBalanceTarget} to become {newBalanceTarget} and reach {eventEntry.NewTargetPublicKeyBalance}"); } // Assume there was a successful target deposit _knownPublicKeyBalances[eventEntry.TransferTargetPublicKey] = newBalanceTarget; } // Unlock balance for a deposit detection _knownPublicKeyBalances[eventEntry.TransferSourcePublicKey] = newBalanceSrc; }