/// <summary> /// Creates a new wallet. /// </summary> /// <remarks> /// If it's the first time this wallet is created within this class, it is added to a collection for use by other tests. /// If the same parameters have already been used to create a wallet, the wallet will be retrieved from the internal collection and a copy of this wallet will be returned. /// </remarks> /// <param name="name">The name.</param> /// <param name="password">The password.</param> /// <returns></returns> public Stratis.Bitcoin.Features.Wallet.Wallet GenerateBlankWallet(string name, string password) { if (this.walletsGenerated.TryGetValue((name, password), out Stratis.Bitcoin.Features.Wallet.Wallet existingWallet)) { string serializedExistingWallet = JsonConvert.SerializeObject(existingWallet, Formatting.None); return(JsonConvert.DeserializeObject <Stratis.Bitcoin.Features.Wallet.Wallet>(serializedExistingWallet)); } Stratis.Bitcoin.Features.Wallet.Wallet newWallet = WalletTestsHelpers.GenerateBlankWallet(name, password); this.walletsGenerated.Add((name, password), newWallet); string serializedNewWallet = JsonConvert.SerializeObject(newWallet, Formatting.None); return(JsonConvert.DeserializeObject <Stratis.Bitcoin.Features.Wallet.Wallet>(serializedNewWallet)); }
public WalletFixture() { this.walletsGenerated = new Dictionary <(string, string), Stratis.Bitcoin.Features.Wallet.Wallet>(); }
public async Task <object> GetTransaction(string txid, bool include_watchonly = false) { if (!uint256.TryParse(txid, out uint256 trxid)) { throw new ArgumentException(nameof(txid)); } if (include_watchonly) { WalletHistoryModel history = await GetWatchOnlyTransactionAsync(trxid); if ((history?.AccountsHistoryModel?.FirstOrDefault()?.TransactionsHistory?.Count ?? 0) != 0) { return(history); } } // First check the regular wallet accounts. WalletAccountReference accountReference = this.GetWalletAccountReference(); Wallet hdWallet = this.walletManager.WalletRepository.GetWallet(accountReference.WalletName); HdAccount hdAccount = this.walletManager.WalletRepository.GetAccounts(hdWallet, accountReference.AccountName).First(); IWalletAddressReadOnlyLookup addressLookup = this.walletManager.WalletRepository.GetWalletAddressLookup(accountReference.WalletName); bool IsChangeAddress(Script scriptPubKey) { return(addressLookup.Contains(scriptPubKey, out AddressIdentifier addressIdentifier) && addressIdentifier.AddressType == 1); } // Get the transaction from the wallet by looking into received and send transactions. List <TransactionData> receivedTransactions = this.walletManager.WalletRepository.GetTransactionOutputs(hdAccount, null, trxid, true) .Where(td => !IsChangeAddress(td.ScriptPubKey)).ToList(); List <TransactionData> sentTransactions = this.walletManager.WalletRepository.GetTransactionInputs(hdAccount, null, trxid, true).ToList(); TransactionData firstReceivedTransaction = receivedTransactions.FirstOrDefault(); TransactionData firstSendTransaction = sentTransactions.FirstOrDefault(); if (firstReceivedTransaction == null && firstSendTransaction == null) { throw new RPCServerException(RPCErrorCode.RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id."); } uint256 blockHash = null; int? blockHeight, blockIndex; DateTimeOffset transactionTime; SpendingDetails spendingDetails = firstSendTransaction?.SpendingDetails; if (firstReceivedTransaction != null) { blockHeight = firstReceivedTransaction.BlockHeight; blockIndex = firstReceivedTransaction.BlockIndex; blockHash = firstReceivedTransaction.BlockHash; transactionTime = firstReceivedTransaction.CreationTime; } else { blockHeight = spendingDetails.BlockHeight; blockIndex = spendingDetails.BlockIndex; blockHash = spendingDetails.BlockHash; transactionTime = spendingDetails.CreationTime; } // 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; if (block != null) { if (blockIndex == null) { blockIndex = block.Transactions.FindIndex(t => t.GetHash() == trxid); } transactionFromStore = block.Transactions[(int)blockIndex]; } } bool isGenerated; string hex; if (transactionFromStore != null) { transactionTime = Utils.UnixTimeToDateTime(chainedHeaderBlock.ChainedHeader.Header.Time); isGenerated = transactionFromStore.IsCoinBase || transactionFromStore.IsCoinStake; hex = transactionFromStore.ToHex(); } else { 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, BlockTime = block?.Header.BlockTime.ToUnixTimeSeconds(), TransactionId = uint256.Parse(txid), TransactionTime = transactionTime.ToUnixTimeSeconds(), TimeReceived = transactionTime.ToUnixTimeSeconds(), Details = new List <GetTransactionDetailsModel>(), Hex = hex }; // Send transactions details. if (spendingDetails != null) { Money feeSent = Money.Zero; if (firstSendTransaction != null) { // Get the change. long change = spendingDetails.Change.Sum(o => o.Amount); Money inputsAmount = new Money(sentTransactions.Sum(i => i.Amount)); Money outputsAmount = new Money(spendingDetails.Payments.Sum(p => p.Amount) + change); feeSent = inputsAmount - outputsAmount; } var details = spendingDetails.Payments .GroupBy(detail => detail.DestinationAddress) .Select(p => new GetTransactionDetailsModel() { Address = p.Key, Category = GetTransactionDetailsCategoryModel.Send, OutputIndex = p.First().OutputIndex, Amount = 0 - p.Sum(detail => detail.Amount.ToDecimal(MoneyUnit.BTC)), Fee = -feeSent.ToDecimal(MoneyUnit.BTC) }); model.Details.AddRange(details); } // 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. IScriptAddressReader scriptAddressReader = this.FullNode.NodeService <IScriptAddressReader>(); foreach (TransactionData 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 recognize 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; } string address = scriptAddressReader.GetAddressFromScriptPubKey(this.FullNode.Network, trxInWallet.ScriptPubKey); model.Details.Add(new GetTransactionDetailsModel { Address = 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); }