コード例 #1
0
        public async Task BroadCastTransaction(Guid operationId, Transaction tx)
        {
            var operation = await _operationMetaRepository.Get(operationId);

            if (operation == null)
            {
                throw new BusinessException("Operation not found", ErrorCode.BadInputParameter);
            }

            if (await _operationEventRepository.Exist(operationId, OperationEventType.Broadcasted))
            {
                throw new BusinessException("Transaction already brodcasted", ErrorCode.TransactionAlreadyBroadcasted);
            }
            var hash = tx.GetHash().ToString();
            await _transactionBlobStorage.AddOrReplaceTransaction(operationId, hash, TransactionBlobType.BeforeBroadcast, tx.ToHex());

            var lastBlockHeight = await _blockChainProvider.GetLastBlockHeight();

            await _blockChainProvider.BroadCastTransaction(tx);

            await _observableOperationRepository.InsertOrReplace(ObervableOperation.Create(operation, BroadcastStatus.InProgress, hash, lastBlockHeight));

            await _unconfirmedTransactionRepository.InsertOrReplace(UnconfirmedTransaction.Create(operationId, hash));

            await _operationEventRepository.InsertIfNotExist(OperationEvent.Create(operationId, OperationEventType.Broadcasted));

            await _spentOutputRepository.InsertSpentOutputs(operationId, tx.Inputs.Select(i => new Output(i.PrevOut)));
        }
コード例 #2
0
        public async Task <Transaction> GetOrBuildTransferTransaction(Guid operationId,
                                                                      BitcoinAddress fromAddress,
                                                                      BitcoinAddress toAddress,
                                                                      string assetId,
                                                                      Money amountToSend,
                                                                      bool includeFee)
        {
            if (await _operationMetaRepository.Exist(operationId))
            {
                var alreadyBuildedTransaction = await _transactionBlobStorage.GetTransaction(operationId, TransactionBlobType.Initial);

                return(Transaction.Parse(alreadyBuildedTransaction));
            }

            var buildedTransaction = await _transactionBuilder.GetTransferTransaction(fromAddress, toAddress, amountToSend, includeFee);

            await _transactionBlobStorage.AddOrReplaceTransaction(operationId, TransactionBlobType.Initial,
                                                                  buildedTransaction.TransactionData.ToHex());

            var operation = OperationMeta.Create(operationId, fromAddress.ToString(), toAddress.ToString(), assetId,
                                                 buildedTransaction.Amount.Satoshi, buildedTransaction.Fee.Satoshi, includeFee);
            await _operationMetaRepository.Insert(operation);

            return(buildedTransaction.TransactionData);
        }
コード例 #3
0
        public async Task <string> GetOrBuildTransferTransaction(Guid operationId,
                                                                 BitcoinAddress fromAddress,
                                                                 PubKey fromAddressPubkey,
                                                                 BitcoinAddress toAddress,
                                                                 string assetId,
                                                                 Money amountToSend,
                                                                 bool includeFee)
        {
            if (await _operationMetaRepository.Exist(operationId))
            {
                return(await _transactionBlobStorage.GetTransaction(operationId, TransactionBlobType.Initial));
            }

            var buildedTransaction = await _transactionBuilder.GetTransferTransaction(fromAddress, fromAddressPubkey, toAddress, amountToSend, includeFee);

            var transactionContext =
                Serializer.ToString((tx: buildedTransaction.TransactionData, spentCoins: buildedTransaction.SpentCoins));

            await _transactionBlobStorage.AddOrReplaceTransaction(operationId,
                                                                  TransactionBlobType.Initial,
                                                                  transactionContext);

            var operation = OperationMeta.Create(operationId, fromAddress.ToString(), toAddress.ToString(), assetId,
                                                 buildedTransaction.Amount.Satoshi, buildedTransaction.Fee.Satoshi, includeFee);
            await _operationMetaRepository.Insert(operation);

            return(transactionContext);
        }
コード例 #4
0
        public async Task BroadcastTransaction(BroadcastingTransaction transaction, QueueTriggeringContext context)
        {
            try
            {
                var signedByClientTr = await _transactionBlobStorage.GetTransaction(transaction.TransactionId, TransactionBlobType.Initial);

                var signedByExchangeTr = await _exchangeSignatureApi.SignTransaction(signedByClientTr);

                if (!await _settingsRepository.Get(Constants.CanBeBroadcastedSetting, true))
                {
                    await _transactionBlobStorage.AddOrReplaceTransaction(transaction.TransactionId, TransactionBlobType.Signed, signedByExchangeTr);

                    context.MoveMessageToPoison(transaction.ToJson());
                    return;
                }

                var tr = new Transaction(signedByExchangeTr);
                await _broadcastService.BroadcastTransaction(transaction.TransactionId, tr);

                if (transaction.TransactionCommandType == TransactionCommandType.SegwitTransferToHotwallet)
                {
                    _cqrsEngine.PublishEvent(new CashinCompletedEvent {
                        OperationId = transaction.TransactionId, TxHash = tr.GetHash().ToString()
                    }, BitcoinBoundedContext.Name);
                }

                if (transaction.TransactionCommandType == TransactionCommandType.Transfer)
                {
                    _cqrsEngine.PublishEvent(new CashoutCompletedEvent {
                        OperationId = transaction.TransactionId, TxHash = tr.GetHash().ToString()
                    }, BitcoinBoundedContext.Name);
                }
            }
            catch (RPCException e)
            {
                if (e.Message != transaction.LastError)
                {
                    await _logger.WriteWarningAsync("BroadcastingTransactionFunction", "BroadcastTransaction", $"Id: [{transaction.TransactionId}]", $"Message: {e.Message} Code:{e.RPCCode} CodeMessage:{e.RPCCodeMessage}");
                }

                transaction.LastError = e.Message;

                var unacceptableTx = _unacceptableTxErrors.Any(o => e.Message.Contains(o));

                if (transaction.DequeueCount >= _settings.MaxDequeueCount || unacceptableTx)
                {
                    context.MoveMessageToPoison();
                }
                else
                {
                    transaction.DequeueCount++;
                    context.MoveMessageToEnd(transaction.ToJson());
                    context.SetCountQueueBasedDelay(_settings.MaxQueueDelay, 200);
                }
            }
        }
コード例 #5
0
        public async Task <BuiltTransactionInfo> GetOrBuildTransferTransaction(Guid operationId,
                                                                               BitcoinAddress fromAddress,
                                                                               PubKey fromAddressPubkey,
                                                                               BitcoinAddress toAddress,
                                                                               string assetId,
                                                                               Money amountToSend,
                                                                               bool includeFee)
        {
            var existingOperation = await _operationMetaRepository.Get(operationId);

            if (existingOperation != null)
            {
                var existingAmount = existingOperation.IncludeFee
                    ? existingOperation.AmountSatoshi + existingOperation.FeeSatoshi
                    : existingOperation.AmountSatoshi;

                if (existingOperation.FromAddress != fromAddress.ToString() ||
                    existingOperation.ToAddress != toAddress.ToString() ||
                    existingOperation.AssetId != assetId ||
                    existingOperation.IncludeFee != includeFee ||
                    existingAmount != amountToSend.Satoshi)
                {
                    throw new BusinessException("Conflict in operation parameters", ErrorCode.Conflict);
                }

                return(await GetExistingTransaction(existingOperation.OperationId, existingOperation.Hash));
            }

            var builtTransaction = await _transactionBuilder.GetTransferTransaction(fromAddress, fromAddressPubkey, toAddress, amountToSend, includeFee);

            var builtTransactionInfo = new BuiltTransactionInfo
            {
                TransactionHex = builtTransaction.TransactionData.ToHex(),
                UsedCoins      = builtTransaction.UsedCoins
            };

            var txHash = builtTransaction.TransactionData.GetHash().ToString();

            await _transactionBlobStorage.AddOrReplaceTransaction(operationId, txHash, TransactionBlobType.Initial, builtTransactionInfo.ToJson(_network));

            var operation = OperationMeta.Create(operationId, txHash, fromAddress.ToString(), toAddress.ToString(), assetId,
                                                 builtTransaction.Amount.Satoshi, builtTransaction.Fee.Satoshi, includeFee);

            if (await _operationMetaRepository.TryInsert(operation))
            {
                return(builtTransactionInfo);
            }

            existingOperation = await _operationMetaRepository.Get(operationId);

            return(await GetExistingTransaction(operationId, existingOperation.Hash));
        }
コード例 #6
0
        public async Task ProcessMessage(TransactionQueueMessage message, QueueTriggeringContext context)
        {
            CreateTransactionResponse transactionResponse;

            try
            {
                var request = await _signRequestRepository.GetSignRequest(message.TransactionId);

                if (request?.Invalidated == true)
                {
                    context.MoveMessageToPoison(message.ToJson());
                    return;
                }

                switch (message.Type)
                {
                case TransactionCommandType.Issue:
                    var issue = message.Command.DeserializeJson <IssueCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetIssueTransaction(
                        OpenAssetsHelper.ParseAddress(issue.Address),
                        issue.Amount, await _assetRepository.GetAssetById(issue.Asset), message.TransactionId);

                    break;

                case TransactionCommandType.Transfer:
                    var transfer = message.Command.DeserializeJson <TransferCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetTransferTransaction(
                        OpenAssetsHelper.ParseAddress(transfer.SourceAddress),
                        OpenAssetsHelper.ParseAddress(transfer.DestinationAddress), transfer.Amount,
                        await _assetRepository.GetAssetById(transfer.Asset), message.TransactionId);

                    break;

                case TransactionCommandType.TransferAll:
                    var transferAll = message.Command.DeserializeJson <TransferAllCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetTransferAllTransaction(
                        OpenAssetsHelper.ParseAddress(transferAll.SourceAddress),
                        OpenAssetsHelper.ParseAddress(transferAll.DestinationAddress),
                        message.TransactionId);

                    break;

                case TransactionCommandType.Swap:
                    var swap = message.Command.DeserializeJson <SwapCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetSwapTransaction(
                        OpenAssetsHelper.ParseAddress(swap.MultisigCustomer1),
                        swap.Amount1,
                        await _assetRepository.GetAssetById(swap.Asset1),
                        OpenAssetsHelper.ParseAddress(swap.MultisigCustomer2),
                        swap.Amount2,
                        await _assetRepository.GetAssetById(swap.Asset2),
                        message.TransactionId);

                    break;

                case TransactionCommandType.Destroy:
                    var destroy = message.Command.DeserializeJson <DestroyCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetDestroyTransaction(
                        OpenAssetsHelper.ParseAddress(destroy.Address),
                        destroy.Amount,
                        await _assetRepository.GetAssetById(destroy.Asset),
                        message.TransactionId);

                    break;

                case TransactionCommandType.SegwitTransferToHotwallet:
                    var segwitTransfer = message.Command.DeserializeJson <SegwitTransferCommand>();
                    transactionResponse = await _lykkeTransactionBuilderService.GetTransferFromSegwitWallet(
                        OpenAssetsHelper.ParseAddress(segwitTransfer.SourceAddress), message.TransactionId);

                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
            }
            catch (BackendException e) when(e.Code == ErrorCode.NoCoinsFound)
            {
                if (message.Type == TransactionCommandType.SegwitTransferToHotwallet)
                {
                    _cqrsEngine.PublishEvent(new CashinCompletedEvent {
                        OperationId = message.TransactionId
                    }, BitcoinBoundedContext.Name);
                }
                return;
            }
            catch (BackendException e)
            {
                if (e.Text != message.LastError)
                {
                    await _logger.WriteWarningAsync("TransactionBuildFunction", "ProcessMessage", $"Id: [{message.TransactionId}], cmd: [{message.Command}]", e.Text);
                }

                message.LastError = e.Text;
                if (message.DequeueCount >= _settings.MaxDequeueCount)
                {
                    context.MoveMessageToPoison(message.ToJson());
                }
                else
                {
                    message.DequeueCount++;
                    context.MoveMessageToEnd(message.ToJson());
                    context.SetCountQueueBasedDelay(_settings.MaxQueueDelay, 200);
                }
                return;
            }

            await _transactionBlobStorage.AddOrReplaceTransaction(message.TransactionId, TransactionBlobType.Initial, transactionResponse.Transaction);


            await _queueFactory(Constants.BroadcastingQueue).PutRawMessageAsync(new BroadcastingTransaction
            {
                TransactionCommandType = message.Type,
                TransactionId          = message.TransactionId
            }.ToJson());
        }
コード例 #7
0
        public async Task <IActionResult> CreateCashout([FromBody] TransferRequest model)
        {
            if (model.Amount <= 0)
            {
                throw new BackendException("Amount can't be less or equal to zero", ErrorCode.BadInputParameter);
            }

            var sourceAddress = OpenAssetsHelper.ParseAddress(model.SourceAddress);

            if (sourceAddress == null)
            {
                throw new BackendException("Invalid source address provided", ErrorCode.InvalidAddress);
            }

            var destAddress = OpenAssetsHelper.ParseAddress(model.DestinationAddress);

            if (destAddress == null)
            {
                throw new BackendException("Invalid destination address provided", ErrorCode.InvalidAddress);
            }

            var asset = await _assetRepository.GetAssetById(model.Asset);

            if (asset == null)
            {
                throw new BackendException("Provided asset is missing in database", ErrorCode.AssetNotFound);
            }

            if (model.Fee.GetValueOrDefault() < 0)
            {
                throw new BackendException("Fee must be greater than or equal to zero", ErrorCode.BadInputParameter);
            }

            if (model.Amount <= model.Fee.GetValueOrDefault())
            {
                throw new BackendException("Amount is less than fee", ErrorCode.BadInputParameter);
            }

            var transactionId = await _builder.AddTransactionId(model.TransactionId, model.ToJson());

            CreateTransactionResponse createTransactionResponse;

            if (OpenAssetsHelper.IsBitcoin(asset.Id) && model.Fee.HasValue)
            {
                createTransactionResponse = await _builder.GetPrivateTransferTransaction(sourceAddress, destAddress, model.Amount,
                                                                                         model.Fee.Value, transactionId);

                await _transactionSignRequestRepository.DoNotSign(transactionId);
            }
            else
            {
                createTransactionResponse = await _builder.GetTransferTransaction(sourceAddress, destAddress, model.Amount, asset, transactionId, true, true);
            }

            await _transactionBlobStorage.AddOrReplaceTransaction(transactionId, TransactionBlobType.Initial, createTransactionResponse.Transaction);

            return(Ok(new TransactionResponse
            {
                Transaction = createTransactionResponse.Transaction,
                TransactionId = createTransactionResponse.TransactionId,
                Fee = (createTransactionResponse as PrivateTransferResponse)?.Fee ?? 0
            }));
        }