Пример #1
0
        private async Task HandleAcceptAsync(ClientSwap swap, ClientSwap clientSwap)
        {
            // check party requisites
            if (clientSwap.PartyAddress == null)
            {
                throw new InternalException(
                          code: Errors.InvalidWallets,
                          description: $"Incorrect party address for swap {swap.Id}");
            }

            //if (IsCriminal(clientSwap.PartyAddress))
            //    throw new InternalException(
            //        code: Errors.IsCriminalWallet,
            //        description: $"Party wallet is criminal for swap {swap.Id}");

            if (clientSwap.RewardForRedeem < 0)
            {
                throw new InternalException(
                          code: Errors.InvalidRewardForRedeem,
                          description: $"Incorrect reward for redeem for swap {swap.Id}");
            }

            swap.PartyAddress         = clientSwap.PartyAddress;
            swap.PartyRewardForRedeem = clientSwap.PartyRewardForRedeem;
            RaiseSwapUpdated(swap, SwapStateFlags.Empty);

            // broadcast initiator payment
            await GetCurrencySwap(swap.SoldCurrency)
            .BroadcastPaymentAsync(swap)
            .ConfigureAwait(false);

            await GetCurrencySwap(swap.PurchasedCurrency)
            .PrepareToReceiveAsync(swap)
            .ConfigureAwait(false);
        }
Пример #2
0
 private JObject InitParams(
     ClientSwap swap,
     long refundTimestamp,
     long redeemFeeAmount)
 {
     return(JObject.Parse(@"{'prim':'Left','args':[{'prim':'Left','args':[{'prim':'Pair','args':[{'string':'" + swap.PartyAddress + "'},{'prim':'Pair','args':[{'prim':'Pair','args':[{'bytes':'" + swap.SecretHash.ToHexString() + "'},{'int':'" + refundTimestamp + "'}]},{'int':'" + redeemFeeAmount + "'}]}]}]}]}"));
 }
Пример #3
0
        private async Task HandleExistingSwapAsync(ClientSwap swap, ClientSwap clientSwap)
        {
            try
            {
                if (IsInitiate(swap, clientSwap) && swap.IsAcceptor)
                {
                    // handle initiate
                    await HandleInitiateAsync(swap, clientSwap)
                    .ConfigureAwait(false);
                }
                else if (IsAccept(swap, clientSwap) && swap.IsInitiator)
                {
                    // handle accept
                    await HandleAcceptAsync(swap, clientSwap)
                    .ConfigureAwait(false);
                }
                else if (IsPartyPayment(swap, clientSwap))
                {
                    // party payment
                    await GetCurrencySwap(swap.PurchasedCurrency)
                    .HandlePartyPaymentAsync(swap, clientSwap)
                    .ConfigureAwait(false);
                }
            }
            catch (Exception e)
            {
                Log.Error(e, "Existing swap handle error");
            }

            // update swap status
            swap.Status = clientSwap.Status;
            RaiseSwapUpdated(swap, SwapStateFlags.Empty);
        }
        public Task <bool> AddSwapAsync(ClientSwap swap)
        {
            if (!_swapById.TryAdd(swap.Id, swap))
            {
                return(Task.FromResult(false));
            }

            try
            {
                lock (_syncRoot)
                {
                    using (var db = new LiteDatabase(ConnectionString, _bsonMapper))
                    {
                        db.GetCollection <ClientSwap>(SwapsCollectionName)
                        .Insert(swap);
                    }

                    return(Task.FromResult(true));
                }
            }
            catch (Exception e)
            {
                Log.Error(e, "Swap add error");
            }

            return(Task.FromResult(false));
        }
        public static SwapViewModel CreateSwapViewModel(ClientSwap swap)
        {
            var fromCurrency = CurrencyViewModelCreator.CreateViewModel(
                currency: swap.SoldCurrency,
                subscribeToUpdates: false);

            var toCurrency = CurrencyViewModelCreator.CreateViewModel(
                currency: swap.PurchasedCurrency,
                subscribeToUpdates: false);

            var fromAmount = AmountHelper.QtyToAmount(swap.Side, swap.Qty, swap.Price);
            var toAmount   = AmountHelper.QtyToAmount(swap.Side.Opposite(), swap.Qty, swap.Price);

            return(new SwapViewModel
            {
                Id = swap.Id.ToString(),
                CompactState = CompactStateBySwap(swap),
                Mode = ModeBySwap(swap),
                Time = swap.TimeStamp,

                FromBrush = new SolidColorBrush(fromCurrency.AmountColor),
                FromAmount = fromAmount,
                FromAmountFormat = fromCurrency.CurrencyFormat,
                FromCurrencyCode = fromCurrency.CurrencyCode,

                ToBrush = new SolidColorBrush(toCurrency.AmountColor),
                ToAmount = toAmount,
                ToAmountFormat = toCurrency.CurrencyFormat,
                ToCurrencyCode = toCurrency.CurrencyCode,

                Price = swap.Price,
                PriceFormat = $"F{swap.Symbol.Quote.Digits}"
            });
        }
        private async Task GetSecretAsync(ClientSwap swap, ITxPoint spentPoint)
        {
            Log.Debug("Try to get CounterParty's payment spent output {@hash}:{@no} for swap {@swapId}",
                      spentPoint.Hash,
                      spentPoint.Index,
                      swap.Id);

            var soldCurrency = swap.SoldCurrency;

            var swapInput = await((IInOutBlockchainApi)soldCurrency.BlockchainApi)
                            .GetInputAsync(spentPoint.Hash, spentPoint.Index)
                            .ConfigureAwait(false);

            var secret     = swapInput.ExtractSecret();
            var secretHash = CreateSwapSecretHash(secret);

            if (!secretHash.SequenceEqual(swap.SecretHash))
            {
                throw new InternalException(
                          code: Errors.InvalidSecretHash,
                          description: "Invalid secret hash");
            }

            swap.Secret = secret;
            RaiseSwapUpdated(swap, SwapStateFlags.HasSecret);
        }
Пример #7
0
        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
                });
            }
        }
Пример #8
0
        private async Task BroadcastTxAsync(
            ClientSwap swap,
            TezosTransaction tx,
            CancellationToken cancellationToken = default(CancellationToken))
        {
            var txId = await Xtz.BlockchainApi
                       .BroadcastAsync(tx, cancellationToken)
                       .ConfigureAwait(false);

            if (txId == null)
            {
                throw new Exception("Transaction Id is null");
            }

            Log.Debug("TxId {@id} for swap {@swapId}", txId, swap.Id);

            // account new unconfirmed transaction
            await Account
            .UpsertTransactionAsync(
                tx : tx,
                updateBalance : true,
                notifyIfUnconfirmed : true,
                notifyIfBalanceUpdated : true,
                cancellationToken : cancellationToken)
            .ConfigureAwait(false);

            // todo: transaction receipt status control
        }
        private async Task <IBitcoinBasedTransaction> CreateRefundTxAsync(
            ClientSwap swap,
            IBitcoinBasedTransaction paymentTx,
            string refundAddress,
            DateTimeOffset lockTime)
        {
            Log.Debug("Create refund tx for swap {@swapId}", swap.Id);

            var tx = await _transactionFactory
                     .CreateSwapRefundTxAsync(
                paymentTx : paymentTx,
                swap : swap,
                refundAddress : refundAddress,
                lockTime : lockTime)
                     .ConfigureAwait(false);

            if (tx == null)
            {
                throw new InternalException(
                          code: Errors.TransactionCreationError,
                          description: $"Refund tx creation error for swap {swap.Id}");
            }

            Log.Debug("Refund tx successfully created for swap {@swapId}", swap.Id);

            return(tx);
        }
Пример #10
0
        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);
        }
Пример #11
0
        private bool IsPartyPayment(ClientSwap swap, ClientSwap clientSwap)
        {
            var isInitiatorPaymentReceived = swap.IsStatusChanged(clientSwap.Status, SwapStatus.InitiatorPaymentReceived);
            var isAcceptorPaymentReceived  = swap.IsStatusChanged(clientSwap.Status, SwapStatus.AcceptorPaymentReceived);

            return(swap.IsAcceptor && isInitiatorPaymentReceived ||
                   swap.IsInitiator && isAcceptorPaymentReceived);
        }
        public async Task <IBlockchainTransaction> CreateSwapPaymentTxTest()
        {
            var bitcoinApi = new Mock <IInOutBlockchainApi>();

            bitcoinApi.Setup(a => a.GetUnspentOutputsAsync(It.IsAny <string>(), null, new CancellationToken()))
            .Returns(Task.FromResult(GetTestOutputs(Common.Alice.PubKey, NBitcoin.Network.TestNet)));

            var litecoinApi = new Mock <IInOutBlockchainApi>();

            litecoinApi.Setup(a => a.GetUnspentOutputsAsync(It.IsAny <string>(), null, new CancellationToken()))
            .Returns(Task.FromResult(GetTestOutputs(Common.Bob.PubKey, AltNetworkSets.Litecoin.Testnet)));

            var tempCurrencies = new Currencies(Common.CurrenciesConfiguration.GetSection(Atomix.Core.Network.TestNet.ToString()));

            var bitcoin = tempCurrencies.Get <Bitcoin>();

            bitcoin.BlockchainApi = bitcoinApi.Object;

            var litecoin = tempCurrencies.Get <Litecoin>();

            litecoin.BlockchainApi = litecoinApi.Object;

            var aliceBtcWallet = GetWallet(bitcoin, Common.Alice.PubKey);
            //var aliceLtcWallet = GetWallet(litecoin, Common.Alice.PubKey);

            var bobBtcWallet = GetWallet(bitcoin, Common.Bob.PubKey);
            //var bobLtcWallet = GetWallet(litecoin, Common.Bob.PubKey);

            const decimal lastPrice = 0.000001m;
            const decimal lastQty   = 10m;

            var swap = new ClientSwap
            {
                Symbol = new Symbol {
                    Base = litecoin, Quote = bitcoin
                },
                Side  = Side.Buy,
                Price = lastPrice,
                Qty   = lastQty
            };

            var tx = await new BitcoinBasedSwapTransactionFactory()
                     .CreateSwapPaymentTxAsync(
                currency: bitcoin,
                swap: swap,
                fromWallets: new [] { aliceBtcWallet.Address },
                refundAddress: aliceBtcWallet.Address,
                toAddress: bobBtcWallet.Address,
                lockTime: DateTimeOffset.UtcNow.AddHours(1),
                secretHash: Common.SecretHash,
                secretSize: Common.Secret.Length,
                outputsSource: new BlockchainTxOutputSource())
                     .ConfigureAwait(false);

            Assert.NotNull(tx);

            return(tx);
        }
Пример #13
0
        public override async Task PartyRedeemAsync(ClientSwap swap)
        {
            Log.Debug("Create redeem for counterParty for swap {@swapId}", swap.Id);

            var walletAddress = (await Account
                                 .GetUnspentAddressesAsync(
                                     currency: Currency,
                                     amount: 0,
                                     fee: Eth.RedeemGasLimit,
                                     feePrice: Eth.GasPriceInGwei,
                                     isFeePerTransaction: false,
                                     addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress)
                                 .ConfigureAwait(false))
                                .FirstOrDefault();

            if (walletAddress == null)
            {
                Log.Error("Insufficient balance for party redeem. Cannot find the address containing the required amount of funds.");
                return;
            }

            var nonce = await EthereumNonceManager.Instance
                        .GetNonce(Eth, walletAddress.Address)
                        .ConfigureAwait(false);

            var message = new RedeemFunctionMessage
            {
                FromAddress  = walletAddress.Address,
                HashedSecret = swap.SecretHash,
                Secret       = swap.Secret,
                Nonce        = nonce,
                GasPrice     = Atomix.Ethereum.GweiToWei(Eth.GasPriceInGwei),
            };

            message.Gas = await EstimateGasAsync(message, new BigInteger(Eth.RedeemGasLimit))
                          .ConfigureAwait(false);

            var txInput = message.CreateTransactionInput(Eth.SwapContractAddress);

            var redeemTx = new EthereumTransaction(Eth, txInput)
            {
                Type = EthereumTransaction.OutputTransaction
            };

            var signResult = await SignTransactionAsync(redeemTx)
                             .ConfigureAwait(false);

            if (!signResult)
            {
                Log.Error("Transaction signing error");
                return;
            }

            await BroadcastTxAsync(swap, redeemTx)
            .ConfigureAwait(false);
        }
        public static ClientSwap ResolveSymbol(this ClientSwap swap, ISymbols symbols)
        {
            swap.Symbol = symbols.FirstOrDefault(s => s.Name == swap.Symbol?.Name);

            if (swap.Symbol == null)
            {
                throw new Exception("Symbol resolving error");
            }

            return(swap);
        }
        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
            });
        }
        public static ClientSwap ResolveRelationshipsByName(
            this ClientSwap swap,
            IList <Symbol> symbols)
        {
            if (swap == null)
            {
                return(null);
            }

            swap.Symbol = symbols.FirstOrDefault(s => s.Name == swap.Symbol?.Name);

            return(swap);
        }
        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
            });
        }
Пример #18
0
        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);
        }
Пример #19
0
        public Task <IBitcoinBasedTransaction> CreateSwapRefundTxAsync(
            IBitcoinBasedTransaction paymentTx,
            ClientSwap swap,
            string refundAddress,
            DateTimeOffset lockTime)
        {
            var currency    = (BitcoinBasedCurrency)paymentTx.Currency;
            var orderAmount = (long)(AmountHelper.QtyToAmount(swap.Side, swap.Qty, swap.Price) *
                                     currency.DigitsMultiplier);

            var swapOutputs = paymentTx.Outputs
                              .Cast <BitcoinBasedTxOutput>()
                              .Where(o => o.Value == orderAmount && o.IsSwapPayment)
                              .ToList();

            if (swapOutputs.Count != 1)
            {
                throw new Exception("Payment tx must have only one swap payment output");
            }

            var estimatedSigSize = EstimateSigSize(swapOutputs, forRefund: true);

            var txSize = currency
                         .CreateSwapRefundTx(
                unspentOutputs: swapOutputs,
                destinationAddress: refundAddress,
                changeAddress: refundAddress,
                amount: orderAmount,
                fee: 0,
                lockTime: lockTime)
                         .VirtualSize();

            var fee = (long)(currency.FeeRate * (txSize + estimatedSigSize));

            if (orderAmount - fee < 0)
            {
                throw new Exception($"Insufficient funds for fee. Available {orderAmount}, required {fee}");
            }

            var tx = currency.CreateSwapRefundTx(
                unspentOutputs: swapOutputs,
                destinationAddress: refundAddress,
                changeAddress: refundAddress,
                amount: orderAmount - fee,
                fee: fee,
                lockTime: lockTime);

            return(Task.FromResult(tx));
        }
        private async Task BroadcastRedeemAsync(ClientSwap swap, IBlockchainTransaction redeemTx)
        {
            var currency = swap.PurchasedCurrency;

            var txId = await currency.BlockchainApi
                       .BroadcastAsync(redeemTx)
                       .ConfigureAwait(false);

            if (txId == null)
            {
                throw new Exception("Transaction Id is null");
            }

            Log.Debug("Redeem tx {@txId} successfully broadcast for swap {@swapId}", txId, swap.Id);
        }
Пример #21
0
        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
            });
        }
        private static SwapCompactState CompactStateBySwap(ClientSwap swap)
        {
            if (swap.IsComplete)
            {
                return(SwapCompactState.Completed);
            }

            if (swap.IsCanceled)
            {
                return(SwapCompactState.Canceled);
            }

            return(swap.IsRefunded
                ? SwapCompactState.Refunded
                : SwapCompactState.InProgress);
        }
Пример #23
0
        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);
        }
Пример #24
0
        public async Task HandleSwapAsync(ClientSwap clientSwap)
        {
            var swap = await _account
                       .GetSwapByIdAsync(clientSwap.Id)
                       .ConfigureAwait(false);

            if (swap == null)
            {
                await RunSwapAsync(clientSwap)
                .ConfigureAwait(false);
            }
            else
            {
                await HandleExistingSwapAsync(swap, clientSwap)
                .ConfigureAwait(false);
            }
        }
Пример #25
0
        public override async Task PartyRedeemAsync(ClientSwap swap)
        {
            Log.Debug("Create redeem for acceptor for swap {@swapId}", swap.Id);

            var walletAddress = (await Account
                                 .GetUnspentAddressesAsync(
                                     currency: Currency,
                                     amount: 0,
                                     fee: Xtz.RedeemFee.ToTez() + Xtz.RedeemStorageLimit.ToTez(),
                                     feePrice: 0,
                                     isFeePerTransaction: false,
                                     addressUsagePolicy: AddressUsagePolicy.UseOnlyOneAddress)
                                 .ConfigureAwait(false))
                                .FirstOrDefault();

            if (walletAddress == null)
            {
                Log.Error("Insufficient balance for party redeem. Cannot find the address containing the required amount of funds.");
                return;
            }

            var redeemTx = new TezosTransaction(Xtz)
            {
                From         = walletAddress.Address,
                To           = Xtz.SwapContractAddress,
                Amount       = 0,
                Fee          = Xtz.RedeemFee,
                GasLimit     = Xtz.RedeemGasLimit,
                StorageLimit = Xtz.RedeemStorageLimit,
                Params       = RedeemParams(swap),
                Type         = TezosTransaction.OutputTransaction
            };

            var signResult = await SignTransactionAsync(redeemTx)
                             .ConfigureAwait(false);

            if (!signResult)
            {
                Log.Error("Transaction signing error");
                return;
            }

            await BroadcastTxAsync(swap, redeemTx)
            .ConfigureAwait(false);
        }
Пример #26
0
        public Task <IBitcoinBasedTransaction> CreateSwapRedeemTxAsync(
            IBitcoinBasedTransaction paymentTx,
            ClientSwap swap,
            string redeemAddress)
        {
            var currency    = (BitcoinBasedCurrency)paymentTx.Currency;
            var orderAmount = (long)(AmountHelper.QtyToAmount(swap.Side.Opposite(), swap.Qty, swap.Price) *
                                     currency.DigitsMultiplier);

            var swapOutputs = paymentTx
                              .SwapOutputs()
                              .ToList();

            if (swapOutputs.Count != 1)
            {
                throw new Exception("Payment tx must have only one swap payment output");
            }

            var estimatedSigSize = EstimateSigSize(swapOutputs);

            var txSize = currency
                         .CreateP2PkhTx(
                unspentOutputs: swapOutputs,
                destinationAddress: redeemAddress,
                changeAddress: redeemAddress,
                amount: orderAmount,
                fee: 0)
                         .VirtualSize();

            var fee = (long)(currency.FeeRate * (txSize + estimatedSigSize));

            if (orderAmount - fee < 0)
            {
                throw new Exception($"Insufficient funds for fee. Available {orderAmount}, required {fee}");
            }

            var tx = currency.CreateP2PkhTx(
                unspentOutputs: swapOutputs,
                destinationAddress: redeemAddress,
                changeAddress: redeemAddress,
                amount: orderAmount - fee,
                fee: fee);

            return(Task.FromResult(tx));
        }
Пример #27
0
        private async Task InitiateSwapAsync(ClientSwap swap)
        {
            Log.Debug("Initiate swap {@swapId}", swap.Id);

            if (swap.Secret == null)
            {
                var secret = _account.Wallet
                             .GetDeterministicSecret(swap.SoldCurrency, swap.TimeStamp);

                swap.Secret = secret.SubArray(0, CurrencySwap.DefaultSecretSize);
                RaiseSwapUpdated(swap, SwapStateFlags.HasSecret);

                secret.Clear();
            }

            if (swap.SecretHash == null)
            {
                swap.SecretHash = CurrencySwap.CreateSwapSecretHash(swap.Secret);
                RaiseSwapUpdated(swap, SwapStateFlags.HasSecretHash);
            }

            if (swap.ToAddress == null)
            {
                swap.ToAddress = (await _account
                                  .GetRedeemAddressAsync(swap.PurchasedCurrency)
                                  .ConfigureAwait(false))
                                 .Address;
                RaiseSwapUpdated(swap, SwapStateFlags.Empty);
            }

            var purchasedCurrencyBalance = await _account
                                           .GetBalanceAsync(swap.PurchasedCurrency)
                                           .ConfigureAwait(false);

            swap.RewardForRedeem = purchasedCurrencyBalance.Available < swap.PurchasedCurrency.GetDefaultRedeemFee() &&
                                   !(swap.PurchasedCurrency is BitcoinBasedCurrency)
                ? swap.PurchasedCurrency.GetDefaultRedeemFee() * 2
                : 0;
            RaiseSwapUpdated(swap, SwapStateFlags.Empty);

            _swapClient.SwapInitiateAsync(swap);
        }
        private async Task <IBitcoinBasedTransaction> CreatePaymentTxAsync(
            ClientSwap swap,
            string refundAddress,
            DateTimeOffset lockTime)
        {
            var currency = (BitcoinBasedCurrency)swap.SoldCurrency;

            Log.Debug("Create swap payment {@currency} tx for swap {@swapId}",
                      currency.Name,
                      swap.Id);

            var unspentAddresses = (await Account
                                    .GetUnspentAddressesAsync(currency)
                                    .ConfigureAwait(false))
                                   .ToList()
                                   .SortList((a, b) => a.AvailableBalance().CompareTo(b.AvailableBalance()))
                                   .Select(a => a.Address);

            var tx = await _transactionFactory
                     .CreateSwapPaymentTxAsync(
                currency : currency,
                swap : swap,
                fromWallets : unspentAddresses,
                refundAddress : refundAddress,
                toAddress : swap.PartyAddress,
                lockTime : lockTime,
                secretHash : swap.SecretHash,
                secretSize : DefaultSecretSize,
                outputsSource : new LocalTxOutputSource(Account))
                     .ConfigureAwait(false);

            if (tx == null)
            {
                throw new InternalException(
                          code: Errors.TransactionCreationError,
                          description: $"Payment tx creation error for swap {swap.Id}");
            }

            Log.Debug("Payment tx successfully created for swap {@swapId}", swap.Id);

            return(tx);
        }
        private async Task <IBitcoinBasedTransaction> SignPaymentTxAsync(
            ClientSwap swap,
            IBitcoinBasedTransaction paymentTx)
        {
            Log.Debug("Sign payment tx for swap {@swapId}", swap.Id);

            var tx = await new BitcoinBasedSwapSigner(Account)
                     .SignPaymentTxAsync(paymentTx)
                     .ConfigureAwait(false);

            if (tx == null)
            {
                throw new InternalException(
                          code: Errors.TransactionSigningError,
                          description: $"Payment tx signing error for swap {swap.Id}");
            }

            Log.Debug("Payment tx successfully signed for swap {@swapId}", swap.Id);

            return(tx);
        }
        public Task <bool> UpdateSwapAsync(ClientSwap swap)
        {
            try
            {
                lock (_syncRoot)
                {
                    using (var db = new LiteDatabase(ConnectionString, _bsonMapper))
                    {
                        var result = db.GetCollection <ClientSwap>(SwapsCollectionName)
                                     .Update(swap);

                        return(Task.FromResult(result));
                    }
                }
            }
            catch (Exception e)
            {
                Log.Error(e, "Swap update error");
            }

            return(Task.FromResult(false));
        }