public TransactionHashInfo Post([FromBody] TransactionModel request)
        {
            if (!ShouldSendCoins(request.RecipientAddress))
            {
                return(new TransactionHashInfo()
                {
                    IsValid = false,
                    ErrorMessage = "User has received coins in the last hour."
                });
            }

            if (request.Value > _appSettings.MaxAddressDonationPerHour)
            {
                return(new TransactionHashInfo()
                {
                    IsValid = false,
                    ErrorMessage = String.Format("User cannot receive more than {0} coins per hour.", _appSettings.MaxAddressDonationPerHour)
                });
            }

            var             privateKey = CryptoUtils.HexToByteArray(_appSettings.PrivateKey);
            var             publicKey  = CryptoUtils.GetPublicFor(privateKey);
            var             date       = DateTime.UtcNow;
            TransactionData dataToSign = GetDataToSign(request, publicKey, date);

            var json          = Newtonsoft.Json.JsonConvert.SerializeObject(dataToSign);
            var msgHash       = CryptoUtils.GetSha256Bytes(json);
            var signedMessage = CryptoUtils.BouncyCastleSign(msgHash, privateKey);

            // send http request to node
            var signedTransactionData = new TransactionDataSigned
            {
                From            = _appSettings.FaucetAddress,
                To              = request.RecipientAddress,
                Value           = request.Value,
                Fee             = 0,
                SenderPubKey    = CryptoUtils.ByteArrayToHex(publicKey),
                DateCreated     = date,
                SenderSignature = CryptoUtils.ByteArrayToHex(signedMessage)
            };

            var res = HttpUtils.DoApiPost <TransactionDataSigned, TransactionHashInfo>(request.NodeUrl, "api/transactions", signedTransactionData);

            if (res.IsValid)
            {
                // add transaction record with limits
                var recordHistory = new TransactionHistory
                {
                    Address      = request.RecipientAddress,
                    DateReceived = res.DateReceived,
                    Amount       = request.Value
                };
                _dbService.Add(request.RecipientAddress, recordHistory);
            }
            return(res);
        }
        private void PropagateTransactionToPeers(TransactionDataSigned transaction)
        {
            var tasks = new List <Task>();

            dbService.GetPeers().ForEach(p =>
            {
                tasks.Add(Task.Run(() =>
                                   HttpUtils.DoApiPost <TransactionDataSigned, TransactionHashInfo>(p.Url, TRANSACTION_API_PATH, transaction)));
            });

            Task.WaitAll(tasks.ToArray());
        }
        public TransactionHashInfo CreateTransaction(TransactionDataSigned signedData)
        {
            var dateReceived   = DateTime.UtcNow;
            var newTransaction = new Transaction(signedData);

            if (dbService.GetTransactions().Any(t => t.TransactionHashHex == newTransaction.TransactionHashHex))
            {
                return(new TransactionHashInfo
                {
                    IsValid = true,
                    ErrorMessage = $"Duplicate transaction",
                    DateReceived = dateReceived,
                    TransactionHash = newTransaction.TransactionHashHex
                });
            }

            bool isValidTransaction = ValidateTransaction(signedData);

            if (!isValidTransaction)
            {
                return(new TransactionHashInfo
                {
                    IsValid = isValidTransaction,
                    ErrorMessage = "Transaction data is corrupted.",
                    DateReceived = dateReceived,
                    TransactionHash = ""
                });
            }

            //validate balance
            var bal      = GetBalance(signedData.From, 1).ConfirmedBalance.Balance;
            var hasFunds = (bal >= signedData.Value + signedData.Fee);

            if (!hasFunds)
            {
                return(new TransactionHashInfo
                {
                    IsValid = false,
                    ErrorMessage = $"Not enough funds. Available funds: {bal}, required funds: {signedData.Value} + {signedData.Fee} for the fee",
                    DateReceived = dateReceived,
                    TransactionHash = ""
                });
            }

            dbService.AddTransaction(newTransaction);
            PropagateTransactionToPeers(newTransaction);

            return(new TransactionHashInfo()
            {
                IsValid = isValidTransaction, DateReceived = dateReceived, TransactionHash = newTransaction.TransactionHashHex
            });
        }
        private bool ValidateTransaction(TransactionDataSigned signedData)
        {
            TransactionData toValidate  = new TransactionData(signedData);
            var             messageData = JsonConvert.SerializeObject(toValidate);

            // Validate signature
            var senderPublicKey = CryptoUtils.HexToByteArray(signedData.SenderPubKey);
            var senderSignature = CryptoUtils.HexToByteArray(signedData.SenderSignature);
            var messageDataHash = CryptoUtils.GetSha256Bytes(messageData);
            var isTestValid     = CryptoUtils.BouncyCastleVerify(messageDataHash, senderSignature, senderPublicKey);

            return(isTestValid);
        }
 public TransactionHashInfo Post([FromBody] TransactionDataSigned data)
 {
     return(_blockchainService.CreateTransaction(data));
 }