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))); }
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); }
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); }
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); } } }
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)); }
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()); }
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 })); }