示例#1
0
        public Transaction CreateColdStakingSetupTransaction(Wallet.Types.Wallet wallet, string password, HdAddress spendingAddress, PubKey destinationColdPubKey, PubKey destinationHotPubKey, HdAddress changeAddress, Money amount, Money fee)
        {
            TransactionOutputData spendingTransaction = wallet.walletStore.GetForAddress(spendingAddress.Address).ElementAt(0);
            var coin = new Coin(spendingTransaction.Id, (uint)spendingTransaction.Index, spendingTransaction.Amount, spendingTransaction.ScriptPubKey);

            Key privateKey = Key.Parse(wallet.EncryptedSeed, password, wallet.Network);

            Script script = ColdStakingScriptTemplate.Instance.GenerateScriptPubKey(destinationHotPubKey.Hash, destinationColdPubKey.Hash);

            var builder = new TransactionBuilder(wallet.Network);

            builder.Extensions.Add(new ColdStakingBuilderExtension(false));
            Transaction tx = builder
                             .AddCoins(new List <Coin> {
                coin
            })
                             .AddKeys(new ExtKey(privateKey, wallet.ChainCode).Derive(new KeyPath(spendingAddress.HdPath)).GetWif(wallet.Network))
                             .Send(script, amount)
                             .SetChange(changeAddress.ScriptPubKey)
                             .SendFees(fee)
                             .BuildTransaction(true);

            if (!builder.Verify(tx))
            {
                throw new WalletException("Could not build transaction, please make sure you entered the correct data.");
            }

            return(tx);
        }
示例#2
0
        public void GetAllTransactionsReturnsTransactionsFromWallet()
        {
            var wallet = new Types.Wallet();

            wallet.walletStore = new WalletMemoryStore();
            AccountRoot stratisAccountRoot = CreateAccountRootWithHdAccountHavingAddresses("StratisAccount", KnownCoinTypes.Stratis);

            TransactionOutputData transaction1 = CreateTransaction(new uint256(1), new Money(15000), 1);
            TransactionOutputData transaction2 = CreateTransaction(new uint256(2), new Money(91209), 1);

            transaction1.OutPoint = new OutPoint(new uint256(1), 1);
            transaction1.Address  = stratisAccountRoot.Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Address;
            wallet.walletStore.InsertOrUpdate(transaction1);
            transaction2.OutPoint = new OutPoint(new uint256(2), 1);
            transaction2.Address  = stratisAccountRoot.Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address;
            wallet.walletStore.InsertOrUpdate(transaction2);

            wallet.AccountsRoot.Add(stratisAccountRoot);

            List <TransactionOutputData> result = wallet.GetAllTransactions().ToList();

            Assert.Equal(2, result.Count);
            Assert.Contains(transaction1.OutPoint, result.Select(x => x.OutPoint));
            Assert.Contains(transaction2.OutPoint, result.Select(x => x.OutPoint));
        }
        public static Transaction SetupValidTransaction(Features.Wallet.Types.Wallet wallet, string password, HdAddress spendingAddress, Script destinationScript, HdAddress changeAddress, Money amount, Money fee)
        {
            TransactionOutputData spendingTransaction = wallet.walletStore.GetForAddress(spendingAddress.Address).ElementAt(0);

            spendingTransaction.Address = spendingAddress.Address;

            var coin = new Coin(spendingTransaction.Id, (uint)spendingTransaction.Index, spendingTransaction.Amount, spendingTransaction.ScriptPubKey);

            Key privateKey = Key.Parse(wallet.EncryptedSeed, password, wallet.Network);

            var         builder = new TransactionBuilder(wallet.Network);
            Transaction tx      = builder
                                  .AddCoins(new List <Coin> {
                coin
            })
                                  .AddKeys(new ExtKey(privateKey, wallet.ChainCode).Derive(new KeyPath(spendingAddress.HdPath)).GetWif(wallet.Network))
                                  .Send(destinationScript, amount)
                                  .SetChange(changeAddress.ScriptPubKey)
                                  .SendFees(fee)
                                  .BuildTransaction(true);

            if (!builder.Verify(tx))
            {
                throw new WalletException("Could not build transaction, please make sure you entered the correct data.");
            }

            return(tx);
        }
示例#4
0
        public void IsSpentWithTransactionHavingNoSpendingDetailsReturnsFalse()
        {
            var transaction = new TransactionOutputData
            {
                SpendingDetails = null
            };

            Assert.False(transaction.IsSpent());
        }
示例#5
0
        public void IsSpentWithTransactionHavingSpendingDetailsReturnsTrue()
        {
            var transaction = new TransactionOutputData
            {
                SpendingDetails = new SpendingDetails()
            };

            Assert.True(transaction.IsSpent());
        }
示例#6
0
        public void IsConfirmedWithTransactionHavingNoBlockHeightReturnsFalse()
        {
            var transaction = new TransactionOutputData
            {
                BlockHeight = null
            };

            Assert.False(transaction.IsConfirmed());
        }
示例#7
0
        public void IsConfirmedWithTransactionHavingBlockHeightReturnsTrue()
        {
            var transaction = new TransactionOutputData
            {
                BlockHeight = 15
            };

            Assert.True(transaction.IsConfirmed());
        }
        private int GetConformationCount(TransactionOutputData transaction)
        {
            if (transaction.BlockHeight.HasValue)
            {
                var blockCount = this.ConsensusManager?.Tip.Height ?? -1; // TODO: This is available in FullNodeController, should refactor and reuse the logic.
                return(blockCount - transaction.BlockHeight.Value);
            }

            return(-1);
        }
示例#9
0
        public void UnspentAmountConfirmedOnlyGivenNoSpendingDetailsReturnsZero()
        {
            var transaction = new TransactionOutputData
            {
                SpendingDetails = null
            };

            Money result = transaction.GetUnspentAmount(true);

            Assert.Equal(Money.Zero, result);
        }
        public static SpendingDetails CreateSpendingDetails(TransactionOutputData changeTransaction, PaymentDetails paymentDetails)
        {
            var spendingDetails = new SpendingDetails
            {
                TransactionId = changeTransaction.Id,
                CreationTime  = new DateTimeOffset(new DateTime(2017, 6, 23, 1, 2, 3)),
                BlockHeight   = changeTransaction.BlockHeight
            };

            spendingDetails.Payments.Add(paymentDetails);
            return(spendingDetails);
        }
示例#11
0
        public void UnspentAmountNotConfirmedOnlyGivenNoSpendingDetailsReturnsTransactionAmount()
        {
            var transaction = new TransactionOutputData
            {
                SpendingDetails = null,
                Amount          = new Money(15)
            };

            Money result = transaction.GetUnspentAmount(false);

            Assert.Equal(new Money(15), result);
        }
示例#12
0
        public void UnspentAmountConfirmedOnlyGivenBeingUnConfirmedAndSpentUnconfirmedReturnsZero()
        {
            var transaction = new TransactionOutputData
            {
                SpendingDetails = new SpendingDetails(),
                Amount          = new Money(15),
            };

            Money result = transaction.GetUnspentAmount(true);

            Assert.Equal(Money.Zero, result);
        }
示例#13
0
        public void UnspentAmountConfirmedOnlyGivenSpendableAndConfirmedReturnsAmount()
        {
            var transaction = new TransactionOutputData
            {
                SpendingDetails = null,
                Amount          = new Money(15),
                BlockHeight     = 15
            };

            Money result = transaction.GetUnspentAmount(true);

            Assert.Equal(new Money(15), result);
        }
示例#14
0
        public void UnspentAmountNotConfirmedOnlyGivenBeingConfirmedAndSpentConfirmedReturnsZero()
        {
            var transaction = new TransactionOutputData
            {
                SpendingDetails = new SpendingDetails {
                    BlockHeight = 16
                },
                Amount      = new Money(15),
                BlockHeight = 15
            };

            Money result = transaction.GetUnspentAmount(false);

            Assert.Equal(Money.Zero, result);
        }
示例#15
0
        /// <summary>
        /// Determines whether or not the input's address exists in the wallet's set of addresses.
        /// </summary>
        /// <param name="addresses">The wallet's external and internal addresses.</param>
        /// <param name="txDictionary">The set of transactions to check against.</param>
        /// <param name="txIn">The input to check.</param>
        /// <returns><c>true</c>if the input's address exist in the wallet.</returns>
        private bool IsTxInMine(IEnumerable <HdAddress> addresses, Dictionary <uint256, TransactionOutputData> txDictionary, TxIn txIn)
        {
            TransactionOutputData previousTransaction = null;

            txDictionary.TryGetValue(txIn.PrevOut.Hash, out previousTransaction);

            if (previousTransaction == null)
            {
                return(false);
            }

            var previousTx = this.blockStore.GetTransactionById(previousTransaction.Id);

            if (txIn.PrevOut.N >= previousTx.Outputs.Count)
            {
                return(false);
            }

            // We now need to check if the scriptPubkey is in our wallet.
            // See https://github.com/bitcoin/bitcoin/blob/011c39c2969420d7ca8b40fbf6f3364fe72da2d0/src/script/ismine.cpp
            return(IsAddressMine(addresses, previousTx.Outputs[txIn.PrevOut.N].ScriptPubKey));
        }
示例#16
0
        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 List <ListTransactionsModel> ListTransactions(string account        = "*", int count = 10, int skip = 0,
                                                             bool includeWatchOnly = true)
        {
            var listTransactionsModels = new List <ListTransactionsModel>();

            if (count < 0)
            {
                throw new ArgumentException("Negative count", nameof(count));
            }

            if (skip < 0)
            {
                throw new ArgumentException("Negative skip", nameof(skip));
            }

            // Transaction dictionary of <Transaction, isWatchOnly>
            // isWatchOnly to be used to specify that the transaction is watch only.
            var transactions = new Dictionary <TransactionOutputData, bool>();

            WalletAccountReference accountReference = this.GetWalletAccountReference();

            // Get all watch-only transactions.
            if (includeWatchOnly)
            {
                var selectedWatchOnlyTransactions = this.watchOnlyWalletManager
                                                    .GetWatchedTransactions()
                                                    .Values
                                                    .Skip(skip)
                                                    .Take(count);
                foreach (var watchTransactionData in selectedWatchOnlyTransactions)
                {
                    var watchedTransaction = this.blockStore.GetTransactionById(watchTransactionData.Id);
                    if (watchedTransaction == null)
                    {
                        continue;
                    }

                    var watchedOutput = watchedTransaction.Outputs.Find(
                        txOut => txOut.ScriptPubKey.GetDestinationAddress(Network).ToString() ==
                        watchTransactionData.Address);

                    var transactionData = new TransactionOutputData()
                    {
                        Address     = watchTransactionData.Address,
                        Amount      = watchedOutput != null ? watchedOutput.Value : 0,
                        Id          = watchTransactionData.Id,
                        BlockHash   = watchTransactionData.BlockHash,
                        BlockHeight = GetTransactionBlockHeader(watchTransactionData.Id).Height,
                        MerkleProof = watchTransactionData.MerkleProof
                    };

                    transactions.Add(transactionData, true);
                }
            }

            // Get all wallet transactions for the specified wallet.
            Func <IHdAccount, bool> accountFilter = account == "*" || account == null ?
                                                    Wallet.Types.Wallet.AllAccounts :
                                                    a => a.Name == account;

            Wallet.Types.Wallet wallet = this.walletManager.GetWallet(accountReference.WalletName);

            IEnumerable <TransactionOutputData> selectedTransactions = wallet.GetAllTransactions(accountFilter)
                                                                       .Skip(skip)
                                                                       .Take(count);

            foreach (var transactionData in selectedTransactions)
            {
                transactions.Add(transactionData, false);
            }

            // Collect all related outputs for a given transaction.
            foreach ((TransactionOutputData transactionOutputData, bool isWatchOnly) in transactions)
            {
                listTransactionsModels.AddRange(
                    GetListTransactionModels(transactionOutputData, isWatchOnly));
            }

            // From bitcoin/src/wallet/rpcwallet.cpp: iterate backwards until we have nCount items to return
            listTransactionsModels.Sort((ListTransactionsModel x, ListTransactionsModel y) =>
            {
                if (x.BlockHeight == y.BlockHeight)
                {
                    return(0);
                }
                return(x.BlockHeight > y.BlockHeight ? -1 : 1);
            });

            return(listTransactionsModels);
        }
        private static TransactionItemModel FindSimilarReceivedTransactionOutput(List <TransactionItemModel> items, TransactionOutputData transaction)
        {
            TransactionItemModel existingTransaction = items.FirstOrDefault(i => i.Id == transaction.Id &&
                                                                            i.Type == TransactionItemType.Received &&
                                                                            i.ConfirmedInBlock == transaction.BlockHeight);

            return(existingTransaction);
        }
        public static WalletHistoryModel GetHistory(IWalletManager walletManager, Network network, WalletHistoryRequest request)
        {
            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 = walletManager.GetHistory(request.WalletName, request.AccountName);

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

                IEnumerable <FlatHistory> query = accountHistory.History;

                if (!string.IsNullOrEmpty(request.Address))
                {
                    query = query.Where(x => x.Address.Address == request.Address);
                }

                // Sorting the history items by descending dates. That includes received and sent dates.
                List <FlatHistory> items = query
                                           .OrderBy(o => o.Transaction.IsConfirmed() ? 1 : 0)
                                           .ThenByDescending(o => o.Transaction.SpendingDetails?.CreationTime ?? o.Transaction.CreationTime)
                                           .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.IsSpent())).ToList();

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

                foreach (FlatHistory item in history)
                {
                    // Count only unique transactions and limit it to MaxHistoryItemsPerAccount.
                    int processedTransactions = uniqueProcessedTxIds.Count;
                    if (processedTransactions >= WalletController.MaxHistoryItemsPerAccount)
                    {
                        break;
                    }

                    TransactionOutputData transaction = item.Transaction;
                    HdAddress             address     = item.Address;

                    // First we look for staking transaction as they require special attention.
                    // A staking transaction spends one of our inputs into 2 outputs or more, paid to the same address.
                    if (transaction.SpendingDetails?.IsCoinStake != null && transaction.SpendingDetails.IsCoinStake.Value)
                    {
                        // We look for the output(s) 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,
                                BlockIndex       = transaction.SpendingDetails.BlockIndex
                            };

                            transactionItems.Add(stakingItem);
                            uniqueProcessedTxIds.Add(stakingItem.Id);
                        }

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

                    // 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,
                            BlockIndex       = transaction.SpendingDetails.BlockIndex,
                            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;
                        }

                        transactionItems.Add(sentItem);
                        uniqueProcessedTxIds.Add(sentItem.Id);
                    }

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

                    // Create a record for a 'receive' transaction.
                    if (transaction.IsCoinStake == null && !address.IsChangeAddress())
                    {
                        // First check if we already have a similar transaction output, in which case we just sum up the amounts
                        TransactionItemModel existingReceivedItem = FindSimilarReceivedTransactionOutput(transactionItems, transaction);

                        if (existingReceivedItem == null)
                        {
                            // 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,
                                BlockIndex       = transaction.BlockIndex
                            };

                            transactionItems.Add(receivedItem);
                            uniqueProcessedTxIds.Add(receivedItem.Id);
                        }
                        else
                        {
                            existingReceivedItem.Amount += transaction.Amount;
                        }
                    }
                }

                transactionItems = transactionItems.Distinct(new SentTransactionItemModelComparer()).Select(e => e).ToList();

                // Sort and filter the history items.
                List <TransactionItemModel> itemsToInclude = transactionItems.OrderByDescending(t => t.Timestamp)
                                                             .Where(x => String.IsNullOrEmpty(request.SearchQuery) || (x.Id.ToString() == request.SearchQuery || x.ToAddress == request.SearchQuery || x.Payments.Any(p => p.DestinationAddress == request.SearchQuery)))
                                                             .Skip(request.Skip ?? 0)
                                                             .Take(request.Take ?? transactionItems.Count)
                                                             .ToList();

                model.AccountsHistoryModel.Add(new AccountHistoryModel
                {
                    TransactionsHistory = itemsToInclude,
                    Name     = accountHistory.Account.Name,
                    CoinType = network.Consensus.CoinType,
                    HdPath   = accountHistory.Account.HdPath
                });
            }

            return(model);
        }
示例#20
0
        public void CreateWalletData()
        {
            string walltName = "wallet-with-funds";
            string path      = Path.Combine(WalletOutputDataPath + @"\txdb", walltName + ".db");

            if (File.Exists(path))
            {
                File.Delete(path);
            }

            var network = new BitcoinRegTest();
            var folder  = new DataFolder(WalletOutputDataPath);
            var wallet  = new Features.Wallet.Types.Wallet()
            {
                Name = walltName
            };

            var walletStore = new WalletStore(network, folder, wallet);

            string dataPath = Path.Combine(DataPath, "wallet-data-table.json");

            var dataTable = JsonConvert.DeserializeObject <WalletDataItem[]>(File.ReadAllText(dataPath))[0];

            WalletData walletData = new WalletData
            {
                Key           = dataTable.Id,
                WalletName    = dataTable.WalletName,
                EncryptedSeed = dataTable.EncryptedSeed,
                WalletTip     = new HashHeightPair(uint256.Parse(dataTable.WalletTip.Split("-")[1]),
                                                   int.Parse(dataTable.WalletTip.Split("-")[0])),
                BlockLocator = dataTable.BlockLocator.Select(uint256.Parse).ToList()
            };

            walletStore.SetData(walletData);

            dataPath = Path.Combine(DataPath, "wallet-transactions-table.json");

            var transactionsTable = JsonConvert.DeserializeObject <TransactionDataItem[]>(File.ReadAllText(dataPath));

            foreach (var item in transactionsTable)
            {
                var trx = new TransactionOutputData
                {
                    OutPoint = new OutPoint(uint256.Parse(item.OutPoint.Split("-")[0]),
                                            int.Parse(item.OutPoint.Split("-")[1])),
                    Address     = item.Address,
                    Id          = uint256.Parse(item.Id),
                    Amount      = new Money(item.Amount),
                    Index       = item.Index,
                    BlockHeight = item.BlockHeight,
                    BlockHash   = item.BlockHash != null?uint256.Parse(item.BlockHash) : null,
                                      CreationTime    = DateTimeOffset.FromUnixTimeSeconds(long.Parse(item.CreationTime)),
                                      ScriptPubKey    = new Script(Encoders.Hex.DecodeData(item.ScriptPubKey)),
                                      IsPropagated    = item.IsPropagated,
                                      AccountIndex    = item.AccountIndex,
                                      IsCoinStake     = item.IsCoinStake,
                                      IsCoinBase      = item.IsCoinBase,
                                      IsColdCoinStake = item.IsColdCoinStake,
                };

                if (item.SpendingDetails != null)
                {
                    trx.SpendingDetails = new Features.Wallet.Database.SpendingDetails
                    {
                        BlockHeight   = item.SpendingDetails.BlockHeight,
                        IsCoinStake   = item.SpendingDetails.IsCoinStake,
                        CreationTime  = DateTimeOffset.FromUnixTimeSeconds(long.Parse(item.SpendingDetails.CreationTime)),
                        TransactionId = uint256.Parse(item.SpendingDetails.TransactionId)
                    };

                    if (item.SpendingDetails.Payments != null)
                    {
                        foreach (PaymentDetails spendingDetailsPayment in item.SpendingDetails.Payments)
                        {
                            trx.SpendingDetails.Payments.Add(new Features.Wallet.Database.PaymentDetails
                            {
                                Amount                  = new Money(spendingDetailsPayment.Amount),
                                DestinationAddress      = spendingDetailsPayment.DestinationAddress,
                                DestinationScriptPubKey = new Script(Encoders.Hex.DecodeData(spendingDetailsPayment.DestinationScriptPubKey)),
                                OutputIndex             = spendingDetailsPayment.OutputIndex,
                                PayToSelf               = spendingDetailsPayment.PayToSelf
                            });
                        }
                    }
                }

                walletStore.InsertOrUpdate(trx);
            }
        }
示例#21
0
        public void ProcessTransactionWithValidColdStakingSetupLoadsTransactionsIntoWalletIfMatching()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            Directory.CreateDirectory(dataFolder.WalletPath);

            Wallet.Types.Wallet wallet = this.walletFixture.GenerateBlankWallet("myWallet", "password");
            (ExtKey ExtKey, string ExtPubKey)accountKeys = WalletTestsHelpers.GenerateAccountKeys(wallet, "password", "m/44'/0'/0'");

            (PubKey PubKey, BitcoinPubKeyAddress Address)spendingKeys = WalletTestsHelpers.GenerateAddressKeys(wallet, accountKeys.ExtPubKey, "0/0");
            (PubKey PubKey, BitcoinPubKeyAddress Address)changeKeys   = WalletTestsHelpers.GenerateAddressKeys(wallet, accountKeys.ExtPubKey, "1/0");

            Wallet.Types.Wallet coldWallet = this.walletFixture.GenerateBlankWallet("myColdWallet", "password");
            (ExtKey ExtKey, string ExtPubKey)accountColdKeys = WalletTestsHelpers.GenerateAccountKeys(coldWallet, "password", $"m/44'/0'/{ColdStakingManager.ColdWalletAccountIndex}'");

            (PubKey PubKey, BitcoinPubKeyAddress Address)destinationColdKeys = WalletTestsHelpers.GenerateAddressKeys(coldWallet, accountColdKeys.ExtPubKey, "0/0");

            Wallet.Types.Wallet hotWallet = this.walletFixture.GenerateBlankWallet("myHotWallet", "password");
            (ExtKey ExtKey, string ExtPubKey)accountHotKeys = WalletTestsHelpers.GenerateAccountKeys(hotWallet, "password", $"m/44'/0'/{ColdStakingManager.HotWalletAccountIndex}'");

            (PubKey PubKey, BitcoinPubKeyAddress Address)destinationHotKeys = WalletTestsHelpers.GenerateAddressKeys(hotWallet, accountHotKeys.ExtPubKey, "0/0");

            var spendingAddress = new HdAddress
            {
                Index        = 0,
                HdPath       = $"m/44'/0'/0'/0/0",
                Address      = spendingKeys.Address.ToString(),
                Pubkey       = spendingKeys.PubKey.ScriptPubKey,
                ScriptPubKey = spendingKeys.Address.ScriptPubKey,
            };

            var destinationColdAddress = new HdAddress
            {
                Index        = 0,
                HdPath       = $"m/44'/0'/{ColdStakingManager.ColdWalletAccountIndex}'/0/0",
                Address      = destinationColdKeys.Address.ToString(),
                Pubkey       = destinationColdKeys.PubKey.ScriptPubKey,
                ScriptPubKey = destinationColdKeys.Address.ScriptPubKey,
            };

            var destinationHotAddress = new HdAddress
            {
                Index        = 0,
                HdPath       = $"m/44'/0'/{ColdStakingManager.HotWalletAccountIndex}'/0/0",
                Address      = destinationHotKeys.Address.ToString(),
                Pubkey       = destinationHotKeys.PubKey.ScriptPubKey,
                ScriptPubKey = destinationHotKeys.Address.ScriptPubKey,
            };

            var changeAddress = new HdAddress
            {
                Index        = 0,
                HdPath       = $"m/44'/0'/0'/1/0",
                Address      = changeKeys.Address.ToString(),
                Pubkey       = changeKeys.PubKey.ScriptPubKey,
                ScriptPubKey = changeKeys.Address.ScriptPubKey,
            };

            // Generate a spendable transaction
            (ChainIndexer chain, uint256 blockhash, Block block)chainInfo = WalletTestsHelpers.CreateChainAndCreateFirstBlockWithPaymentToAddress(wallet.Network, spendingAddress);
            TransactionOutputData spendingTransaction = WalletTestsHelpers.CreateTransactionDataFromFirstBlock(chainInfo);

            spendingTransaction.Address = spendingAddress.Address;
            wallet.walletStore.InsertOrUpdate(spendingTransaction);

            wallet.AccountsRoot.ElementAt(0).Accounts.Add(new HdAccount
            {
                Index             = 0,
                Name              = "account 0",
                HdPath            = "m/44'/0'/0'",
                ExtendedPubKey    = accountKeys.ExtPubKey,
                ExternalAddresses = new List <HdAddress> {
                    spendingAddress
                },
                InternalAddresses = new List <HdAddress> {
                    changeAddress
                }
            });

            coldWallet.AccountsRoot.ElementAt(0).Accounts.Add(new HdAccount
            {
                Index             = ColdStakingManager.ColdWalletAccountIndex,
                Name              = ColdStakingManager.ColdWalletAccountName,
                HdPath            = $"m/44'/0'/{ColdStakingManager.ColdWalletAccountIndex}'",
                ExtendedPubKey    = accountColdKeys.ExtPubKey,
                ExternalAddresses = new List <HdAddress> {
                    destinationColdAddress
                },
                InternalAddresses = new List <HdAddress> {
                }
            });

            hotWallet.AccountsRoot.ElementAt(0).Accounts.Add(new HdAccount
            {
                Index             = ColdStakingManager.HotWalletAccountIndex,
                Name              = ColdStakingManager.HotWalletAccountName,
                HdPath            = $"m/44'/0'/{ColdStakingManager.HotWalletAccountIndex}'",
                ExtendedPubKey    = accountHotKeys.ExtPubKey,
                ExternalAddresses = new List <HdAddress> {
                    destinationHotAddress
                },
                InternalAddresses = new List <HdAddress> {
                }
            });

            var walletFeePolicy = new Mock <IWalletFeePolicy>();

            walletFeePolicy.Setup(w => w.GetMinimumFee(258, 50))
            .Returns(new Money(5000));

            var walletSettings = new WalletSettings(new NodeSettings(network: this.Network));

            var coldWalletManager = new ColdStakingManager(this.Network, chainInfo.chain, walletSettings, dataFolder, walletFeePolicy.Object,
                                                           new Mock <IAsyncProvider>().Object, new NodeLifetime(), new ScriptAddressReader(), this.LoggerFactory.Object, DateTimeProvider.Default, new Mock <ISignals>().Object, new Mock <IBroadcasterManager>().Object);

            coldWalletManager.Wallets.Add(wallet);
            coldWalletManager.Wallets.Add(coldWallet);
            coldWalletManager.LoadKeysLookup();

            // Create another instance for the hot wallet as it is not allowed to have both wallets on the same instance.
            var hotWalletManager = new ColdStakingManager(this.Network, chainInfo.chain, walletSettings, dataFolder, walletFeePolicy.Object,
                                                          new Mock <IAsyncProvider>().Object, new NodeLifetime(), new ScriptAddressReader(), this.LoggerFactory.Object, DateTimeProvider.Default, new Mock <ISignals>().Object, new Mock <IBroadcasterManager>().Object);

            hotWalletManager.Wallets.Add(hotWallet);
            hotWalletManager.LoadKeysLookup();

            // Create a cold staking setup transaction.
            Transaction transaction = this.CreateColdStakingSetupTransaction(wallet, "password", spendingAddress, destinationColdKeys.PubKey, destinationHotKeys.PubKey,
                                                                             changeAddress, new Money(7500), new Money(5000));

            coldWalletManager.ProcessTransaction(transaction);
            hotWalletManager.ProcessTransaction(transaction);

            HdAddress spentAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0);

            Assert.Equal(1, wallet.walletStore.GetForAddress(spendingAddress.Address).Count());
            Assert.Equal(transaction.GetHash(), wallet.walletStore.GetForAddress(spentAddressResult.Address).ElementAt(0).SpendingDetails.TransactionId);
            Assert.Equal(transaction.Outputs[1].Value, wallet.walletStore.GetForAddress(spentAddressResult.Address).ElementAt(0).SpendingDetails.Payments.ElementAt(1).Amount);
            Assert.Equal(transaction.Outputs[1].ScriptPubKey, wallet.walletStore.GetForAddress(spentAddressResult.Address).ElementAt(0).SpendingDetails.Payments.ElementAt(1).DestinationScriptPubKey);

            Assert.Equal(1, wallet.walletStore.GetForAddress(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Address).Count());
            TransactionOutputData changeAddressResult = wallet.walletStore.GetForAddress(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Address).ElementAt(0);

            Assert.Equal(transaction.GetHash(), changeAddressResult.Id);
            Assert.Equal(transaction.Outputs[0].Value, changeAddressResult.Amount);
            Assert.Equal(transaction.Outputs[0].ScriptPubKey, changeAddressResult.ScriptPubKey);

            // Verify that the transaction has been recorded in the cold wallet.
            Assert.Equal(1, coldWallet.walletStore.GetForAddress(coldWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).Count());
            TransactionOutputData destinationColdAddressResult = coldWallet.walletStore.GetForAddress(coldWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).ElementAt(0);

            Assert.Equal(transaction.GetHash(), destinationColdAddressResult.Id);
            Assert.Equal(transaction.Outputs[1].Value, destinationColdAddressResult.Amount);
            Assert.Equal(transaction.Outputs[1].ScriptPubKey, destinationColdAddressResult.ScriptPubKey);

            // Verify that the transaction has been recorded in the hot wallet.
            Assert.Equal(1, hotWallet.walletStore.GetForAddress(hotWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).Count());
            TransactionOutputData destinationHotAddressResult = hotWallet.walletStore.GetForAddress(hotWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).ElementAt(0);

            Assert.Equal(transaction.GetHash(), destinationHotAddressResult.Id);
            Assert.Equal(transaction.Outputs[1].Value, destinationHotAddressResult.Amount);
            Assert.Equal(transaction.Outputs[1].ScriptPubKey, destinationHotAddressResult.ScriptPubKey);

            // Try withdrawing from the cold staking setup.
            Wallet.Types.Wallet withdrawalWallet = this.walletFixture.GenerateBlankWallet("myWithDrawalWallet", "password");
            (ExtKey ExtKey, string ExtPubKey)withdrawalAccountKeys = WalletTestsHelpers.GenerateAccountKeys(wallet, "password", "m/44'/0'/0'");

            (PubKey PubKey, BitcoinPubKeyAddress Address)withdrawalKeys = WalletTestsHelpers.GenerateAddressKeys(withdrawalWallet, withdrawalAccountKeys.ExtPubKey, "0/0");

            // Withdrawing to this address.
            var withdrawalAddress = new HdAddress
            {
                Index        = 0,
                HdPath       = $"m/44'/0'/0'/0/0",
                Address      = withdrawalKeys.Address.ToString(),
                Pubkey       = withdrawalKeys.PubKey.ScriptPubKey,
                ScriptPubKey = withdrawalKeys.Address.ScriptPubKey,
            };

            withdrawalWallet.AccountsRoot.ElementAt(0).Accounts.Add(new HdAccount
            {
                Index             = 0,
                Name              = "account 0",
                HdPath            = "m/44'/0'/0'",
                ExtendedPubKey    = accountKeys.ExtPubKey,
                ExternalAddresses = new List <HdAddress> {
                    withdrawalAddress
                },
                InternalAddresses = new List <HdAddress> {
                }
            });

            // Will spend from the cold stake address and send the change back to the same address.
            var         coldStakeAddress      = coldWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0);
            Transaction withdrawalTransaction = this.CreateColdStakingWithdrawalTransaction(coldWallet, "password", coldStakeAddress,
                                                                                            withdrawalKeys.PubKey, ColdStakingScriptTemplate.Instance.GenerateScriptPubKey(destinationColdKeys.PubKey.Hash, destinationHotKeys.PubKey.Hash),
                                                                                            new Money(750), new Money(262));

            // Wallet manager for the wallet receiving the funds.
            var receivingWalletManager = new ColdStakingManager(this.Network, chainInfo.chain, walletSettings, dataFolder, walletFeePolicy.Object,
                                                                new Mock <IAsyncProvider>().Object, new NodeLifetime(), new ScriptAddressReader(), this.LoggerFactory.Object, DateTimeProvider.Default, new Mock <ISignals>().Object, new Mock <IBroadcasterManager>().Object);

            receivingWalletManager.Wallets.Add(withdrawalWallet);
            receivingWalletManager.LoadKeysLookup();

            // Process the transaction in the cold wallet manager.
            coldWalletManager.ProcessTransaction(withdrawalTransaction);

            // Process the transaction in the hot wallet manager.
            hotWalletManager.ProcessTransaction(withdrawalTransaction);

            // Process the transaction in the receiving wallet manager.
            receivingWalletManager.ProcessTransaction(withdrawalTransaction);

            // Verify that the transaction has been recorded in the withdrawal wallet.
            Assert.Equal(1, withdrawalWallet.walletStore.GetForAddress(withdrawalWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).Count());
            TransactionOutputData withdrawalAddressResult = withdrawalWallet.walletStore.GetForAddress(withdrawalWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).First(x => x.Id == withdrawalTransaction.GetHash());

            Assert.Equal(withdrawalTransaction.GetHash(), withdrawalAddressResult.Id);
            Assert.Equal(withdrawalTransaction.Outputs[1].Value, withdrawalAddressResult.Amount);
            Assert.Equal(withdrawalTransaction.Outputs[1].ScriptPubKey, withdrawalAddressResult.ScriptPubKey);

            // Verify that the transaction has been recorded in the cold wallet.
            Assert.Equal(2, coldWallet.walletStore.GetForAddress(coldWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).Count());
            TransactionOutputData coldAddressResult = coldWallet.walletStore.GetForAddress(coldWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).First(x => x.Id == withdrawalTransaction.GetHash());

            Assert.Equal(withdrawalTransaction.GetHash(), coldAddressResult.Id);
            Assert.Equal(withdrawalTransaction.Outputs[0].Value, coldAddressResult.Amount);
            Assert.Equal(withdrawalTransaction.Outputs[0].ScriptPubKey, coldAddressResult.ScriptPubKey);

            // Verify that the transaction has been recorded in the hot wallet.
            Assert.Equal(2, hotWallet.walletStore.GetForAddress(hotWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).Count());
            TransactionOutputData hotAddressResult = hotWallet.walletStore.GetForAddress(hotWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Address).First(x => x.Id == withdrawalTransaction.GetHash());

            Assert.Equal(withdrawalTransaction.GetHash(), hotAddressResult.Id);
            Assert.Equal(withdrawalTransaction.Outputs[0].Value, hotAddressResult.Amount);
            Assert.Equal(withdrawalTransaction.Outputs[0].ScriptPubKey, hotAddressResult.ScriptPubKey);

            // Verify the hot amount returned by GetBalances.
            AccountBalance hotBalance = hotWalletManager.GetBalances("myHotWallet", ColdStakingManager.HotWalletAccountName).FirstOrDefault();

            Assert.Equal(hotBalance.AmountUnconfirmed, hotAddressResult.Amount);

            // Verify the cold amount returned by GetBalances.
            AccountBalance coldBalance = coldWalletManager.GetBalances("myColdWallet", ColdStakingManager.ColdWalletAccountName).FirstOrDefault();

            Assert.Equal(coldBalance.AmountUnconfirmed, coldAddressResult.Amount);
        }
示例#22
0
        public void WalletStore_GetBalanceForAddress_And_GetBalanceForAccount()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            WalletStore store = new WalletStore(this.Network, dataFolder, new Types.Wallet {
                Name = "wallet1", EncryptedSeed = "EncryptedSeed1"
            });

            // Create some temp data
            for (int indexAddress = 0; indexAddress < 3; indexAddress++)
            {
                var scriptInsert = new Key().PubKey.GetAddress(this.Network).ScriptPubKey.ToString();
                for (int indexTrx = 0; indexTrx < 5; indexTrx++)
                {
                    store.InsertOrUpdate(Create(new OutPoint(new uint256((ulong)indexTrx), indexAddress), scriptInsert));
                }
            }

            string script = null;

            for (int accountIndex = 0; accountIndex < 2; accountIndex++)
            {
                script = new Key().PubKey.GetAddress(this.Network).ScriptPubKey.ToString();

                TransactionOutputData trx = null;

                // spent
                trx = Create(new OutPoint(new uint256(21), accountIndex * 10), script, 2); store.InsertOrUpdate(trx);
                trx = Create(new OutPoint(new uint256(22), accountIndex * 10), script, 2); trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
                trx = Create(new OutPoint(new uint256(23), accountIndex * 10), script, 2); trx.IsColdCoinStake = true; trx.BlockHeight = null; store.InsertOrUpdate(trx);

                // cold stake unspent spend
                trx = Create(new OutPoint(new uint256(3), accountIndex * 10), script, 2); trx.IsColdCoinStake = true; trx.SpendingDetails = null; store.InsertOrUpdate(trx);
                trx = Create(new OutPoint(new uint256(4), accountIndex * 10), script, 2); trx.IsColdCoinStake = true; trx.SpendingDetails = null; store.InsertOrUpdate(trx);

                // cold stake unspent spend unconfirmed
                trx = Create(new OutPoint(new uint256(5), accountIndex * 10), script, 2); trx.IsColdCoinStake = true; trx.BlockHeight = null; trx.SpendingDetails = null; store.InsertOrUpdate(trx);
                trx = Create(new OutPoint(new uint256(6), accountIndex * 10), script, 2); trx.IsColdCoinStake = true; trx.BlockHeight = null; trx.SpendingDetails = null; store.InsertOrUpdate(trx);

                // unspent spend
                trx = Create(new OutPoint(new uint256(7), accountIndex * 10), script, 2); trx.IsColdCoinStake = false; trx.SpendingDetails = null; store.InsertOrUpdate(trx);
                trx = Create(new OutPoint(new uint256(8), accountIndex * 10), script, 2); trx.IsColdCoinStake = null; trx.SpendingDetails = null; store.InsertOrUpdate(trx);

                // unspent spend unconfirmed
                trx = Create(new OutPoint(new uint256(9), accountIndex * 10), script, 2); trx.IsColdCoinStake = false; trx.BlockHeight = null; trx.SpendingDetails = null; store.InsertOrUpdate(trx);
                trx = Create(new OutPoint(new uint256(10), accountIndex * 10), script, 2); trx.IsColdCoinStake = null; trx.BlockHeight = null; trx.SpendingDetails = null; store.InsertOrUpdate(trx);
            }

            var findforAddress = script;
            var res            = store.GetBalanceForAddress(findforAddress, false);

            res.AmountConfirmed.Should().Be(20);
            res.AmountUnconfirmed.Should().Be(20);

            res = store.GetBalanceForAddress(findforAddress, true);
            res.AmountConfirmed.Should().Be(10);
            res.AmountUnconfirmed.Should().Be(10);

            res = store.GetBalanceForAccount(2, false);
            res.AmountConfirmed.Should().Be(40);
            res.AmountUnconfirmed.Should().Be(40);

            res = store.GetBalanceForAccount(2, true);
            res.AmountConfirmed.Should().Be(20);
            res.AmountUnconfirmed.Should().Be(20);
        }
示例#23
0
        public void WalletStore_GetAccountHistory()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            WalletStore store = new WalletStore(this.Network, dataFolder, new Types.Wallet {
                Name = "wallet1", EncryptedSeed = "EncryptedSeed1"
            });

            string script = null;

            script = new Key().PubKey.GetAddress(this.Network).ScriptPubKey.ToString();

            TransactionOutputData trx = null;
            ulong index = 20;
            ulong time  = 2000;
            var   dt    = DateTimeOffset.Now;

            // unconfirmed spent
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails.BlockHeight = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails.BlockHeight = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails.BlockHeight = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails.BlockHeight = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            // with 4 outputs
            trx = Create(new OutPoint(new uint256(index++), 00), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails.BlockHeight = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0001), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails.BlockHeight = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0002), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails.BlockHeight = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0003), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails.BlockHeight = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);

            // unconfirmed unspent
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            // with 4 outputs
            trx = Create(new OutPoint(new uint256(index++), 00), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0001), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0002), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0003), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.BlockHeight = null; trx.SpendingDetails = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);

            // confirmed spent
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            // with 4 outputs
            trx = Create(new OutPoint(new uint256(index++), 00), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0001), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0002), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0003), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);

            // confirmed unspent
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.SpendingDetails = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.SpendingDetails = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.SpendingDetails = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index++), 10), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.SpendingDetails = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            // with 4 outputs
            trx = Create(new OutPoint(new uint256(index++), 00), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.SpendingDetails = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0001), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.SpendingDetails = null; trx.IsColdCoinStake = true; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0002), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.SpendingDetails = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);
            trx = Create(new OutPoint(new uint256(index), 0003), script, 2); trx.SpendingDetails.CreationTime = dt.AddMinutes(time--); trx.SpendingDetails = null; trx.IsColdCoinStake = false; store.InsertOrUpdate(trx);

            var res = store.GetAccountHistory(2, false);

            res.Should().HaveCount(22);
            res = store.GetAccountHistory(2, true);
            res.Should().HaveCount(15);
        }
示例#24
0
        private WalletTransactionHandlerTestContext SetupWallet()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            Types.Wallet wallet = WalletTestsHelpers.GenerateBlankWallet("myWallet1", "password");
            (ExtKey ExtKey, string ExtPubKey)accountKeys = WalletTestsHelpers.GenerateAccountKeys(wallet, "password", "m/44'/0'/0'");
            (PubKey PubKey, BitcoinPubKeyAddress Address)spendingKeys    = WalletTestsHelpers.GenerateAddressKeys(wallet, accountKeys.ExtPubKey, "0/0");
            (PubKey PubKey, BitcoinPubKeyAddress Address)destinationKeys = WalletTestsHelpers.GenerateAddressKeys(wallet, accountKeys.ExtPubKey, "0/1");

            var address = new HdAddress
            {
                Index        = 0,
                HdPath       = $"m/44'/0'/0'/0/0",
                Address      = spendingKeys.Address.ToString(),
                Pubkey       = spendingKeys.PubKey.ScriptPubKey,
                ScriptPubKey = spendingKeys.Address.ScriptPubKey,
                // Transactions = new List<TransactionData>()
            };

            var chain = new ChainIndexer(wallet.Network);

            WalletTestsHelpers.AddBlocksWithCoinbaseToChain(wallet.walletStore as WalletMemoryStore, wallet.Network, chain, address);
            TransactionOutputData addressTransaction = wallet.walletStore.GetForAddress(address.Address).First();

            wallet.AccountsRoot.ElementAt(0).Accounts.Add(new HdAccount
            {
                Index             = 0,
                Name              = "account1",
                HdPath            = "m/44'/0'/0'",
                ExtendedPubKey    = accountKeys.ExtPubKey,
                ExternalAddresses = new List <HdAddress> {
                    address
                },
                InternalAddresses = new List <HdAddress>()
            });

            var walletFeePolicy = new Mock <IWalletFeePolicy>();

            walletFeePolicy.Setup(w => w.GetFeeRate(FeeType.Low.ToConfirmations()))
            .Returns(new FeeRate(20000));

            var walletManager = new WalletManager(this.LoggerFactory.Object, this.Network, chain,
                                                  new WalletSettings(NodeSettings.Default(this.Network)), dataFolder,
                                                  walletFeePolicy.Object, new Mock <IAsyncProvider>().Object, new NodeLifetime(), DateTimeProvider.Default, this.scriptAddressReader);
            var walletTransactionHandler =
                new WalletTransactionHandler(this.LoggerFactory.Object, walletManager, walletFeePolicy.Object, this.Network, this.standardTransactionPolicy);

            walletManager.Wallets.Add(wallet);

            var walletReference = new WalletAccountReference
            {
                AccountName = "account1",
                WalletName  = "myWallet1"
            };

            return(new WalletTransactionHandlerTestContext
            {
                Wallet = wallet,
                AccountKeys = accountKeys,
                DestinationKeys = destinationKeys,
                AddressTransaction = addressTransaction,
                WalletTransactionHandler = walletTransactionHandler,
                WalletReference = walletReference
            });
        }
        /**
         * Collects all related outputs for a given transaction.
         * 1. For "send" transactions, find a later transaction that spent an output.
         * 2. For "receive" transactions, just render the output.
         */
        private IEnumerable <ListTransactionsModel> GetListTransactionModels(
            TransactionOutputData transactionOutputData, bool isWatchOnly)
        {
            var transaction   = this.blockStore.GetTransactionById(transactionOutputData.Id);
            var chainedHeader = GetTransactionBlockHeader(transactionOutputData.Id);

            if (transaction == null)
            {
                return(new List <ListTransactionsModel>());
            }

            var  listTransactionModels = new List <ListTransactionsModel>();
            uint blockTime             = Utils.DateTimeToUnixTime(chainedHeader.Header.BlockTime);

            // Add all "receives" to the wallet.
            var listTransactionModel = new ListTransactionsModel()
            {
                InvolvesWatchOnly = isWatchOnly,
                Amount            = transactionOutputData.Amount.ToDecimal(MoneyUnit.BTC),
                Address           = transactionOutputData.Address,
                Category          = ListSinceBlockTransactionCategoryModel.Receive,
                TransactionId     = transaction.GetHash().ToString(),
                BlockHeight       = chainedHeader.Height,
                BlockHash         = chainedHeader.HashBlock.ToString(),
                BlockTime         = blockTime,
                TransactionTime   = blockTime,
                Confirmations     = this.chainIndexer.Tip.Height - chainedHeader.Height + 1
            };

            if (transaction.IsCoinBase)
            {
                // COIN_MATURITY = 100 as part of bitcoin consensus.
                listTransactionModel.Category = listTransactionModel.Confirmations < 100 ?
                                                ListSinceBlockTransactionCategoryModel.Immature :
                                                ListSinceBlockTransactionCategoryModel.Generate;
            }

            listTransactionModels.Add(listTransactionModel);

            var spendingDetails = transactionOutputData.SpendingDetails;

            if (spendingDetails?.BlockHeight == null)
            {
                return(listTransactionModels);
            }
            var spendChainHeader = GetTransactionBlockHeader(spendingDetails.TransactionId);

            var spentTransactionOutput = new ListTransactionsModel()
            {
                InvolvesWatchOnly = isWatchOnly,
                Amount            = -transactionOutputData.Amount.ToDecimal(MoneyUnit.BTC),
                Address           = transactionOutputData.Address,
                Category          = ListSinceBlockTransactionCategoryModel.Send,
                TransactionId     = spendingDetails.TransactionId.ToString(),
                BlockHeight       = spendingDetails.BlockHeight ?? -1,
                BlockHash         = spendingDetails.IsSpentConfirmed() ? spendChainHeader.HashBlock.ToString() : "",
                BlockTime         = spendChainHeader.Header.Time,
                Confirmations     = this.chainIndexer.Tip.Height - spendChainHeader.Height + 1,
                TimeReceived      = Utils.DateTimeToUnixTime(spendingDetails.CreationTime)
            };

            listTransactionModels.Add(spentTransactionOutput);

            return(listTransactionModels);
        }