public override Task PrepareToReceiveAsync(ClientSwap swap) { // initiator waits "accepted" event, acceptor waits "initiated" event var handler = swap.IsInitiator ? SwapAcceptedEventHandler : (OnTaskDelegate)SwapInitiatedEventHandler; var lockTimeSeconds = swap.IsInitiator ? DefaultAcceptorLockTimeInSeconds : DefaultInitiatorLockTimeInSeconds; var refundTimeStampUtcInSec = new DateTimeOffset(swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeSeconds)).ToUnixTimeSeconds(); TaskPerformer.EnqueueTask(new TezosSwapInitiatedControlTask { Currency = Currency, Swap = swap, Interval = TimeSpan.FromSeconds(30), RefundTimestamp = refundTimeStampUtcInSec, CompleteHandler = handler, CancelHandler = SwapCanceledEventHandler }); return(Task.CompletedTask); }
private async Task RestoreForPurchasedCurrencyAsync(ClientSwap swap) { if (swap.RewardForRedeem > 0 && swap.StateFlags.HasFlag(SwapStateFlags.IsPaymentBroadcast)) { // may be swap already redeemed by someone else await WaitForRedeemAsync(swap) .ConfigureAwait(false); } else if (swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemBroadcast) && !swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemConfirmed)) { if (!(swap.RedeemTx is TezosTransaction redeemTx)) { Log.Error("Can't restore swap {@id}. Redeem tx is null", swap.Id); return; } TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = Currency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = redeemTx.Id, CompleteHandler = RedeemConfirmedEventHandler }); } }
private void RedeemPartyControlCompletedEventHandler(BackgroundTask task) { var redeemControlTask = task as TezosRedeemControlTask; var swap = redeemControlTask?.Swap; if (swap == null) { return; } Log.Debug("Handle redeem party control completed event for swap {@swapId}", swap.Id); if (swap.IsAcceptor) { swap.Secret = redeemControlTask.Secret; swap.SetRedeemConfirmed(); RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemConfirmed); // get transactions & update balance for address TaskPerformer.EnqueueTask(new AddressBalanceUpdateTask { Account = Account, Address = swap.ToAddress, Currency = Currency, }); } }
public override async Task HandlePartyPaymentAsync(ClientSwap swap, ClientSwap clientSwap) { Log.Debug("Handle party's payment txId for swap {@swapId}", swap.Id); if (swap.PartyPaymentTxId != null) { throw new InternalException( code: Errors.WrongSwapMessageOrder, description: $"Party's payment txId already received for swap {swap.Id}"); } if (clientSwap.PartyPaymentTxId == null) { throw new InternalException( code: Errors.InvalidPaymentTxId, description: "TxId is null"); } swap.PartyPaymentTxId = clientSwap.PartyPaymentTxId; RaiseSwapUpdated(swap, SwapStateFlags.Empty); // get party payment tx from block-chain var currency = (BitcoinBasedCurrency)swap.PurchasedCurrency; var tx = await GetPaymentTxAsync(currency, swap.PartyPaymentTxId) .ConfigureAwait(false); var refundLockTime = swap.IsInitiator ? DefaultAcceptorLockTimeInSeconds : DefaultInitiatorLockTimeInSeconds; if (!BitcoinBasedTransactionVerifier.TryVerifyPartyPaymentTx( tx: tx, swap: swap, secretHash: swap.SecretHash, refundLockTime: refundLockTime, error: out var error)) { throw new InternalException(error); } swap.PartyPaymentTx = tx; RaiseSwapUpdated(swap, SwapStateFlags.HasPartyPayment); // track initiator payment confirmation TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = swap.PurchasedCurrency, Swap = swap, TxId = swap.PartyPaymentTxId, Interval = DefaultConfirmationCheckInterval, CompleteHandler = PartyPaymentConfirmedEventHandler }); }
private async void PaymentConfirmedEventHandler(BackgroundTask task) { var confirmationCheckTask = task as TransactionConfirmationCheckTask; var swap = confirmationCheckTask?.Swap; if (swap == null) { return; } Log.Debug("Handle payment confirmed event for swap {@swapId}", swap.Id); swap.SetPaymentConfirmed(); RaiseSwapUpdated(swap, SwapStateFlags.IsPaymentConfirmed); try { if (confirmationCheckTask.Transactions.Any()) { await Account .UpsertTransactionAsync( tx : confirmationCheckTask.Transactions.First(), updateBalance : true) .ConfigureAwait(false); } var refundTimeUtc = swap.TimeStamp.ToUniversalTime() + TimeSpan.FromSeconds(swap.IsInitiator ? DefaultInitiatorLockTimeInSeconds : DefaultAcceptorLockTimeInSeconds); TaskPerformer.EnqueueTask(new RefundTimeControlTask { Currency = Currency, Swap = swap, Interval = DefaultRefundInterval, RefundTimeUtc = refundTimeUtc, CompleteHandler = RefundTimeControlEventHandler }); } catch (Exception e) { Log.Error(e, "Error while handle payment tx confirmed event"); } if (swap.IsInitiator) { RaiseInitiatorPaymentConfirmed(swap); } else { RaiseAcceptorPaymentConfirmed(swap); } }
public void AssignTaskToUser(int taskId, int userId) { var task = unitOfWork.TaskRepository.GetByID(taskId); var user = unitOfWork.UserRepository.GetByID(userId); var taskPerformer = new TaskPerformer() { Task = task, User = user }; unitOfWork.TaskPerformerRepository.Insert(taskPerformer); unitOfWork.Save(); }
public void AssignUserToTask(int userId, int taskId) { var task = unitOfWork.TaskRepository.GetByID(taskId); var user = unitOfWork.UserRepository.GetByID(userId); var taskPerformer = new TaskPerformer(); taskPerformer.Task = task; taskPerformer.User = user; unitOfWork.TaskPerformerRepository.Insert(taskPerformer); unitOfWork.Save(); }
public override async Task RedeemAsync(ClientSwap swap) { var currency = swap.PurchasedCurrency; var redeemAddress = await Account .GetFreeInternalAddressAsync(currency) .ConfigureAwait(false); // create redeem tx swap.RedeemTx = await CreateRedeemTxAsync( swap : swap, paymentTx : (IBitcoinBasedTransaction)swap.PartyPaymentTx, redeemAddress : redeemAddress.Address) .ConfigureAwait(false); // sign redeem tx swap.RedeemTx = await SignRedeemTxAsync( swap : swap, redeemTx : (IBitcoinBasedTransaction)swap.RedeemTx, paymentTx : (IBitcoinBasedTransaction)swap.PartyPaymentTx, redeemAddress : redeemAddress) .ConfigureAwait(false); swap.SetRedeemSigned(); RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemSigned); // broadcast redeem tx await BroadcastRedeemAsync( swap : swap, redeemTx : swap.RedeemTx) .ConfigureAwait(false); swap.SetRedeemBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemBroadcast); // add new unconfirmed transaction await Account .UpsertTransactionAsync( tx : swap.RedeemTx, updateBalance : true) .ConfigureAwait(false); TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = swap.PurchasedCurrency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = swap.RedeemTx.Id, CompleteHandler = RedeemConfirmedEventHandler }); }
private Task RestoreForSoldCurrencyAsync(ClientSwap swap) { if (swap.StateFlags.HasFlag(SwapStateFlags.IsPaymentBroadcast)) { if (!(swap.PaymentTx is EthereumTransaction)) { Log.Error("Can't restore swap {@id}. Payment tx is null.", swap.Id); return(Task.CompletedTask); } var lockTimeInSeconds = swap.IsInitiator ? DefaultInitiatorLockTimeInSeconds : DefaultAcceptorLockTimeInSeconds; // start redeem control TaskPerformer.EnqueueTask(new EthereumRedeemControlTask { Currency = Currency, RefundTimeUtc = swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeInSeconds), Swap = swap, CompleteHandler = RedeemControlCompletedEventHandler, CancelHandler = RedeemControlCanceledEventHandler }); } else { if (DateTime.UtcNow < swap.TimeStamp.ToUniversalTime() + DefaultMaxSwapTimeout) { if (swap.IsInitiator) { // todo: initiate swap //await InitiateSwapAsync(swapState) // .ConfigureAwait(false); } else { // todo: request secret hash from server } } else { swap.Cancel(); RaiseSwapUpdated(swap, SwapStateFlags.IsCanceled); } } return(Task.CompletedTask); }
private async void RefundTimeControlEventHandler(BackgroundTask task) { var refundTimeControlTask = task as RefundTimeControlTask; var swap = refundTimeControlTask?.Swap; if (swap == null) { return; } try { var refundTx = (IBitcoinBasedTransaction)swap.RefundTx; var txId = await Currency.BlockchainApi .BroadcastAsync(refundTx) .ConfigureAwait(false); if (txId == null) { throw new Exception("Transaction Id is null"); } Log.Debug("Refund tx id {@txId} for swap {@swapId}", txId, swap.Id); // todo: check result swap.SetRefundBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsRefundBroadcast); await Account .UpsertTransactionAsync( tx : refundTx, updateBalance : true) .ConfigureAwait(false); TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = Currency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = swap.RefundTx.Id, CompleteHandler = RefundConfirmedEventHandler }); } catch (Exception e) { Log.Error(e, "Refund task error"); } }
public override async Task BroadcastPaymentAsync(ClientSwap swap) { var lockTimeInSeconds = swap.IsInitiator ? DefaultInitiatorLockTimeInSeconds : DefaultAcceptorLockTimeInSeconds; var txs = (await CreatePaymentTxsAsync(swap, lockTimeInSeconds) .ConfigureAwait(false)) .ToList(); if (txs.Count == 0) { Log.Error("Can't create payment transactions"); return; } var signResult = await SignPaymentTxsAsync(txs) .ConfigureAwait(false); if (!signResult) { return; } swap.PaymentTx = txs.First(); swap.SetPaymentSigned(); RaiseSwapUpdated(swap, SwapStateFlags.IsPaymentSigned); foreach (var tx in txs) { await BroadcastTxAsync(swap, tx) .ConfigureAwait(false); } swap.PaymentTx = txs.First(); swap.SetPaymentBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsPaymentBroadcast); // start redeem control TaskPerformer.EnqueueTask(new EthereumRedeemControlTask { Currency = Currency, RefundTimeUtc = swap.TimeStamp.ToUniversalTime().AddSeconds(lockTimeInSeconds), Swap = swap, CompleteHandler = RedeemControlCompletedEventHandler, CancelHandler = RedeemControlCanceledEventHandler }); }
public override Task WaitForRedeemAsync(ClientSwap swap) { Log.Debug("Wait redeem for swap {@swapId}", swap.Id); // start redeem control TaskPerformer.EnqueueTask(new TezosRedeemControlTask { Currency = Currency, RefundTimeUtc = swap.TimeStamp.ToUniversalTime().AddSeconds(DefaultAcceptorLockTimeInSeconds), Swap = swap, CompleteHandler = RedeemPartyControlCompletedEventHandler, CancelHandler = RedeemPartyControlCanceledEventHandler }); return(Task.CompletedTask); }
private void RefundTimeReachedEventHandler(BackgroundTask task) { var refundTimeControlTask = task as RefundTimeControlTask; var swap = refundTimeControlTask?.Swap; if (swap == null) { return; } Log.Debug("Refund time reached for swap {@swapId}", swap.Id); TaskPerformer.EnqueueTask(new TezosRefundControlTask { Currency = Currency, Swap = swap, CompleteHandler = RefundConfirmedEventHandler, CancelHandler = RefundEventHandler }); }
private void RedeemControlCanceledEventHandler(BackgroundTask task) { var redeemControlTask = task as TezosRedeemControlTask; var swap = redeemControlTask?.Swap; if (swap == null) { return; } Log.Debug("Handle redeem control canceled event for swap {@swapId}", swap.Id); TaskPerformer.EnqueueTask(new RefundTimeControlTask { Currency = Currency, RefundTimeUtc = redeemControlTask.RefundTimeUtc, Swap = swap, CompleteHandler = RefundTimeReachedEventHandler }); }
/// <summary> /// Initializes a new instance of the <see cref="Supervisor"/> class. /// </summary> /// <param name="fMainParam">The f main parameter.</param> public Supervisor(MDIParentForm fMainParam) { frmMain = fMainParam; var fileName = AppDomain.CurrentDomain.BaseDirectory + Path.DirectorySeparatorChar + @"\Config\ElevenSettings.xml"; GlobalSettings = ElevenSettings.Load(fileName); if (string.IsNullOrEmpty(GlobalSettings.FolderSettings.Logs)) { GlobalSettings.FolderSettings.Logs = AppDomain.CurrentDomain.BaseDirectory + Path.DirectorySeparatorChar + "Logs"; } //Path Log - Creazione if (!Directory.Exists(GlobalSettings.FolderSettings.Logs)) { Directory.CreateDirectory(GlobalSettings.FolderSettings.Logs); } Log = new LogManager(GlobalSettings.FolderSettings.Logs + Path.DirectorySeparatorChar + "Eleven_" + PreciseDateTime.Now.ToString("yyyyMMdd") + ".log"); Log.LogEvent("[---------------------------------------------]"); Log.LogEvent("[E-LEVEN][" + Assembly.GetExecutingAssembly().GetName().Version + "]"); //Creazione Task taskPerformer = new TaskPerformer(this); //Gatewqay gateway = new Gateway(1, 1); InitExchanges(); //Engine e = new Engine( // Engine = new Engine(frmMain, gateway); //InitSecurities(); System.Threading.Tasks.Task.Factory.StartNew(() => InitSecuritiesDelay()); }
private Task RestoreForPurchasedCurrencyAsync(ClientSwap swap) { if (swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemBroadcast) && !swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemConfirmed)) { if (!(swap.RedeemTx is IBitcoinBasedTransaction redeemTx)) { Log.Error("Can't restore swap {@id}. Redeem tx is null", swap.Id); return(Task.CompletedTask); } TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = Currency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = redeemTx.Id, CompleteHandler = RedeemConfirmedEventHandler }); } return(Task.CompletedTask); }
private async Task RefundAsync( ClientSwap swap, CancellationToken cancellationToken = default(CancellationToken)) { Log.Debug("Create refund for swap {@swap}", swap.Id); var walletAddress = (await Account.GetUnspentAddressesAsync( currency: Currency, amount: 0, fee: Xtz.RefundFee.ToTez() + Xtz.RefundStorageLimit.ToTez(), feePrice: 0, isFeePerTransaction: true, addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress, cancellationToken: cancellationToken) .ConfigureAwait(false)) .FirstOrDefault(); if (walletAddress == null) { Log.Error("Insufficient funds for refund"); return; } var refundTx = new TezosTransaction(Xtz) { From = walletAddress.Address, To = Xtz.SwapContractAddress, Fee = Xtz.RefundFee, GasLimit = Xtz.RefundGasLimit, StorageLimit = Xtz.RefundStorageLimit, Params = RefundParams(swap), Type = TezosTransaction.OutputTransaction }; var signResult = await SignTransactionAsync(refundTx, cancellationToken) .ConfigureAwait(false); if (!signResult) { Log.Error("Transaction signing error"); return; } swap.RefundTx = refundTx; swap.SetRefundSigned(); RaiseSwapUpdated(swap, SwapStateFlags.IsRefundSigned); await BroadcastTxAsync(swap, refundTx, cancellationToken) .ConfigureAwait(false); swap.RefundTx = refundTx; swap.SetRefundBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsRefundBroadcast); TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = Currency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = refundTx.Id, CompleteHandler = RefundConfirmedEventHandler }); }
public override async Task BroadcastPaymentAsync(ClientSwap swap) { if (swap.IsAcceptor && (!swap.StateFlags.HasFlag(SwapStateFlags.HasPartyPayment) || !swap.StateFlags.HasFlag(SwapStateFlags.IsPartyPaymentConfirmed))) { Log.Debug("CounterParty is not ready to broadcast payment tx for swap {@swap}", swap.Id); return; } var lockTimeInSeconds = swap.IsInitiator ? DefaultInitiatorLockTimeInSeconds : DefaultAcceptorLockTimeInSeconds; await CreatePaymentAsync(swap, lockTimeInSeconds) .ConfigureAwait(false); Log.Debug("Broadcast payment tx for swap {@swap}", swap.Id); var currency = swap.SoldCurrency; // broadcast payment transaction var txId = await currency.BlockchainApi .BroadcastAsync(swap.PaymentTx) .ConfigureAwait(false); swap.PaymentTxId = txId ?? throw new Exception("Transaction Id is null"); swap.SetPaymentBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsPaymentBroadcast); Log.Debug("Payment txId {@id}", txId); // account new unconfirmed transaction await Account .UpsertTransactionAsync( tx : swap.PaymentTx, updateBalance : true, notifyIfUnconfirmed : false) .ConfigureAwait(false); // send payment txId to party SwapClient.SwapPaymentAsync(swap); if (swap.IsAcceptor) { var swapOutputs = ((IBitcoinBasedTransaction)swap.PaymentTx) .SwapOutputs() .ToList(); if (swapOutputs.Count != 1) { throw new InternalException( code: Errors.SwapError, description: "Payment tx must have only one swap output"); } // track counter party payment spent event TaskPerformer.EnqueueTask(new BitcoinBasedOutputSpentTask { Currency = currency, Swap = swap, OutputHash = txId, OutputIndex = swapOutputs.First().Index, Interval = DefaultOutputSpentCheckInterval, CompleteHandler = PaymentSpentEventHandler }); } // track payment transaction confirmation TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = currency, Swap = swap, TxId = txId, Interval = DefaultConfirmationCheckInterval, CompleteHandler = PaymentConfirmedEventHandler }); }
private async Task RestoreForSoldCurrencyAsync(ClientSwap swap) { if (swap.StateFlags.HasFlag(SwapStateFlags.IsPaymentBroadcast)) { if (!(swap.PaymentTx is IBitcoinBasedTransaction tx)) { Log.Error("Can't restore swap {@id}. Payment tx is null.", swap.Id); return; } // check payment transaction spent var api = (IInOutBlockchainApi)Currency.BlockchainApi; var spentPoint = await api .IsTransactionOutputSpent(tx.Id, tx.SwapOutputs().First().Index) // todo: check specific output .ConfigureAwait(false); if (spentPoint != null && (swap.RefundTx == null || (swap.RefundTx != null && spentPoint.Hash != swap.RefundTx.Id))) { // raise redeem for counter party in other chain if (swap.IsAcceptor) { PaymentSpentEventHandler(swap, spentPoint); } // else // nothing to do (waiting for redeem confirmation in another chain) return; } if (!(swap.RefundTx is IBitcoinBasedTransaction refundTx)) { Log.Error("Can't restore swap {@id}. Refund tx is null", swap.Id); return; } if (swap.StateFlags.HasFlag(SwapStateFlags.IsRefundBroadcast)) { // wait for refund confirmation TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = Currency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = refundTx.Id, CompleteHandler = RefundConfirmedEventHandler }); } else { var refundTimeUtc = swap.TimeStamp.ToUniversalTime() + TimeSpan.FromSeconds(swap.IsInitiator ? DefaultInitiatorLockTimeInSeconds : DefaultAcceptorLockTimeInSeconds); // refund control TaskPerformer.EnqueueTask(new RefundTimeControlTask { Currency = Currency, Swap = swap, Interval = DefaultRefundInterval, RefundTimeUtc = refundTimeUtc, CompleteHandler = RefundTimeControlEventHandler }); } } else { if (DateTime.UtcNow < swap.TimeStamp.ToUniversalTime() + DefaultMaxSwapTimeout) { if (swap.IsInitiator) { // todo: initiate swap //await InitiateSwapAsync(swapState) // .ConfigureAwait(false); } else { // todo: request secret hash from server } } else { swap.Cancel(); RaiseSwapUpdated(swap, SwapStateFlags.IsCanceled); } } }
private async Task RefundAsync( ClientSwap swap, CancellationToken cancellationToken = default(CancellationToken)) { Log.Debug("Create refund for swap {@swap}", swap.Id); var walletAddress = (await Account.GetUnspentAddressesAsync( currency: Currency, amount: 0, // todo: account storage fee fee: Eth.RefundGasLimit, feePrice: Eth.GasPriceInGwei, isFeePerTransaction: true, addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress) .ConfigureAwait(false)) .FirstOrDefault(); if (walletAddress == null) { Log.Error("Insufficient funds for refund"); return; } var nonce = await EthereumNonceManager.Instance .GetNonce(Eth, walletAddress.Address) .ConfigureAwait(false); var message = new RefundFunctionMessage { FromAddress = walletAddress.Address, HashedSecret = swap.SecretHash, GasPrice = Atomix.Ethereum.GweiToWei(Eth.GasPriceInGwei), Nonce = nonce, }; message.Gas = await EstimateGasAsync(message, new BigInteger(Eth.RefundGasLimit)) .ConfigureAwait(false); var txInput = message.CreateTransactionInput(Eth.SwapContractAddress); var refundTx = new EthereumTransaction(Eth, txInput) { Type = EthereumTransaction.OutputTransaction }; var signResult = await SignTransactionAsync(refundTx, cancellationToken) .ConfigureAwait(false); if (!signResult) { Log.Error("Transaction signing error"); return; } swap.RefundTx = refundTx; swap.SetRefundSigned(); RaiseSwapUpdated(swap, SwapStateFlags.IsRefundSigned); await BroadcastTxAsync(swap, refundTx, cancellationToken) .ConfigureAwait(false); swap.RefundTx = refundTx; swap.SetRefundBroadcast(); RaiseSwapUpdated(swap, SwapStateFlags.IsRefundBroadcast); TaskPerformer.EnqueueTask(new TransactionConfirmationCheckTask { Currency = Currency, Swap = swap, Interval = DefaultConfirmationCheckInterval, TxId = refundTx.Id, CompleteHandler = RefundConfirmedEventHandler }); }