示例#1
0
        public override async Task RedeemAsync(
            Swap swap,
            CancellationToken cancellationToken = default)
        {
            Log.Debug("Redeem for swap {@swap}.", swap.Id);

            var currency = Currencies.GetByName(swap.PurchasedCurrency);

            var needReplaceTx = false;

            if (swap.StateFlags.HasFlag(SwapStateFlags.IsRedeemBroadcast))
            {
                Log.Debug("Check redeem confirmation for swap {@swap}.", swap.Id);

                // redeem already broadcast
                var result = await currency
                             .IsTransactionConfirmed(
                    txId : swap.RedeemTx.Id,
                    cancellationToken : cancellationToken)
                             .ConfigureAwait(false);

                if (result == null)
                {
                    Log.Error("Error while check bitcoin based redeem tx confirmation. Result is null.");
                    return;
                }
                else if (result.HasError && result.Error.Code == (int)HttpStatusCode.NotFound)
                {
                    // probably the transaction was deleted by miners
                    Log.Debug("Probably the transaction {@tx} was deleted by miners.", swap.RedeemTx.Id);
                    needReplaceTx = true;
                }
                else if (result.HasError)
                {
                    Log.Error("Error while check bitcoin based redeem tx confirmation. Code: {@code}. Description: {@description}.",
                              result.Error.Code,
                              result.Error.Description);
                    return;
                }
                else if (result.Value.IsConfirmed) // tx already confirmed
                {
                    Log.Debug("Transaction {@tx} is already confirmed.", swap.RedeemTx.Id);
                    RedeemConfirmedEventHandler(swap, result.Value.Transaction, cancellationToken);
                    return;
                }

                var currentTimeUtc = DateTime.UtcNow;

                var creationTimeUtc = swap.RedeemTx.CreationTime != null
                    ? swap.RedeemTx.CreationTime.Value.ToUniversalTime()
                    : swap.TimeStamp.ToUniversalTime();

                var difference = currentTimeUtc - creationTimeUtc;

                Log.Debug("Currenct time: {@current}, creation time: {@now}, difference: {@diff}",
                          currentTimeUtc,
                          creationTimeUtc,
                          difference);

                // check transaction creation time and try replacing it with a higher fee
                if (difference >= TimeSpan.FromHours(4))
                {
                    needReplaceTx = true;
                }

                if (!needReplaceTx)
                {
                    TrackTransactionConfirmationAsync(
                        swap: swap,
                        currency: currency,
                        txId: swap.RedeemTx.Id,
                        confirmationHandler: RedeemConfirmedEventHandler,
                        cancellationToken: cancellationToken)
                    .FireAndForget();

                    return;
                }
            }

            if (swap.IsInitiator)
            {
                var redeemDeadline = swap.TimeStamp.ToUniversalTime().AddSeconds(DefaultAcceptorLockTimeInSeconds) - RedeemTimeReserve;

                if (DateTime.UtcNow > redeemDeadline)
                {
                    Log.Error("Redeem dedline reached for swap {@swap}", swap.Id);
                    return;
                }
            }

            var redeemAddress = await _account
                                .GetFreeInternalAddressAsync()
                                .ConfigureAwait(false);

            var partyRedeemScript = Convert.FromBase64String(swap.PartyRedeemScript);

            // create redeem tx
            swap.RedeemTx = await CreateRedeemTxAsync(
                swap : swap,
                paymentTx : (IBitcoinBasedTransaction)swap.PartyPaymentTx,
                redeemAddress : redeemAddress.Address,
                redeemScript : partyRedeemScript,
                increaseSequenceNumber : needReplaceTx)
                            .ConfigureAwait(false);

            var toAddress = await _account
                            .GetAddressAsync(currency.Name, swap.ToAddress, cancellationToken)
                            .ConfigureAwait(false);

            // sign redeem tx
            swap.RedeemTx = await SignRedeemTxAsync(
                swap : swap,
                redeemTx : (IBitcoinBasedTransaction)swap.RedeemTx,
                paymentTx : (IBitcoinBasedTransaction)swap.PartyPaymentTx,
                redeemAddress : toAddress,
                redeemScript : partyRedeemScript)
                            .ConfigureAwait(false);

            swap.StateFlags |= SwapStateFlags.IsRedeemSigned;
            RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemSigned);

            // broadcast redeem tx
            await BroadcastRedeemAsync(
                swap : swap,
                redeemTx : swap.RedeemTx,
                cancellationToken : cancellationToken)
            .ConfigureAwait(false);

            swap.StateFlags |= SwapStateFlags.IsRedeemBroadcast;
            RaiseSwapUpdated(swap, SwapStateFlags.IsRedeemBroadcast);

            // add new unconfirmed transaction
            await _account
            .UpsertTransactionAsync(
                tx : swap.RedeemTx,
                updateBalance : true,
                cancellationToken : cancellationToken)
            .ConfigureAwait(false);

            TrackTransactionConfirmationAsync(
                swap: swap,
                currency: currency,
                txId: swap.RedeemTx.Id,
                confirmationHandler: RedeemConfirmedEventHandler,
                cancellationToken: cancellationToken)
            .FireAndForget();
        }
        public override async Task PayAsync(
            Swap swap,
            CancellationToken cancellationToken = default)
        {
            if (swap.IsAcceptor && !swap.HasPartyPayment)
            {
                Log.Debug("Acceptor is not ready to broadcast {@currency} payment tx for swap {@swap}",
                          Currency,
                          swap.Id);

                return;
            }

            if (!await CheckPayRelevanceAsync(swap, cancellationToken))
            {
                return;
            }

            var lockTimeInSeconds = swap.IsInitiator
                ? DefaultInitiatorLockTimeInSeconds
                : DefaultAcceptorLockTimeInSeconds;

            var lockTime = swap.TimeStamp.ToUniversalTime() + TimeSpan.FromSeconds(lockTimeInSeconds);

            var refundAddress = await _account
                                .GetAddressAsync(swap.RefundAddress)
                                .ConfigureAwait(false);

            swap.PaymentTx = await CreatePaymentTxAsync(
                swap : swap,
                refundAddress : refundAddress.Address,
                lockTime : lockTime)
                             .ConfigureAwait(false);

            swap.PaymentTx = await SignPaymentTxAsync(
                swap : swap,
                paymentTx : (IBitcoinBasedTransaction)swap.PaymentTx)
                             .ConfigureAwait(false);

            swap.StateFlags |= SwapStateFlags.IsPaymentSigned;

            await UpdateSwapAsync(swap, SwapStateFlags.IsPaymentSigned)
            .ConfigureAwait(false);

            Log.Debug("Broadcast {@currency} payment tx for swap {@swap}",
                      Currency,
                      swap.Id);

            var currency = Currencies.GetByName(swap.SoldCurrency);

            // broadcast payment transaction
            var broadcastResult = await currency.BlockchainApi
                                  .TryBroadcastAsync(swap.PaymentTx, cancellationToken : cancellationToken)
                                  .ConfigureAwait(false);

            if (broadcastResult.HasError)
            {
                Log.Error("Error while broadcast {@currency} transaction. Code: {@code}. Description: {@description}",
                          currency.Name,
                          broadcastResult.Error.Code,
                          broadcastResult.Error.Description);

                return;
            }

            var txId = broadcastResult.Value;

            swap.PaymentTxId = txId ?? throw new Exception("Transaction Id is null");
            swap.StateFlags |= SwapStateFlags.IsPaymentBroadcast;

            await UpdateSwapAsync(swap, SwapStateFlags.IsPaymentBroadcast, cancellationToken)
            .ConfigureAwait(false);

            Log.Debug("{@currency} payment txId {@id} for swap {@swap}",
                      currency.Name,
                      txId,
                      swap.Id);

            // account new unconfirmed transaction
            await _account
            .UpsertTransactionAsync(
                tx : swap.PaymentTx,
                updateBalance : true,
                notifyIfUnconfirmed : true,
                cancellationToken : cancellationToken)
            .ConfigureAwait(false);
        }