public IActionResult GetAllAddresses([FromQuery] GetAllAddressesModel request) { Guard.NotNull(request, nameof(request)); // Checks the request is valid. if (!this.ModelState.IsValid) { return(BuildErrorResponse(this.ModelState)); } try { Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccountByCoinType(request.AccountName, this.coinType); AddressesModel model = new AddressesModel { Addresses = account.GetCombinedAddresses().Select(address => new AddressModel { Address = address.Address, IsUsed = address.Transactions.Any(), IsChange = address.IsChangeAddress() }) }; 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 BuildCallContractTransactionResponse BuildCallTx(BuildCallContractTransactionRequest request) { AddressBalance addressBalance = this.walletManager.GetAddressBalance(request.Sender); if (addressBalance.AmountConfirmed == 0 && addressBalance.AmountUnconfirmed == 0) { return(BuildCallContractTransactionResponse.Failed(SenderNoBalanceError)); } var selectedInputs = new List <OutPoint>(); selectedInputs = this.walletManager.GetSpendableInputsForAddress(request.WalletName, request.Sender); uint160 addressNumeric = request.ContractAddress.ToUint160(this.network); ContractTxData txData; if (request.Parameters != null && request.Parameters.Any()) { try { object[] methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters); txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Gas)request.GasPrice, (Gas)request.GasLimit, addressNumeric, request.MethodName, methodParameters); } catch (MethodParameterStringSerializerException exception) { return(BuildCallContractTransactionResponse.Failed(exception.Message)); } } else { txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Gas)request.GasPrice, (Gas)request.GasLimit, addressNumeric, request.MethodName); } HdAddress senderAddress = null; if (!string.IsNullOrWhiteSpace(request.Sender)) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccountByCoinType(request.AccountName, this.coinType); if (account == null) { return(BuildCallContractTransactionResponse.Failed($"No account with the name '{request.AccountName}' could be found.")); } senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); } ulong totalFee = (request.GasPrice * request.GasLimit) + Money.Parse(request.FeeAmount); var context = new TransactionBuildContext(this.network) { AccountReference = new WalletAccountReference(request.WalletName, request.AccountName), TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { new Recipient { Amount = request.Amount, ScriptPubKey = new Script(this.callDataSerializer.Serialize(txData)) } }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); return(BuildCallContractTransactionResponse.Succeeded(request.MethodName, transaction, context.TransactionFee)); } catch (Exception exception) { return(BuildCallContractTransactionResponse.Failed(exception.Message)); } }
public EstimateFeeResult EstimateFee(ScTxFeeEstimateRequest request) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccount(request.AccountName); if (account == null) { return(EstimateFeeResult.Failure(AccountNotInWalletError, $"No account with the name '{request.AccountName}' could be found.")); } HdAddress senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); if (senderAddress == null) { return(EstimateFeeResult.Failure(SenderNotInWalletError, $"The given address {request.Sender} was not found in the wallet.")); } if (!this.CheckBalance(senderAddress.Address)) { return(EstimateFeeResult.Failure(InsufficientBalanceError, SenderNoBalanceError)); } List <OutPoint> selectedInputs = this.SelectInputs(request.WalletName, request.Sender, request.Outpoints); if (!selectedInputs.Any()) { return(EstimateFeeResult.Failure(InvalidOutpointsError, "Invalid list of request outpoints have been passed to the method. Please ensure that the outpoints are spendable by the sender address.")); } var recipients = new List <Recipient>(); foreach (RecipientModel recipientModel in request.Recipients) { BitcoinAddress bitcoinAddress = BitcoinAddress.Create(recipientModel.DestinationAddress, this.network); // If it's a potential SC address, check if it's a contract. if (bitcoinAddress is BitcoinPubKeyAddress bitcoinPubKeyAddress) { var address = new uint160(bitcoinPubKeyAddress.Hash.ToBytes()); if (this.stateRoot.IsExist(address)) { return(EstimateFeeResult.Failure(TransferFundsToContractError, $"The recipient address {recipientModel.DestinationAddress} is a contract. Transferring funds directly to a contract is not supported.")); } } recipients.Add(new Recipient { ScriptPubKey = bitcoinAddress.ScriptPubKey, Amount = recipientModel.Amount }); } // Build context var context = new TransactionBuildContext(this.network) { AccountReference = new WalletAccountReference(request.WalletName, request.AccountName), MinConfirmations = MinConfirmationsAllChecks, Shuffle = false, OpReturnData = request.OpReturnData, OpReturnAmount = string.IsNullOrEmpty(request.OpReturnAmount) ? null : Money.Parse(request.OpReturnAmount), SelectedInputs = selectedInputs, AllowOtherInputs = false, Recipients = recipients, ChangeAddress = senderAddress, // Unique for fee estimation TransactionFee = null, FeeType = FeeParser.Parse(request.FeeType), Sign = false, }; Money fee = this.walletTransactionHandler.EstimateFee(context); return(EstimateFeeResult.Success(fee)); }
public BuildCreateContractTransactionResponse BuildCreateTx(BuildCreateContractTransactionRequest request) { AddressBalance addressBalance = this.walletManager.GetAddressBalance(request.Sender); if (addressBalance.AmountConfirmed == 0 && addressBalance.AmountUnconfirmed == 0) { return(BuildCreateContractTransactionResponse.Failed(SenderNoBalanceError)); } var selectedInputs = new List <OutPoint>(); selectedInputs = this.walletManager.GetSpendableInputsForAddress(request.WalletName, request.Sender); ContractTxData txData; if (request.Parameters != null && request.Parameters.Any()) { try { object[] methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters); txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Gas)request.GasPrice, (Gas)request.GasLimit, request.ContractCode.HexToByteArray(), methodParameters); } catch (MethodParameterStringSerializerException exception) { return(BuildCreateContractTransactionResponse.Failed(exception.Message)); } } else { txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Gas)request.GasPrice, (Gas)request.GasLimit, request.ContractCode.HexToByteArray()); } HdAddress senderAddress = null; if (!string.IsNullOrWhiteSpace(request.Sender)) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccountByCoinType(request.AccountName, this.coinType); if (account == null) { return(BuildCreateContractTransactionResponse.Failed($"No account with the name '{request.AccountName}' could be found.")); } senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); } ulong totalFee = (request.GasPrice * request.GasLimit) + Money.Parse(request.FeeAmount); var walletAccountReference = new WalletAccountReference(request.WalletName, request.AccountName); byte[] serializedTxData = this.callDataSerializer.Serialize(txData); Result <ContractTxData> deserialized = this.callDataSerializer.Deserialize(serializedTxData); // We also want to ensure we're sending valid data: AKA it can be deserialized. if (deserialized.IsFailure) { return(BuildCreateContractTransactionResponse.Failed("Invalid data. If network requires code signing, check the code contains a signature.")); } // HACK // If requiring a signature, also check the signature. if (this.network is ISignedCodePubKeyHolder holder) { var signedTxData = (SignedCodeContractTxData)deserialized.Value; bool validSig = new ContractSigner().Verify(holder.SigningContractPubKey, signedTxData.ContractExecutionCode, signedTxData.CodeSignature); if (!validSig) { return(BuildCreateContractTransactionResponse.Failed("Signature in code does not come from required signing key.")); } } var recipient = new Recipient { Amount = request.Amount ?? "0", ScriptPubKey = new Script(serializedTxData) }; var context = new TransactionBuildContext(this.network) { AccountReference = walletAccountReference, TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { recipient }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); uint160 contractAddress = this.addressGenerator.GenerateAddress(transaction.GetHash(), 0); return(BuildCreateContractTransactionResponse.Succeeded(transaction, context.TransactionFee, contractAddress.ToBase58Address(this.network))); } catch (Exception exception) { return(BuildCreateContractTransactionResponse.Failed(exception.Message)); } }
private BuildCallContractTransactionResponse BuildCallTx(BuildCallContractTransactionRequest request) { this.logger.LogTrace(request.ToString()); AddressBalance addressBalance = this.walletManager.GetAddressBalance(request.Sender); if (addressBalance.AmountConfirmed == 0) { return(BuildCallContractTransactionResponse.Failed($"The 'Sender' address you're trying to spend from doesn't have a confirmed balance. Current unconfirmed balance: {addressBalance.AmountUnconfirmed}. Please check the 'Sender' address.")); } var selectedInputs = new List <OutPoint>(); selectedInputs = this.walletManager.GetSpendableTransactionsInWallet(request.WalletName, MinConfirmationsAllChecks).Where(x => x.Address.Address == request.Sender).Select(x => x.ToOutPoint()).ToList(); ulong gasPrice = ulong.Parse(request.GasPrice); ulong gasLimit = ulong.Parse(request.GasLimit); uint160 addressNumeric = new Address(request.ContractAddress).ToUint160(this.network); SmartContractCarrier carrier; if (request.Parameters != null && request.Parameters.Any()) { carrier = SmartContractCarrier.CallContract(ReflectionVirtualMachine.VmVersion, addressNumeric, request.MethodName, gasPrice, new Gas(gasLimit), request.Parameters); } else { carrier = SmartContractCarrier.CallContract(ReflectionVirtualMachine.VmVersion, addressNumeric, request.MethodName, gasPrice, new Gas(gasLimit)); } HdAddress senderAddress = null; if (!string.IsNullOrWhiteSpace(request.Sender)) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccountByCoinType(request.AccountName, this.coinType); senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); } ulong totalFee = (gasPrice * gasLimit) + Money.Parse(request.FeeAmount); var context = new TransactionBuildContext(this.network) { AccountReference = new WalletAccountReference(request.WalletName, request.AccountName), TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { new Recipient { Amount = request.Amount, ScriptPubKey = new Script(carrier.Serialize()) } }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); return(BuildCallContractTransactionResponse.Succeeded(request.MethodName, transaction, context.TransactionFee)); } catch (Exception exception) { return(BuildCallContractTransactionResponse.Failed(exception.Message)); } }
public BuildCreateContractTransactionResponse BuildCreateTx(BuildCreateContractTransactionRequest request) { if (!this.CheckBalance(request.Sender)) { return(BuildCreateContractTransactionResponse.Failed(SenderNoBalanceError)); } List <OutPoint> selectedInputs = this.SelectInputs(request.WalletName, request.Sender, request.Outpoints); if (!selectedInputs.Any()) { return(BuildCreateContractTransactionResponse.Failed("Invalid list of request outpoints have been passed to the method. Please ensure that the outpoints are spendable by the sender address")); } ContractTxData txData; if (request.Parameters != null && request.Parameters.Any()) { try { object[] methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters); txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, request.ContractCode.HexToByteArray(), methodParameters); } catch (MethodParameterStringSerializerException exception) { return(BuildCreateContractTransactionResponse.Failed(exception.Message)); } } else { txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasPrice, (Stratis.SmartContracts.RuntimeObserver.Gas)request.GasLimit, request.ContractCode.HexToByteArray()); } HdAddress senderAddress = null; if (!string.IsNullOrWhiteSpace(request.Sender)) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccount(request.AccountName); if (account == null) { return(BuildCreateContractTransactionResponse.Failed($"No account with the name '{request.AccountName}' could be found.")); } senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); } ulong totalFee = (request.GasPrice * request.GasLimit) + Money.Parse(request.FeeAmount); var walletAccountReference = new WalletAccountReference(request.WalletName, request.AccountName); byte[] serializedTxData = this.callDataSerializer.Serialize(txData); Result <ContractTxData> deserialized = this.callDataSerializer.Deserialize(serializedTxData); // We also want to ensure we're sending valid data: AKA it can be deserialized. if (deserialized.IsFailure) { return(BuildCreateContractTransactionResponse.Failed("Invalid data. If network requires code signing, check the code contains a signature.")); } var recipient = new Recipient { Amount = request.Amount ?? "0", ScriptPubKey = new Script(serializedTxData) }; var context = new TransactionBuildContext(this.network) { AccountReference = walletAccountReference, TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { recipient }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); uint160 contractAddress = this.addressGenerator.GenerateAddress(transaction.GetHash(), 0); return(BuildCreateContractTransactionResponse.Succeeded(transaction, context.TransactionFee, contractAddress.ToBase58Address(this.network))); } catch (Exception exception) { return(BuildCreateContractTransactionResponse.Failed(exception.Message)); } }
public GetTransactionModel GetTransaction(string txid) { if (!uint256.TryParse(txid, out uint256 trxid)) { throw new ArgumentException(nameof(txid)); } WalletAccountReference accountReference = this.GetWalletAccountReference(); Types.Wallet wallet = this.walletManager.GetWalletByName(accountReference.WalletName); HdAccount account = this.walletManager.GetAccounts(accountReference.WalletName).Single(a => a.Name == accountReference.AccountName); // Get the transaction from the wallet by looking into received and send transactions. List <HdAddress> addresses = account.GetCombinedAddresses().ToList(); List <TransactionOutputData> receivedTransactions = addresses.Where(r => !r.IsChangeAddress()).SelectMany(a => wallet.walletStore.GetForAddress(a.Address).Where(t => t.Id == trxid)).ToList(); List <TransactionOutputData> sendTransactions = addresses.SelectMany(a => wallet.walletStore.GetForAddress(a.Address).Where(t => t.SpendingDetails != null && t.SpendingDetails.TransactionId == trxid)).ToList(); if (!receivedTransactions.Any() && !sendTransactions.Any()) { throw new RPCServerException(RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id."); } // Get the block hash from the transaction in the wallet. TransactionOutputData transactionFromWallet = null; uint256 blockHash = null; int? blockHeight, blockIndex; if (receivedTransactions.Any()) { blockHeight = receivedTransactions.First().BlockHeight; blockIndex = receivedTransactions.First().BlockIndex; blockHash = receivedTransactions.First().BlockHash; transactionFromWallet = receivedTransactions.First(); } else { blockHeight = sendTransactions.First().SpendingDetails.BlockHeight; blockIndex = sendTransactions.First().SpendingDetails.BlockIndex; blockHash = blockHeight != null?this.ChainIndexer.GetHeader(blockHeight.Value).HashBlock : null; } // Get the block containing the transaction (if it has been confirmed). ChainedHeaderBlock chainedHeaderBlock = null; if (blockHash != null) { this.ConsensusManager.GetOrDownloadBlocks(new List <uint256> { blockHash }, b => { chainedHeaderBlock = b; }); } Block block = null; Transaction transactionFromStore = null; if (chainedHeaderBlock != null) { block = chainedHeaderBlock.Block; transactionFromStore = block.Transactions.Single(t => t.GetHash() == trxid); } DateTimeOffset transactionTime; bool isGenerated; string hex; if (transactionFromStore != null) { // TODO: Use block header time only. The transaction times will need to be uniformly set to a fixed value when an anti-malleability softfork activates if (transactionFromStore is IPosTransactionWithTime posTrx) { transactionTime = Utils.UnixTimeToDateTime(posTrx.Time); } else { transactionTime = Utils.UnixTimeToDateTime(chainedHeaderBlock.ChainedHeader.Header.Time); } isGenerated = transactionFromStore.IsCoinBase || transactionFromStore.IsCoinStake; hex = transactionFromStore.ToHex(); } else if (transactionFromWallet != null) { transactionTime = transactionFromWallet.CreationTime; isGenerated = transactionFromWallet.IsCoinBase == true || transactionFromWallet.IsCoinStake == true; hex = transactionFromWallet.Hex; } else { transactionTime = sendTransactions.First().SpendingDetails.CreationTime; isGenerated = false; hex = null; // TODO get from mempool } var model = new GetTransactionModel { Confirmations = blockHeight != null ? this.ConsensusManager.Tip.Height - blockHeight.Value + 1 : 0, Isgenerated = isGenerated ? true : (bool?)null, BlockHash = blockHash, BlockIndex = blockIndex ?? block?.Transactions.FindIndex(t => t.GetHash() == trxid), BlockTime = block?.Header.BlockTime.ToUnixTimeSeconds(), TransactionId = uint256.Parse(txid), TransactionTime = transactionTime.ToUnixTimeSeconds(), TimeReceived = transactionTime.ToUnixTimeSeconds(), Details = new List <GetTransactionDetailsModel>(), Hex = hex }; Money feeSent = Money.Zero; if (sendTransactions.Any()) { feeSent = wallet.GetSentTransactionFee(trxid); } // Send transactions details. foreach (PaymentDetails paymentDetail in sendTransactions.Select(s => s.SpendingDetails).SelectMany(sd => sd.Payments)) { // Only a single item should appear per destination address. if (model.Details.SingleOrDefault(d => d.Address == paymentDetail.DestinationAddress) == null) { model.Details.Add(new GetTransactionDetailsModel { Address = paymentDetail.DestinationAddress, Category = GetTransactionDetailsCategoryModel.Send, Amount = -paymentDetail.Amount.ToDecimal(MoneyUnit.BTC), Fee = -feeSent.ToDecimal(MoneyUnit.BTC), OutputIndex = paymentDetail.OutputIndex }); } } // Get the ColdStaking script template if available. Dictionary <string, ScriptTemplate> templates = this.walletManager.GetValidStakingTemplates(); ScriptTemplate coldStakingTemplate = templates.ContainsKey("ColdStaking") ? templates["ColdStaking"] : null; // Receive transactions details. foreach (TransactionOutputData trxInWallet in receivedTransactions) { // Skip the details if the script pub key is cold staking. // TODO: Verify if we actually need this any longer, after changing the internals to recognice account type! if (coldStakingTemplate != null && coldStakingTemplate.CheckScriptPubKey(trxInWallet.ScriptPubKey)) { continue; } GetTransactionDetailsCategoryModel category; if (isGenerated) { category = model.Confirmations > this.FullNode.Network.Consensus.CoinbaseMaturity ? GetTransactionDetailsCategoryModel.Generate : GetTransactionDetailsCategoryModel.Immature; } else { category = GetTransactionDetailsCategoryModel.Receive; } model.Details.Add(new GetTransactionDetailsModel { Address = trxInWallet.Address, Category = category, Amount = trxInWallet.Amount.ToDecimal(MoneyUnit.BTC), OutputIndex = trxInWallet.Index }); } model.Amount = model.Details.Sum(d => d.Amount); model.Fee = model.Details.FirstOrDefault(d => d.Category == GetTransactionDetailsCategoryModel.Send)?.Fee; return(model); }
public BuildContractTransactionResult BuildTx(BuildContractTransactionRequest request) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccount(request.AccountName); if (account == null) { return(BuildContractTransactionResult.Failure(AccountNotInWalletError, $"No account with the name '{request.AccountName}' could be found.")); } HdAddress senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); if (senderAddress == null) { return(BuildContractTransactionResult.Failure(SenderNotInWalletError, $"The given address {request.Sender} was not found in the wallet.")); } if (!this.CheckBalance(senderAddress.Address)) { return(BuildContractTransactionResult.Failure(InsufficientBalanceError, SenderNoBalanceError)); } (List <OutPoint> selectedInputs, string message) = SelectInputs(request.WalletName, request.Sender, request.Outpoints); if (!string.IsNullOrEmpty(message)) { return(BuildContractTransactionResult.Failure(InvalidOutpointsError, message)); } var recipients = new List <Recipient>(); foreach (RecipientModel recipientModel in request.Recipients) { var bitcoinAddress = BitcoinAddress.Create(recipientModel.DestinationAddress, this.network); // If it's a potential SC address, check if it's a contract. if (bitcoinAddress is BitcoinPubKeyAddress bitcoinPubKeyAddress) { var address = new uint160(bitcoinPubKeyAddress.Hash.ToBytes()); if (this.stateRoot.IsExist(address)) { return(BuildContractTransactionResult.Failure(TransferFundsToContractError, $"The recipient address {recipientModel.DestinationAddress} is a contract. Transferring funds directly to a contract is not supported.")); } } recipients.Add(new Recipient { ScriptPubKey = bitcoinAddress.ScriptPubKey, Amount = recipientModel.Amount }); } var context = new TransactionBuildContext(this.network) { AccountReference = new WalletAccountReference(request.WalletName, request.AccountName), TransactionFee = string.IsNullOrEmpty(request.FeeAmount) ? null : Money.Parse(request.FeeAmount), MinConfirmations = MinConfirmationsAllChecks, Shuffle = false, OpReturnData = request.OpReturnData, OpReturnAmount = string.IsNullOrEmpty(request.OpReturnAmount) ? null : Money.Parse(request.OpReturnAmount), WalletPassword = request.Password, SelectedInputs = selectedInputs, AllowOtherInputs = false, Recipients = recipients, ChangeAddress = senderAddress }; Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); var model = new WalletBuildTransactionModel { Hex = transaction.ToHex(), Fee = context.TransactionFee, TransactionId = transaction.GetHash() }; return(BuildContractTransactionResult.Success(model)); }
protected override IEnumerable <EventBase> GetMessages() { foreach (string walletName in this.walletManager.GetWalletsNames()) { WalletGeneralInfoClientEvent clientEvent = null; try { Wallet.Types.Wallet wallet = this.walletManager.GetWallet(walletName); IEnumerable <AccountBalance> balances = this.walletManager.GetBalances(walletName, calculatSpendable: true); IList <AccountBalanceModel> accountBalanceModels = new List <AccountBalanceModel>(); foreach (var balance in balances) { HdAccount account = balance.Account; var accountBalanceModel = new AccountBalanceModel { CoinType = wallet.Network.Consensus.CoinType, Name = account.Name, HdPath = account.HdPath, AmountConfirmed = balance.AmountConfirmed, AmountUnconfirmed = balance.AmountUnconfirmed, SpendableAmount = balance.SpendableAmount, Addresses = account.GetCombinedAddresses().Select(address => { (Money confirmedAmount, Money unConfirmedAmount, bool anytrx) = address.GetBalances(wallet.walletStore, account.IsNormalAccount()); return(new AddressModel { Address = address.Address, IsUsed = anytrx, IsChange = address.IsChangeAddress(), AmountConfirmed = confirmedAmount, AmountUnconfirmed = unConfirmedAmount }); }) }; accountBalanceModels.Add(accountBalanceModel); } clientEvent = new WalletGeneralInfoClientEvent { WalletName = walletName, WalletInfo = new WalletGeneralInfoModel { Network = wallet.Network, CreationTime = wallet.CreationTime, LastBlockSyncedHeight = wallet.AccountsRoot.Single().LastBlockSyncedHeight, ConnectedNodes = this.connectionManager.ConnectedPeers.Count(), ChainTip = this.chainIndexer.Tip.Height, IsChainSynced = this.chainIndexer.IsDownloaded(), IsDecrypted = true, }, AccountsBalances = accountBalanceModels }; // Get the wallet's file path. (string folder, IEnumerable <string> fileNameCollection) = this.walletManager.GetWalletsFiles(); string searchFile = Path.ChangeExtension(walletName, this.walletManager.GetWalletFileExtension()); string fileName = fileNameCollection.FirstOrDefault(i => i.Equals(searchFile)); if (!string.IsNullOrEmpty(folder) && !string.IsNullOrEmpty(fileName)) { clientEvent.WalletInfo.WalletFilePath = Path.Combine(folder, fileName); } } catch (Exception e) { this.log.LogError(e, "Exception occurred: {0}"); } if (null != clientEvent) { yield return(clientEvent); } } }
private BuildCreateContractTransactionResponse BuildCreateTx(BuildCreateContractTransactionRequest request) { this.logger.LogTrace(request.ToString()); AddressBalance addressBalance = this.walletManager.GetAddressBalance(request.Sender); if (addressBalance.AmountConfirmed == 0) { return(BuildCreateContractTransactionResponse.Failed($"The 'Sender' address you're trying to spend from doesn't have a confirmed balance. Current unconfirmed balance: {addressBalance.AmountUnconfirmed}. Please check the 'Sender' address.")); } var selectedInputs = new List <OutPoint>(); selectedInputs = this.walletManager.GetSpendableTransactionsInWallet(request.WalletName, MinConfirmationsAllChecks).Where(x => x.Address.Address == request.Sender).Select(x => x.ToOutPoint()).ToList(); ContractTxData txData; if (request.Parameters != null && request.Parameters.Any()) { var methodParameters = this.methodParameterStringSerializer.Deserialize(request.Parameters); txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Gas)request.GasPrice, (Gas)request.GasLimit, request.ContractCode.HexToByteArray(), methodParameters); } else { txData = new ContractTxData(ReflectionVirtualMachine.VmVersion, (Gas)request.GasPrice, (Gas)request.GasLimit, request.ContractCode.HexToByteArray()); } HdAddress senderAddress = null; if (!string.IsNullOrWhiteSpace(request.Sender)) { Features.Wallet.Wallet wallet = this.walletManager.GetWallet(request.WalletName); HdAccount account = wallet.GetAccountByCoinType(request.AccountName, this.coinType); senderAddress = account.GetCombinedAddresses().FirstOrDefault(x => x.Address == request.Sender); } ulong totalFee = (request.GasPrice * request.GasLimit) + Money.Parse(request.FeeAmount); var walletAccountReference = new WalletAccountReference(request.WalletName, request.AccountName); var recipient = new Recipient { Amount = request.Amount ?? "0", ScriptPubKey = new Script(this.callDataSerializer.Serialize(txData)) }; var context = new TransactionBuildContext(this.network) { AccountReference = walletAccountReference, TransactionFee = totalFee, ChangeAddress = senderAddress, SelectedInputs = selectedInputs, MinConfirmations = MinConfirmationsAllChecks, WalletPassword = request.Password, Recipients = new[] { recipient }.ToList() }; try { Transaction transaction = this.walletTransactionHandler.BuildTransaction(context); uint160 contractAddress = this.addressGenerator.GenerateAddress(transaction.GetHash(), 0); return(BuildCreateContractTransactionResponse.Succeeded(transaction, context.TransactionFee, contractAddress.ToBase58Address(this.network))); } catch (Exception exception) { return(BuildCreateContractTransactionResponse.Failed(exception.Message)); } }
public override async Task <WalletBuildTransactionModel> OfflineSignRequest(OfflineSignRequest request, CancellationToken cancellationToken) { return(await Task.Run(() => { Transaction unsignedTransaction = this.network.CreateTransaction(request.UnsignedTransaction); uint256 originalTxId = unsignedTransaction.GetHash(); var builder = new TransactionBuilder(this.network); var coins = new List <Coin>(); var signingKeys = new List <ISecret>(); ExtKey seedExtKey = this.walletManager.GetExtKey(new WalletAccountReference() { AccountName = request.WalletAccount, WalletName = request.WalletName }, request.WalletPassword); // Have to determine which private key to use for each UTXO being spent. bool coldStakingWithdrawal = false; foreach (UtxoDescriptor utxo in request.Utxos) { Script scriptPubKey = Script.FromHex(utxo.ScriptPubKey); coins.Add(new Coin(uint256.Parse(utxo.TransactionId), uint.Parse(utxo.Index), Money.Parse(utxo.Amount), scriptPubKey)); // Now try get the associated private key. We therefore need to determine the address that contains the UTXO. string address; if (scriptPubKey.IsScriptType(ScriptType.ColdStaking)) { ColdStakingScriptTemplate.Instance.ExtractScriptPubKeyParameters(scriptPubKey, out KeyId hotPubKeyHash, out KeyId coldPubKeyHash); address = coldPubKeyHash.GetAddress(this.network).ToString(); coldStakingWithdrawal = true; } else { // We assume that if it wasn't a cold staking scriptPubKey then it must have been P2PKH. address = scriptPubKey.GetDestinationAddress(this.network)?.ToString(); if (address == null) { throw new FeatureException(HttpStatusCode.BadRequest, "Could not resolve address.", $"Could not resolve address from UTXO's scriptPubKey '{scriptPubKey.ToHex()}'."); } } IEnumerable <HdAccount> accounts = this.walletManager.GetAccounts(request.WalletName); IEnumerable <HdAddress> addresses = accounts.SelectMany(hdAccount => hdAccount.GetCombinedAddresses()); HdAddress hdAddress = addresses.FirstOrDefault(a => a.Address == address || a.Bech32Address == address); if (coldStakingWithdrawal && hdAddress == null) { var coldStakingManager = this.walletManager as ColdStakingManager; Wallet.Wallet wallet = coldStakingManager.GetWallet(request.WalletName); HdAccount coldAccount = coldStakingManager.GetColdStakingAccount(wallet, true); IEnumerable <HdAddress> coldAccountAddresses = coldAccount.GetCombinedAddresses(); hdAddress = coldAccountAddresses.FirstOrDefault(a => a.Address == address || a.Bech32Address == address); } // It is possible that the address is outside the gap limit. So if it is not found we optimistically presume the address descriptors will fill in the missing information later. if (hdAddress != null) { ExtKey addressExtKey = seedExtKey.Derive(new KeyPath(hdAddress.HdPath)); BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(this.network); signingKeys.Add(addressPrivateKey); } } // Address descriptors are 'easier' to look the private key up against if provided, but may not always be available. foreach (AddressDescriptor address in request.Addresses) { ExtKey addressExtKey = seedExtKey.Derive(new KeyPath(address.KeyPath)); BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(this.network); signingKeys.Add(addressPrivateKey); } // Offline cold staking transaction handling. We check both the offline setup and the offline withdrawal cases here. if (unsignedTransaction.Outputs.Any(o => o.ScriptPubKey.IsScriptType(ScriptType.ColdStaking)) || coldStakingWithdrawal) { // This will always be added in 'cold' mode if we are processing an offline signing request. builder.Extensions.Add(new ColdStakingBuilderExtension(false)); } builder.AddCoins(coins); builder.AddKeys(signingKeys.ToArray()); builder.SignTransactionInPlace(unsignedTransaction); if (!builder.Verify(unsignedTransaction, out TransactionPolicyError[] errors)) { throw new FeatureException(HttpStatusCode.BadRequest, "Failed to validate signed transaction.", $"Failed to validate signed transaction '{unsignedTransaction.GetHash()}' from offline request '{originalTxId}'."); } var builtTransactionModel = new WalletBuildTransactionModel() { TransactionId = unsignedTransaction.GetHash(), Hex = unsignedTransaction.ToHex(), Fee = request.Fee }; return builtTransactionModel; }, cancellationToken));