public IActionResult Call([FromBody] BuildCallContractTransactionRequest request)
        {
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            BuildCallContractTransactionResponse response = this.smartContractTransactionService.BuildCallTx(request);

            if (!response.Success)
            {
                return(BadRequest(Json(response)));
            }

            Transaction transaction = this.network.CreateTransaction(response.Hex);

            this.walletManager.ProcessTransaction(transaction, null, null, false);
            this.broadcasterManager.BroadcastTransactionAsync(transaction).GetAwaiter().GetResult();

            return(Json(response));
        }
예제 #2
0
        public IActionResult ListAccounts([FromQuery] ListAccountsModel request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                IEnumerable <HdAccount> result = this.walletManager.GetAccounts(request.WalletName);
                return(this.Json(result.Select(a => a.Name)));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #3
0
        public IActionResult GetUnusedAddress([FromQuery] GetUnusedAddressModel request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                HdAddress result = this.walletManager.GetUnusedAddress(new WalletAccountReference(request.WalletName, request.AccountName));
                return(this.Json(result.Address));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #4
0
        private IActionResult VoteAddKickFedMember(HexPubKeyModel request, bool addMember)
        {
            Guard.NotNull(request, nameof(request));

            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            if (!this.fedManager.IsFederationMember)
            {
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Only federation members can vote", string.Empty));
            }

            try
            {
                var key = new PubKey(request.PubKeyHex);

                if (IsMultisigMember(this.network, key))
                {
                    return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Multisig members can't be voted on", string.Empty));
                }

                IFederationMember federationMember = new FederationMember(key);
                byte[]            fedMemberBytes   = (this.network.Consensus.ConsensusFactory as PoAConsensusFactory).SerializeFederationMember(federationMember);

                this.votingManager.ScheduleVote(new VotingData()
                {
                    Key  = addMember ? VoteKey.AddFederationMember : VoteKey.KickFederationMember,
                    Data = fedMemberBytes
                });

                return(this.Ok());
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "There was a problem executing a command.", e.ToString()));
            }
        }
        public IActionResult GetReceipt([FromQuery] string txHash)
        {
            this.logger.LogTrace("(){0}:{1}", nameof(txHash), txHash);
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODELSTATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            uint256 txHashNum            = new uint256(txHash);
            SmartContractReceipt receipt = this.receiptStorage.GetReceipt(txHashNum);

            if (receipt == null)
            {
                this.logger.LogTrace("(-)[RECEIPT_NOT_FOUND]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest,
                                                       "Receipt not found.",
                                                       "Could not find a stored transaction for this hash."));
            }

            return(Json(ReceiptModel.FromSmartContractReceipt(receipt, this.network)));
        }
예제 #6
0
        private IActionResult VoteAddKickFedMember(CollateralFederationMemberModel request, bool addMember)
        {
            Guard.NotNull(request, nameof(request));

            // TODO remove this line when multisig recreation is implemented.
            return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Disabled in current version.", string.Empty));

            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            if (!this.fedManager.IsFederationMember)
            {
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "Only federation members can vote", string.Empty));
            }

            try
            {
                var key = new PubKey(request.PubKeyHex);

                IFederationMember federationMember = new CollateralFederationMember(key, new Money(request.CollateralAmountSatoshis), request.CollateralMainchainAddress);

                byte[] fedMemberBytes = (this.network.Consensus.ConsensusFactory as PoAConsensusFactory).SerializeFederationMember(federationMember);

                this.votingManager.ScheduleVote(new VotingData()
                {
                    Key  = addMember ? VoteKey.AddFederationMember : VoteKey.KickFederationMember,
                    Data = fedMemberBytes
                });

                return(this.Ok());
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, "There was a problem executing a command.", e.ToString()));
            }
        }
        public IActionResult EstimateOfflineColdStakingSetupFee([FromBody] SetupOfflineColdStakingRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // Checks the request is valid.
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODEL_STATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                Money amount = Money.Parse(request.Amount);

                Money estimatedFee = this.ColdStakingManager.EstimateSetupTransactionFee(
                    this.walletTransactionHandler,
                    request.ColdWalletAddress,
                    request.HotWalletAddress,
                    request.WalletName,
                    request.WalletAccount,
                    null,
                    amount,
                    request.SubtractFeeFromAmount,
                    true,
                    request.SegwitChangeAddress,
                    request.SplitCount);

                this.logger.LogTrace("(-):'{0}'", estimatedFee);
                return(this.Json(estimatedFee));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                this.logger.LogTrace("(-)[ERROR]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
        public IActionResult CreateColdStakingAccount([FromBody] CreateColdStakingAccountRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // Checks that the request is valid.
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODEL_STATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                ExtPubKey extPubKey = null;

                try
                {
                    extPubKey = ExtPubKey.Parse(request.ExtPubKey);
                }
                catch
                {
                }

                var model = new CreateColdStakingAccountResponse
                {
                    AccountName = this.ColdStakingManager.GetOrCreateColdStakingAccount(request.WalletName, request.IsColdWalletAccount, request.WalletPassword, extPubKey).Name
                };

                this.logger.LogTrace("(-):'{0}'", model);
                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                this.logger.LogTrace("(-)[ERROR]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #9
0
        public async Task <IActionResult> BuildAndSendCallSmartContractTransactionAsync([FromBody] BuildCallContractTransactionRequest request)
        {
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            // Ignore this check if the node is running dev mode.
            if (this.nodeSettings.DevMode == null && !this.connectionManager.ConnectedPeers.Any())
            {
                this.logger.LogTrace("(-)[NO_CONNECTED_PEERS]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.Forbidden, "Can't send transaction as the node requires at least one connection.", string.Empty));
            }

            BuildCallContractTransactionResponse response = this.smartContractTransactionService.BuildCallTx(request);

            if (!response.Success)
            {
                return(this.Json(response));
            }

            Transaction transaction = this.network.CreateTransaction(response.Hex);

            await this.broadcasterManager.BroadcastTransactionAsync(transaction);

            // Check if transaction was actually added to a mempool.
            TransactionBroadcastEntry transactionBroadCastEntry = this.broadcasterManager.GetTransaction(transaction.GetHash());

            if (transactionBroadCastEntry?.TransactionBroadcastState == Features.Wallet.Broadcasting.TransactionBroadcastState.CantBroadcast)
            {
                this.logger.LogError("Exception occurred: {0}", transactionBroadCastEntry.ErrorMessage);
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, transactionBroadCastEntry.ErrorMessage, "Transaction Exception"));
            }

            response.Message = $"Your CALL method {request.MethodName} transaction was successfully built and sent. Check the receipt using the transaction ID once it has been included in a new block.";

            return(this.Json(response));
        }
예제 #10
0
        public IActionResult GetXServerStats()
        {
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                var serverStats = new GetXServerStatsResult()
                {
                    Connected = this.xServerManager.ConnectedSeeds.Count,
                    Nodes     = this.xServerManager.ConnectedSeeds
                };

                return(this.Json(serverStats));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #11
0
        public IActionResult GetBalance([FromQuery] WalletBalanceRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                var model = new WalletBalanceModel();

                IEnumerable <AccountBalance> balances = this.walletManager.GetBalances(request.WalletName, request.AccountName);

                foreach (AccountBalance balance in balances)
                {
                    HdAccount account = balance.Account;
                    model.AccountsBalances.Add(new AccountBalanceModel
                    {
                        CoinType          = this.coinType,
                        Name              = account.Name,
                        HdPath            = account.HdPath,
                        AmountConfirmed   = balance.AmountConfirmed,
                        AmountUnconfirmed = balance.AmountUnconfirmed
                    });
                }

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #12
0
        public IActionResult GetColdStakingAddress([FromQuery] GetColdStakingAddressRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // Checks that the request is valid.
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODEL_STATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                var address =
                    this.ColdStakingManager.GetFirstUnusedColdStakingAddress(request.WalletName,
                                                                             request.IsColdWalletAddress);

                var model = new GetColdStakingAddressResponse
                {
                    Address = request.Segwit ? address?.Bech32Address : address?.Address
                };

                if (model.Address == null)
                {
                    throw new WalletException("The cold staking account does not exist.");
                }

                this.logger.LogTrace("(-):'{0}'", model);
                return(Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                this.logger.LogTrace("(-)[ERROR]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
        public IActionResult ColdStakingWithdrawal([FromBody] ColdStakingWithdrawalRequest request)
        {
            Guard.NotNull(request, nameof(request));

            this.logger.LogTrace("({0}:'{1}')", nameof(request), request);

            // Checks the request is valid.
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODEL_STATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                Money amount    = Money.Parse(request.Amount);
                Money feeAmount = Money.Parse(request.Fees);

                Transaction transaction = this.ColdStakingManager.GetColdStakingWithdrawalTransaction(this.walletTransactionHandler,
                                                                                                      request.ReceivingAddress, request.WalletName, request.WalletPassword, amount, feeAmount);

                var model = new ColdStakingWithdrawalResponse
                {
                    TransactionHex = transaction.ToHex()
                };

                this.logger.LogTrace("(-):'{0}'", model);

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                this.logger.LogTrace("(-)[ERROR]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
        public async Task <IActionResult> GetTransactionAsync(string id)
        {
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            if (string.IsNullOrWhiteSpace(id))
            {
                throw new ArgumentNullException("id", "id must be specified");
            }

            try
            {
                Transaction trx   = this.blockRepository.GetTransactionById(new uint256(id));
                var         model = new TransactionVerboseModel(trx, this.network);
                return(Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #15
0
        public IActionResult GetStorage([FromQuery] GetStorageRequest request)
        {
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODELSTATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            var height = request.BlockHeight.HasValue ? request.BlockHeight.Value : (ulong)this.chainIndexer.Height;

            ChainedHeader chainedHeader = this.chainIndexer.GetHeader((int)height);

            var scHeader = chainedHeader?.Header as ISmartContractBlockHeader;

            IStateRepositoryRoot stateAtHeight = this.stateRoot.GetSnapshotTo(scHeader.HashStateRoot.ToBytes());

            uint160 addressNumeric = request.ContractAddress.ToUint160(this.network);

            byte[] storageValue = stateAtHeight.GetStorageValue(addressNumeric, Encoding.UTF8.GetBytes(request.StorageKey));

            if (storageValue == null)
            {
                return(this.NotFound(new
                {
                    Message = string.Format("No data at storage with key {0}", request.StorageKey)
                }));
            }

            // Interpret the storage bytes as an object of the given type
            object interpretedStorageValue = this.InterpretStorageValue(request.DataType, storageValue);

            // Use MethodParamStringSerializer to serialize the interpreted object to a string
            string serialized = MethodParameterStringSerializer.Serialize(interpretedStorageValue, this.network);

            return(this.Json(serialized));
        }
        public IActionResult ReplaceFederationMemberIp([FromBody] ReplaceFederationMemberIpModel model)
        {
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                IPEndPoint endPointToReplace = model.EndPoint.ToIPEndPoint(this.fullNode.Network.DefaultPort);
                IPEndPoint endPointToUse     = model.EndPointToUse.ToIPEndPoint(this.fullNode.Network.DefaultPort);

                if (!this.federatedPegSettings.FederationNodeIpEndPoints.Contains(endPointToReplace))
                {
                    return(this.Json($"{endPointToReplace} does not exist in the federation."));
                }

                this.federatedPegSettings.FederationNodeIpEndPoints.Remove(endPointToReplace);
                this.federatedPegSettings.FederationNodeIpAddresses.Remove(endPointToReplace.Address);

                if (this.federatedPegSettings.FederationNodeIpEndPoints.Contains(endPointToUse))
                {
                    return(this.Json($"{endPointToUse} already exists in the federation."));
                }

                this.federatedPegSettings.FederationNodeIpEndPoints.Add(endPointToUse);
                this.federatedPegSettings.FederationNodeIpAddresses.Add(endPointToUse.Address);

                return(this.Json($"{endPointToReplace} has been replaced with {endPointToUse}."));
            }
            catch (Exception e)
            {
                this.logger.Error("Exception thrown calling /api/FederationGateway/{0}: {1}.", FederationGatewayRouteEndPoint.FederationMemberIpReplace, e.Message);
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #17
0
        protected async Task <IActionResult> Execute <TRequest>(TRequest request, CancellationToken token, Func <TRequest, CancellationToken, Task <IActionResult> > action, bool checkModelState = true)
        {
            if (checkModelState && !this.ModelState.IsValid)
            {
                Guard.NotNull(request, nameof(request));
                this.Logger.LogTrace($"{nameof(request)}(-)[MODEL_STATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                return(await action(request, token));
            }
            catch (FeatureException e)
            {
                this.Logger.LogError("Exception occurred: {0}", e.ToString());
                return(e.MapToErrorResponse());
            }
            catch (Exception e)
            {
                this.Logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #18
0
        public IActionResult Recover([FromBody] WalletRecoveryRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                Wallet wallet = this.walletManager.RecoverWallet(request.Password, request.Name, request.Mnemonic, request.CreationDate, passphrase: request.Passphrase);

                this.SyncFromBestHeightForRecoveredWallets(request.CreationDate);

                return(this.Ok());
            }
            catch (WalletException e)
            {
                // indicates that this wallet already exists
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.Conflict, e.Message, e.ToString()));
            }
            catch (FileNotFoundException e)
            {
                // indicates that this wallet does not exist
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.NotFound, "Wallet not found.", e.ToString()));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #19
0
        public IActionResult SetupColdStaking([FromBody] SetupColdStakingRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // Checks the request is valid.
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODEL_STATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                var amount    = Money.Parse(request.Amount);
                var feeAmount = Money.Parse(request.Fees);

                var transaction = this.ColdStakingManager.GetColdStakingSetupTransaction(
                    this.walletTransactionHandler, request.ColdWalletAddress, request.HotWalletAddress,
                    request.WalletName, request.WalletAccount, request.WalletPassword, amount, feeAmount,
                    request.SegwitChangeAddress, request.PayToScript);

                var model = new SetupColdStakingResponse
                {
                    TransactionHex = transaction.ToHex()
                };

                this.logger.LogTrace("(-):'{0}'", model);
                return(Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                this.logger.LogTrace("(-)[ERROR]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #20
0
        public IActionResult SignWantedSystemMessageAsync([FromBody] SignWantedSystemMessageRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                // parse the transaction
                Transaction tx = this.network.CreateTransaction(request.TransactionHex);

                // check if we have the message transaction in our message store
                WalletManager wm = this.walletManager as WalletManager;
                if (!wm.WantedSystemMessages.Any(t => t.Key == tx.GetHash()))
                {
                    throw new Exception($"The transcation with hash '{tx.GetHash()}' is not in the message store. You must upload it first by using the upload-wanted-system-message API.");
                }
                //WantedSystemMessageModel wantedMessage = wm.WantedSystemMessages.First(t => t.Key == tx.GetHash()).Value;

                // get the signing keys
                List <BitcoinExtKey> privateKeys = new List <BitcoinExtKey>();
                if (!String.IsNullOrWhiteSpace(request.SigningKey))
                {
                    // parse request.SigningKey into a Bitcoin Private Key
                    try
                    {
                        privateKeys.Add(new BitcoinExtKey(request.SigningKey));
                    }
                    catch
                    {
                        throw new Exception($"The signing key you provided is not in a valid format.");
                    }
                }
                else
                {
                    throw new Exception($"You must provide a signing key in order to sign the message.");

                    // TODO: add our own private keys
                }

                // sign the transaction with the key
                var reviewerAddress = wm.ReviewerAddresses[request.ReviewerAddress];
                if (reviewerAddress == null)
                {
                    throw new Exception($"The reviewer '{request.ReviewerAddress}' was not found in the address book.");
                }

                TransactionBuilder tb = new TransactionBuilder(this.network);
                tb.AddCoins((this.walletTransactionHandler as WalletTransactionHandler).GetCoinsForReviewersAddress(reviewerAddress));
                tb.AddCoins(tx);
                Transaction signedTx = tb.AddKeys(privateKeys.ToArray()).SignTransaction(tx);

                if (signedTx.GetHash() == tx.GetHash())
                {
                    throw new Exception($"The signing key you provided is not associated with the message or it is already signed with that key.");
                }

                // return the (partially) signed transaction
                SignWantedSystemMessageModel model = new SignWantedSystemMessageModel
                {
                    TransactionHex = signedTx.ToHex(),
                    WasSigned      = request.TransactionHex != signedTx.ToHex()
                };

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #21
0
        public IActionResult SendWantedSystemMessageAsync([FromBody] SendWantedSystemMessageRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            if (!this.connectionManager.ConnectedPeers.Any())
            {
                throw new WalletException("Can't send transaction: sending transaction requires at least one connection!");
            }

            try
            {
                Transaction transaction = this.network.CreateTransaction(request.Hex);

                WalletSendTransactionModel model = new WalletSendTransactionModel
                {
                    TransactionId = transaction.GetHash(),
                    Outputs       = new List <TransactionOutputModel>()
                };

                foreach (var output in transaction.Outputs)
                {
                    if (WantedSystemMessageTemplate.Instance.CheckScriptPubKey(output.ScriptPubKey))
                    {
                        model.Outputs.Add(new TransactionOutputModel
                        {
                            Address = $"N/A (Wanted System Message)",
                            Amount  = output.Value
                        });
                    }
                    else
                    {
                        model.Outputs.Add(new TransactionOutputModel
                        {
                            Address = output.ScriptPubKey.GetDestinationAddress(this.network).ToString(),
                            Amount  = output.Value
                        });
                    }
                }

                if (transaction.Inputs.All(tri => String.IsNullOrEmpty(tri.ScriptSig.ToString())))
                {
                    throw new Exception("This transcation is not signed. In order to publish a transaction on the network, it must be fully signed first.");
                }

                if (transaction.Inputs.Any(tri => String.IsNullOrEmpty(tri.ScriptSig.ToString())))
                {
                    throw new Exception("This transcation is only partially signed. In order to publish a transaction on the network, it must be fully signed first.");
                }

                this.walletManager.ProcessTransaction(transaction, null, null, false);

                this.broadcasterManager.BroadcastTransactionAsync(transaction).GetAwaiter().GetResult();

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #22
0
        public IActionResult CreateReviewerAddressAsync([FromBody] CreateReviewerAddressRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                // calculate the Manager password hash
                byte[] binaryPassword = System.Text.Encoding.ASCII.GetBytes(request.RsaPassword);

                Org.BouncyCastle.Crypto.Digests.Sha512Digest sha = new Org.BouncyCastle.Crypto.Digests.Sha512Digest();
                sha.BlockUpdate(binaryPassword, 0, binaryPassword.Length);

                byte[] shaOutput = new byte[512 / 8];
                sha.DoFinal(shaOutput, 0);

                NBitcoin.DataEncoders.HexEncoder he = new NBitcoin.DataEncoders.HexEncoder();
                string rsaPasswordHashHex           = he.EncodeData(shaOutput);

                // create the multisig address
                PubKey[] groupMemberKeys = request.SignaturePubKeys.Select(pubKeyHex => new PubKey(pubKeyHex)).ToArray();

                var scriptPubKey = PayToMultiSigTemplate
                                   .Instance
                                   .GenerateScriptPubKey(request.RequeiredSignatureCount, groupMemberKeys);

                PublicReviewerAddressModel model = new PublicReviewerAddressModel
                {
                    PublicApiUrl = request.PublicApiUrl
                };

                // check if the API is reachable and the address can be added to the watch list
                Uri apiRequestUri = new Uri(new Uri(model.PublicApiUrl), $"/api/WatchOnlyWallet/watch?address={scriptPubKey.Hash.GetAddress(this.network).ToString()}");
                try
                {
                    HttpWebRequest apiRequest = (HttpWebRequest)WebRequest.Create(apiRequestUri);

                    ASCIIEncoding encoding = new ASCIIEncoding();
                    string        postData = "";
                    byte[]        data     = encoding.GetBytes(postData);

                    apiRequest.Method        = "POST";
                    apiRequest.ContentType   = "application/x-www-form-urlencoded";
                    apiRequest.ContentLength = data.Length;

                    using (Stream stream = apiRequest.GetRequestStream())
                    {
                        stream.Write(data, 0, data.Length);
                    }

                    HttpWebResponse apiResponse = (HttpWebResponse)apiRequest.GetResponse();

                    string responseString = new StreamReader(apiResponse.GetResponseStream()).ReadToEnd();

                    if (apiResponse.StatusCode != HttpStatusCode.OK)
                    {
                        throw new Exception($"The API request '{apiRequestUri.ToString()}' returned the status code '{apiResponse.StatusCode}'.");
                    }
                }
                catch (Exception e)
                {
                    throw new Exception($"The API request '{apiRequestUri.ToString()}' returned an error '{e.Message}'.");
                }


                // generate the RSA keypair for the address
                AsymmetricCipherKeyPair rsaKeyPair = GetRSAKeyPairFromSeed(request.RsaPassword + scriptPubKey.Hash.GetAddress(this.network).ToString());

                RsaKeyParameters rsaPublicKey = rsaKeyPair.Public as RsaKeyParameters;
                RsaPublicKey     pbk          = new RsaPublicKey()
                {
                    Exponent = rsaPublicKey.Exponent.ToByteArrayUnsigned(),
                    Modulus  = rsaPublicKey.Modulus.ToByteArrayUnsigned()
                };

                RsaPrivateCrtKeyParameters rsaPrivateKey = rsaKeyPair.Private as RsaPrivateCrtKeyParameters;
                RsaPrivateKey prk = new RsaPrivateKey()
                {
                    DP             = rsaPrivateKey.DP.ToByteArrayUnsigned(),
                    DQ             = rsaPrivateKey.DQ.ToByteArrayUnsigned(),
                    Exponent       = rsaPrivateKey.Exponent.ToByteArrayUnsigned(),
                    Modulus        = rsaPrivateKey.Modulus.ToByteArrayUnsigned(),
                    P              = rsaPrivateKey.P.ToByteArrayUnsigned(),
                    PublicExponent = rsaPrivateKey.PublicExponent.ToByteArrayUnsigned(),
                    Q              = rsaPrivateKey.Q.ToByteArrayUnsigned(),
                    QInv           = rsaPrivateKey.QInv.ToByteArrayUnsigned()
                };

                // return all the information we have
                model = new PublicReviewerAddressModel
                {
                    Network            = this.network.ToString(),
                    Address            = scriptPubKey.Hash.GetAddress(this.network).ToString(),
                    PublicName         = request.PublicName,
                    GroupName          = request.GroupName,
                    ValidFrom          = request.ValidFrom.ToString("o"),
                    ValidUntil         = request.ValidUntil.ToString("o"),
                    ScriptPubKeyHex    = scriptPubKey.ToHex(),
                    RsaPublicKeyHex    = pbk.ToHex(),
                    RsaPrivateKeyHex   = prk.ToHex(),
                    RsaPasswordHashHex = rsaPasswordHashHex,
                    PublicApiUrl       = request.PublicApiUrl
                };

                ((WalletManager)this.walletManager).AddReviewerAddressToReviewerStore(model);

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #23
0
        public IActionResult GetBlock([FromQuery] SearchByHashRequest query)
        {
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                uint256 blockId = uint256.Parse(query.Hash);

                ChainedHeader chainedHeader = this.chainIndexer.GetHeader(blockId);

                if (chainedHeader == null)
                {
                    return(this.Ok("Block not found"));
                }

                Block block = chainedHeader.Block ?? this.blockStore.GetBlock(blockId);

                // In rare occasions a block that is found in the
                // indexer may not have been pushed to the store yet.
                if (block == null)
                {
                    return(this.Ok("Block not found"));
                }

                if (!query.OutputJson)
                {
                    return(this.Json(block));
                }

                BlockModel blockModel = query.ShowTransactionDetails
                    ? new BlockTransactionDetailsModel(block, chainedHeader, this.chainIndexer.Tip, this.network)
                    : new BlockModel(block, chainedHeader, this.chainIndexer.Tip, this.network);

                if (this.network.Consensus.IsProofOfStake)
                {
                    var posBlock = block as PosBlock;

                    blockModel.PosBlockSignature = posBlock.BlockSignature.ToHex(this.network.Consensus.ConsensusFactory);
                    blockModel.PosBlockTrust     = new Target(chainedHeader.GetBlockTarget()).ToUInt256().ToString();
                    blockModel.PosChainTrust     = chainedHeader.ChainWork.ToString(); // this should be similar to ChainWork

                    if (this.stakeChain != null)
                    {
                        BlockStake blockStake = this.stakeChain.Get(blockId);

                        blockModel.PosModifierv2 = blockStake?.StakeModifierV2.ToString();
                        blockModel.PosFlags      = blockStake?.Flags == BlockFlag.BLOCK_PROOF_OF_STAKE ? "proof-of-stake" : "proof-of-work";
                        blockModel.PosHashProof  = blockStake?.HashProof.ToString();
                    }
                }

                return(this.Json(blockModel));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #24
0
        public IActionResult UploadWantedSystemMessageAsync([FromBody] UploadWantedSystemMessageRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                Transaction transaction = this.network.CreateTransaction(request.TransactionHex);

                var wantedMessageOuts = transaction.Outputs.Where(txOut => WantedSystemMessageTemplate.Instance.CheckScriptPubKey(txOut.ScriptPubKey));

                if (wantedMessageOuts.Count() == 0)
                {
                    throw new Exception("The transaction you provided doesn't contain any Wanted System Messages.");
                }

                var wm = this.walletManager as WalletManager;

                WantedSystemMessageModel wsmm = null;

                // let's make sure that the transaction is put into the message store
                wsmm = wm.AddWantedSystemMessageToMessageStore(transaction);

                // add the partially signed transaction to the message store
                // TODO: do not limit this check to the first input
                if (transaction.Inputs[0].ScriptSig.Length > 0)
                {
                    wsmm = wm.AddPartiallySignedTxToMessageStore(transaction);
                }


                // get the reviewer's address details
                var reviewerAddress = wm.ReviewerAddresses[request.ReviewerAddress];
                if (reviewerAddress == null)
                {
                    throw new Exception($"The reviewer '{request.ReviewerAddress}' was not found in the address book.");
                }
                var multiSigParams = PayToMultiSigTemplate.Instance.ExtractScriptPubKeyParameters(reviewerAddress.ScriptPubKey);

                // try to combine the partially signed signatures
                TransactionBuilder tb = new TransactionBuilder(this.network);
                tb.AddCoins((this.walletTransactionHandler as WalletTransactionHandler).GetCoinsForReviewersAddress(reviewerAddress));

                var fullySignedTx = tb.CombineSignatures(wsmm.PartiallySignedTransactions.Select(stx => this.network.CreateTransaction(stx.TransactionHex)).ToArray());
                if (fullySignedTx != null)
                {
                    var checkResults = fullySignedTx.Check();

                    // TODO: do not limit this check to the first input
                    string[] signatures     = fullySignedTx.Inputs[0].ScriptSig.ToString().Split(' ');
                    int      signatureCount = signatures.Count(s => s != "0");

                    if ((checkResults != TransactionCheckResult.Success) || (signatureCount < multiSigParams.SignatureCount))
                    {
                        fullySignedTx = null;
                    }
                }

                // return the fully signed transaction if available
                UploadWantedSystemMessageModel model = new UploadWantedSystemMessageModel
                {
                    WantedSystemMessage       = wsmm,
                    FullySignedTransactionHex = fullySignedTx == null ? "" : fullySignedTx.ToHex()
                };

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
        public IActionResult SetupOfflineColdStaking([FromBody] SetupOfflineColdStakingRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // Checks the request is valid.
            if (!this.ModelState.IsValid)
            {
                this.logger.LogTrace("(-)[MODEL_STATE_INVALID]");
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                Money amount    = Money.Parse(request.Amount);
                Money feeAmount = Money.Parse(request.Fees);

                (Transaction transaction, TransactionBuildContext context) = this.ColdStakingManager.GetColdStakingSetupTransaction(
                    this.walletTransactionHandler,
                    request.ColdWalletAddress,
                    request.HotWalletAddress,
                    request.WalletName,
                    request.WalletAccount,
                    null,
                    amount,
                    feeAmount,
                    request.SubtractFeeFromAmount,
                    true,
                    request.SplitCount,
                    request.SegwitChangeAddress);

                // TODO: We use the same code in the regular wallet for offline signing request construction, perhaps it should be moved to a common method
                // Need to be able to look up the keypath for the UTXOs that were used.
                IEnumerable <UnspentOutputReference> spendableTransactions = this.ColdStakingManager.GetSpendableTransactionsInAccount(
                    new WalletAccountReference(request.WalletName, request.WalletAccount)).ToList();

                var utxos     = new List <UtxoDescriptor>();
                var addresses = new List <AddressDescriptor>();
                foreach (ICoin coin in context.TransactionBuilder.FindSpentCoins(transaction))
                {
                    utxos.Add(new UtxoDescriptor()
                    {
                        Amount        = coin.TxOut.Value.ToUnit(MoneyUnit.BTC).ToString(),
                        TransactionId = coin.Outpoint.Hash.ToString(),
                        Index         = coin.Outpoint.N.ToString(),
                        ScriptPubKey  = coin.TxOut.ScriptPubKey.ToHex()
                    });

                    UnspentOutputReference outputReference = spendableTransactions.FirstOrDefault(u => u.Transaction.Id == coin.Outpoint.Hash && u.Transaction.Index == coin.Outpoint.N);

                    if (outputReference != null)
                    {
                        bool segwit = outputReference.Transaction.ScriptPubKey.IsScriptType(ScriptType.P2WPKH);
                        addresses.Add(new AddressDescriptor()
                        {
                            Address = segwit ? outputReference.Address.Bech32Address : outputReference.Address.Address, AddressType = segwit ? "p2wpkh" : "p2pkh", KeyPath = outputReference.Address.HdPath
                        });
                    }
                }

                // Return transaction hex, UTXO list, address list. The offline signer will infer from the transaction structure that a cold staking setup is being made.
                var model = new BuildOfflineSignResponse()
                {
                    WalletName          = request.WalletName,
                    WalletAccount       = request.WalletAccount,
                    Fee                 = context.TransactionFee.ToUnit(MoneyUnit.BTC).ToString(),
                    UnsignedTransaction = transaction.ToHex(),
                    Utxos               = utxos,
                    Addresses           = addresses
                };

                this.logger.LogTrace("(-):'{0}'", model);
                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                this.logger.LogTrace("(-)[ERROR]");
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #26
0
        public IActionResult ListSpendableTransactionOuts([FromBody] ListSpendableTransactionOutsRequest request)
        {
            Guard.NotNull(request, nameof(request));

            // checks the request is valid
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                string requestWalletName  = null;
                string requestAccountName = null;

                if (String.IsNullOrWhiteSpace(request.WalletName) || String.IsNullOrWhiteSpace(request.AccountName))
                {
                    // let's find the proper wallet and account first
                    foreach (var walletName in this.walletManager.GetWalletsNames())
                    {
                        HdAddress address = null;
                        foreach (var hdAccount in this.walletManager.GetWallet(walletName).GetAccountsByCoinType(this.coinType))
                        {
                            address = hdAccount.ExternalAddresses.FirstOrDefault(hdAddress => hdAddress.Address.ToString() == request.Address);
                            if (address == null)
                            {
                                address = hdAccount.InternalAddresses.FirstOrDefault(hdAddress => hdAddress.Address.ToString() == request.Address);
                            }

                            if (address != null)
                            {
                                requestAccountName = hdAccount.Name;
                                break;
                            }
                        }

                        if (requestAccountName != null)
                        {
                            requestWalletName = walletName;
                            break;
                        }
                    }

                    if ((requestWalletName == null) || (requestAccountName == null))
                    {
                        throw new Exception("The address you requested is not in this wallet.");
                    }
                }
                else
                {
                    requestWalletName  = request.WalletName;
                    requestAccountName = request.AccountName;
                }

                WalletAccountReference account = new WalletAccountReference(requestWalletName, requestAccountName);

                List <DetailedSpendableTransactionModel> transactionList = new List <DetailedSpendableTransactionModel>();
                foreach (var spendableOutput in this.walletManager.GetSpendableTransactionsInAccount(account, request.MinConfirmations).OrderByDescending(a => a.Transaction.Amount))
                {
                    if (spendableOutput.Transaction.Amount == 0)
                    {
                        continue;
                    }

                    if (!String.IsNullOrWhiteSpace(request.Address) && (spendableOutput.Address.Address.ToString() != request.Address))
                    {
                        continue;
                    }

                    transactionList.Add(new DetailedSpendableTransactionModel()
                    {
                        Address         = spendableOutput.Address.Address.ToString(),
                        TransactionHash = spendableOutput.Transaction.Id,
                        Index           = spendableOutput.Transaction.Index,
                        Amount          = spendableOutput.Transaction.Amount.Satoshi,
                        ScriptPubKey    = spendableOutput.Transaction.ScriptPubKey.ToHex()
                    });
                }

                ListSpendableTransactionsModel model = new ListSpendableTransactionsModel
                {
                    Network               = this.network.ToString(),
                    WalletName            = account.WalletName,
                    AccountName           = account.AccountName,
                    SpendableTransactions = transactionList
                };

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #27
0
        public IActionResult GetHistory([FromQuery] WalletHistoryRequest request)
        {
            Guard.NotNull(request, nameof(request));

            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            try
            {
                var model = new WalletHistoryModel();

                // Get a list of all the transactions found in an account (or in a wallet if no account is specified), with the addresses associated with them.
                IEnumerable <AccountHistory> accountsHistory = this.walletManager.GetHistory(request.WalletName, request.AccountName);

                foreach (AccountHistory accountHistory in accountsHistory)
                {
                    var transactionItems = new List <TransactionItemModel>();

                    List <FlatHistory> items = accountHistory.History.OrderByDescending(o => o.Transaction.CreationTime).Take(200).ToList();

                    // Represents a sublist containing only the transactions that have already been spent.
                    List <FlatHistory> spendingDetails = items.Where(t => t.Transaction.SpendingDetails != null).ToList();

                    // Represents a sublist of transactions associated with receive addresses + a sublist of already spent transactions associated with change addresses.
                    // In effect, we filter out 'change' transactions that are not spent, as we don't want to show these in the history.
                    List <FlatHistory> history = items.Where(t => !t.Address.IsChangeAddress() || (t.Address.IsChangeAddress() && !t.Transaction.IsSpendable())).ToList();

                    // Represents a sublist of 'change' transactions.
                    List <FlatHistory> allchange = items.Where(t => t.Address.IsChangeAddress()).ToList();

                    foreach (FlatHistory item in history)
                    {
                        TransactionData transaction = item.Transaction;
                        HdAddress       address     = item.Address;

                        // We don't show in history transactions that are outputs of staking transactions.
                        if (transaction.IsCoinStake != null && transaction.IsCoinStake.Value && transaction.SpendingDetails == null)
                        {
                            continue;
                        }

                        // First we look for staking transaction as they require special attention.
                        // A staking transaction spends one of our inputs into 2 outputs, paid to the same address.
                        if (transaction.SpendingDetails?.IsCoinStake != null && transaction.SpendingDetails.IsCoinStake.Value)
                        {
                            // We look for the 2 outputs related to our spending input.
                            List <FlatHistory> relatedOutputs = items.Where(h => h.Transaction.Id == transaction.SpendingDetails.TransactionId && h.Transaction.IsCoinStake != null && h.Transaction.IsCoinStake.Value).ToList();
                            if (relatedOutputs.Any())
                            {
                                // Add staking transaction details.
                                // The staked amount is calculated as the difference between the sum of the outputs and the input and should normally be equal to 1.
                                var stakingItem = new TransactionItemModel
                                {
                                    Type             = TransactionItemType.Staked,
                                    ToAddress        = address.Address,
                                    Amount           = relatedOutputs.Sum(o => o.Transaction.Amount) - transaction.Amount,
                                    Id               = transaction.SpendingDetails.TransactionId,
                                    Timestamp        = transaction.SpendingDetails.CreationTime,
                                    ConfirmedInBlock = transaction.SpendingDetails.BlockHeight
                                };

                                transactionItems.Add(stakingItem);
                            }

                            // No need for further processing if the transaction itself is the output of a staking transaction.
                            if (transaction.IsCoinStake != null)
                            {
                                continue;
                            }
                        }

                        // Create a record for a 'receive' transaction.
                        if (!address.IsChangeAddress())
                        {
                            // Add incoming fund transaction details.
                            var receivedItem = new TransactionItemModel
                            {
                                Type             = TransactionItemType.Received,
                                ToAddress        = address.Address,
                                Amount           = transaction.Amount,
                                Id               = transaction.Id,
                                Timestamp        = transaction.CreationTime,
                                ConfirmedInBlock = transaction.BlockHeight
                            };

                            transactionItems.Add(receivedItem);
                        }

                        // If this is a normal transaction (not staking) that has been spent, add outgoing fund transaction details.
                        if (transaction.SpendingDetails != null && transaction.SpendingDetails.IsCoinStake == null)
                        {
                            // Create a record for a 'send' transaction.
                            uint256 spendingTransactionId = transaction.SpendingDetails.TransactionId;
                            var     sentItem = new TransactionItemModel
                            {
                                Type             = TransactionItemType.Send,
                                Id               = spendingTransactionId,
                                Timestamp        = transaction.SpendingDetails.CreationTime,
                                ConfirmedInBlock = transaction.SpendingDetails.BlockHeight,
                                Amount           = Money.Zero
                            };

                            // If this 'send' transaction has made some external payments, i.e the funds were not sent to another address in the wallet.
                            if (transaction.SpendingDetails.Payments != null)
                            {
                                sentItem.Payments = new List <PaymentDetailModel>();
                                foreach (PaymentDetails payment in transaction.SpendingDetails.Payments)
                                {
                                    sentItem.Payments.Add(new PaymentDetailModel
                                    {
                                        DestinationAddress = payment.DestinationAddress,
                                        Amount             = payment.Amount
                                    });

                                    sentItem.Amount += payment.Amount;
                                }
                            }

                            // Get the change address for this spending transaction.
                            FlatHistory changeAddress = allchange.FirstOrDefault(a => a.Transaction.Id == spendingTransactionId);

                            // Find all the spending details containing the spending transaction id and aggregate the sums.
                            // This is our best shot at finding the total value of inputs for this transaction.
                            var inputsAmount = new Money(spendingDetails.Where(t => t.Transaction.SpendingDetails.TransactionId == spendingTransactionId).Sum(t => t.Transaction.Amount));

                            // The fee is calculated as follows: funds in utxo - amount spent - amount sent as change.
                            sentItem.Fee = inputsAmount - sentItem.Amount - (changeAddress == null ? 0 : changeAddress.Transaction.Amount);

                            // Mined/staked coins add more coins to the total out.
                            // That makes the fee negative. If that's the case ignore the fee.
                            if (sentItem.Fee < 0)
                            {
                                sentItem.Fee = 0;
                            }

                            if (!transactionItems.Contains(sentItem, new SentTransactionItemModelComparer()))
                            {
                                transactionItems.Add(sentItem);
                            }
                        }
                    }

                    model.AccountsHistoryModel.Add(new AccountHistoryModel
                    {
                        TransactionsHistory = transactionItems.OrderByDescending(t => t.Timestamp).ToList(),
                        Name     = accountHistory.Account.Name,
                        CoinType = this.coinType,
                        HdPath   = accountHistory.Account.HdPath
                    });
                }

                return(this.Json(model));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }
예제 #28
0
        //[ProducesResponseType(typeof(void), 401)]
        public IActionResult GetBlockAsync(string id, [FromQuery] BlockQueryRequest query)
        {
            if (!this.ModelState.IsValid)
            {
                return(ModelStateErrors.BuildErrorResponse(this.ModelState));
            }

            ChainedHeader chainHeader = null;


            if (string.IsNullOrWhiteSpace(id))
            {
                throw new ArgumentNullException("id", "id must be block hash or block height");
            }

            // If the id is more than 50 characters, it is likely hash and not height.
            if (id.Length < 50)
            {
                chainHeader = this.chain.GetHeader(int.Parse(id));
            }
            else
            {
                chainHeader = this.chain.GetHeader(new uint256(id));
            }

            if (chainHeader == null)
            {
                return(new NotFoundObjectResult("Block not found"));
            }

            try
            {
                Block block = this.blockStoreCache.GetBlock(chainHeader.Header.GetHash());

                if (block == null)
                {
                    return(new NotFoundObjectResult("Block not found"));
                }

                PosBlockModel blockModel = new PosBlockModel(block, this.chain);

                if (this.stakeChain != null)
                {
                    BlockStake blockStake = this.stakeChain.Get(chainHeader.HashBlock);

                    if (blockStake != null)
                    {
                        blockModel.StakeTime       = blockStake.StakeTime;
                        blockModel.StakeModifierV2 = blockStake.StakeModifierV2;
                        blockModel.HashProof       = blockStake.HashProof;
                    }
                }

                return(Json(blockModel));
            }
            catch (Exception e)
            {
                this.logger.LogError("Exception occurred: {0}", e.ToString());
                return(ErrorHelpers.BuildErrorResponse(HttpStatusCode.BadRequest, e.Message, e.ToString()));
            }
        }