コード例 #1
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Lists all unspent transactions from all accounts in the wallet.
        /// This is distinct from the list of spendable transactions. A transaction can be unspent but not yet spendable due to coinbase/stake maturity, for example.
        /// </summary>
        /// <param name="currentChainHeight">Height of the current chain, used in calculating the number of confirmations.</param>
        /// <param name="confirmations">The number of confirmations required to consider a transaction spendable.</param>
        /// <param name="accountFilter">An optional filter for filtering the accounts being returned.</param>
        /// <returns>A collection of spendable outputs.</returns>
        public IEnumerable <UnspentOutputReference> GetAllUnspentTransactions(IWalletStore walletStore, int currentChainHeight, int confirmations = 0, Func <HdAccount, bool> accountFilter = null)
        {
            IEnumerable <HdAccount> accounts = this.GetAccounts(accountFilter);

            // The logic for retrieving unspent transactions is almost identical to determining spendable transactions, we just don't take coinbase/stake maturity into consideration.
            return(accounts.SelectMany(x => x.GetSpendableTransactions(walletStore, currentChainHeight, 0, confirmations)));
        }
コード例 #2
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Lists all spendable transactions in the current account.
        /// </summary>
        /// <param name="currentChainHeight">The current height of the chain. Used for calculating the number of confirmations a transaction has.</param>
        /// <param name="coinbaseMaturity">The coinbase maturity after which a coinstake transaction is spendable.</param>
        /// <param name="confirmations">The minimum number of confirmations required for transactions to be considered.</param>
        /// <returns>A collection of spendable outputs that belong to the given account.</returns>
        /// <remarks>Note that coinbase and coinstake transaction outputs also have to mature with a sufficient number of confirmations before
        /// they are considered spendable. This is independent of the confirmations parameter.</remarks>
        public IEnumerable <UnspentOutputReference> GetSpendableTransactions(IWalletStore walletStore, int currentChainHeight, long coinbaseMaturity, int confirmations = 0)
        {
            // This will take all the spendable coins that belong to the account and keep the reference to the HdAddress and HdAccount.
            // This is useful so later the private key can be calculated just from a given UTXO.
            foreach (HdAddress address in this.GetCombinedAddresses())
            {
                // A block that is at the tip has 1 confirmation.
                // When calculating the confirmations the tip must be advanced by one.

                int countFrom = currentChainHeight + 1;
                foreach (TransactionOutputData transactionData in address.UnspentTransactions(walletStore))
                {
                    int?confirmationCount = 0;

                    if (transactionData.BlockHeight != null)
                    {
                        confirmationCount = countFrom >= transactionData.BlockHeight ? countFrom - transactionData.BlockHeight : 0;
                    }

                    if (confirmationCount < confirmations)
                    {
                        continue;
                    }

                    bool isCoinBase  = transactionData.IsCoinBase ?? false;
                    bool isCoinStake = transactionData.IsCoinStake ?? false;

                    // Check if this wallet is a normal purpose wallet (not cold staking, etc).
                    if (this.IsNormalAccount())
                    {
                        bool isColdCoinStake = transactionData.IsColdCoinStake ?? false;

                        // Skip listing the UTXO if this is a normal wallet, and the UTXO is marked as an cold coin stake.
                        if (isColdCoinStake)
                        {
                            continue;
                        }
                    }

                    // This output can unconditionally be included in the results.
                    // Or this output is a ColdStake, CoinBase or CoinStake and has reached maturity.
                    if ((!isCoinBase && !isCoinStake) || (confirmationCount > coinbaseMaturity))
                    {
                        yield return(new UnspentOutputReference
                        {
                            Account = this,
                            Address = address,
                            Transaction = transactionData,
                            Confirmations = confirmationCount.Value
                        });
                    }
                }
            }
        }
コード例 #3
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Gets the first account that contains no transaction.
        /// </summary>
        /// <returns>An unused account.</returns>
        public HdAccount GetFirstUnusedAccount(IWalletStore walletStore)
        {
            // Get the accounts root for this type of coin.
            AccountRoot accountsRoot = this.AccountsRoot.Single();

            if (accountsRoot.Accounts.Any())
            {
                // Get an unused account.
                HdAccount firstUnusedAccount = accountsRoot.GetFirstUnusedAccount(walletStore);
                if (firstUnusedAccount != null)
                {
                    return(firstUnusedAccount);
                }
            }

            return(null);
        }
コード例 #4
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Gets the last address that contains transactions.
        /// </summary>
        /// <param name="isChange">Whether the address is a change (internal) address or receiving (external) address.</param>
        /// <returns></returns>
        public HdAddress GetLastUsedAddress(IWalletStore walletStore, bool isChange)
        {
            IEnumerable <HdAddress> addresses = isChange ? this.InternalAddresses : this.ExternalAddresses;

            if (addresses == null)
            {
                return(null);
            }

            List <HdAddress> usedAddresses = addresses.Where(acc => walletStore.CountForAddress(acc.Address) > 0).ToList();

            if (!usedAddresses.Any())
            {
                return(null);
            }

            // gets the used address with the highest index
            int index = usedAddresses.Max(a => a.Index);

            return(usedAddresses.Single(a => a.Index == index));
        }
コード例 #5
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Gets the first receiving address that contains no transaction.
        /// </summary>
        /// <returns>An unused address</returns>
        private HdAddress GetFirstUnusedAddress(IWalletStore walletStore, bool isChange)
        {
            IEnumerable <HdAddress> addresses = isChange ? this.InternalAddresses : this.ExternalAddresses;

            if (addresses == null)
            {
                return(null);
            }

            List <HdAddress> unusedAddresses = addresses.Where(acc => walletStore.CountForAddress(acc.Address) == 0).ToList();

            if (!unusedAddresses.Any())
            {
                return(null);
            }

            // gets the unused address with the lowest index
            int index = unusedAddresses.Min(a => a.Index);

            return(unusedAddresses.Single(a => a.Index == index));
        }
コード例 #6
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Gets the first account that contains no transaction.
        /// </summary>
        /// <returns>An unused account</returns>
        public HdAccount GetFirstUnusedAccount(IWalletStore walletStore)
        {
            if (this.Accounts == null)
            {
                return(null);
            }

            List <HdAccount> unusedAccounts = this.Accounts
                                              .Where(Wallet.NormalAccounts)
                                              .Where(acc =>
                                                     !acc.ExternalAddresses.SelectMany(add => walletStore.GetForAddress(add.Address)).Any()
                                                     &&
                                                     !acc.InternalAddresses.SelectMany(add => walletStore.GetForAddress(add.Address)).Any()).ToList();

            if (!unusedAccounts.Any())
            {
                return(null);
            }

            // gets the unused account with the lowest index
            int index = unusedAccounts.Min(a => a.Index);

            return(unusedAccounts.Single(a => a.Index == index));
        }
コード例 #7
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Get the accounts total spendable value for both confirmed and unconfirmed UTXO.
        /// </summary>
        public (Money ConfirmedAmount, Money UnConfirmedAmount) GetBalances(IWalletStore walletStore, bool excludeColdStakeUtxo)
        {
            var res = walletStore.GetBalanceForAccount(this.Index, excludeColdStakeUtxo);

            return(res.AmountConfirmed, res.AmountUnconfirmed);
        }
コード例 #8
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
 /// <summary>
 /// Gets the first change address that contains no transaction.
 /// </summary>
 /// <returns>An unused address</returns>
 public HdAddress GetFirstUnusedChangeAddress(IWalletStore walletStore)
 {
     return(this.GetFirstUnusedAddress(walletStore, true));
 }
コード例 #9
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
 /// <summary>
 /// Gets the first receiving address that contains no transaction.
 /// </summary>
 /// <returns>An unused address</returns>
 public HdAddress GetFirstUnusedReceivingAddress(IWalletStore walletStore)
 {
     return(this.GetFirstUnusedAddress(walletStore, false));
 }
コード例 #10
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Lists all spendable transactions from all accounts in the wallet.
        /// </summary>
        /// <param name="currentChainHeight">Height of the current chain, used in calculating the number of confirmations.</param>
        /// <param name="confirmations">The number of confirmations required to consider a transaction spendable.</param>
        /// <param name="accountFilter">An optional filter for filtering the accounts being returned.</param>
        /// <returns>A collection of spendable outputs.</returns>
        public IEnumerable <UnspentOutputReference> GetAllSpendableTransactions(IWalletStore walletStore, int currentChainHeight, int confirmations = 0, Func <HdAccount, bool> accountFilter = null)
        {
            IEnumerable <HdAccount> accounts = this.GetAccounts(accountFilter);

            return(accounts.SelectMany(x => x.GetSpendableTransactions(walletStore, currentChainHeight, this.Network.Consensus.CoinbaseMaturity, confirmations)));
        }
コード例 #11
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
        /// <summary>
        /// Get the address total spendable value for both confirmed and unconfirmed UTXO.
        /// </summary>
        public (Money confirmedAmount, Money unConfirmedAmount, bool anyTrx) GetBalances(IWalletStore walletStore, bool excludeColdStakeUtxo)
        {
            var trx = walletStore.GetForAddress(this.Address).ToList();

            List <TransactionOutputData> allTransactions = excludeColdStakeUtxo
                ? trx.Where(t => t.IsColdCoinStake != true).ToList()
                : trx;

            long confirmed = allTransactions.Sum(t => t.GetUnspentAmount(true));
            long total     = allTransactions.Sum(t => t.GetUnspentAmount(false));

            return(confirmed, total - confirmed, trx.Any());
        }
コード例 #12
0
ファイル: Wallet.cs プロジェクト: x42protocol/x42-BlockCore
 /// <summary>
 /// List all spendable transactions in an address.
 /// </summary>
 /// <returns>List of spendable transactions.</returns>
 public IEnumerable <TransactionOutputData> UnspentTransactions(IWalletStore walletStore)
 {
     return(walletStore.GetUnspentForAddress(this.Address));
 }
コード例 #13
0
        public void WalletCanMineWithColdWalletCoins()
        {
            using (NodeBuilder builder = NodeBuilder.Create(this))
            {
                var network = new StratisRegTest();

                CoreNode stratisSender    = CreatePowPosMiningNode(builder, network, TestBase.CreateTestDir(this), coldStakeNode: false);
                CoreNode stratisHotStake  = CreatePowPosMiningNode(builder, network, TestBase.CreateTestDir(this), coldStakeNode: true);
                CoreNode stratisColdStake = CreatePowPosMiningNode(builder, network, TestBase.CreateTestDir(this), coldStakeNode: true);

                stratisSender.WithWallet().Start();
                stratisHotStake.WithWallet().Start();
                stratisColdStake.WithWallet().Start();

                var senderWalletManager = stratisSender.FullNode.WalletManager() as ColdStakingManager;
                var coldWalletManager   = stratisColdStake.FullNode.WalletManager() as ColdStakingManager;
                var hotWalletManager    = stratisHotStake.FullNode.WalletManager() as ColdStakingManager;

                // Set up cold staking account on cold wallet.
                coldWalletManager.GetOrCreateColdStakingAccount(WalletName, true, Password);
                HdAddress    coldWalletAddress = coldWalletManager.GetFirstUnusedColdStakingAddress(WalletName, true);
                IWalletStore walletStore       = coldWalletManager.GetWalletByName(WalletName).walletStore;

                // Set up cold staking account on hot wallet.
                hotWalletManager.GetOrCreateColdStakingAccount(WalletName, false, Password);
                HdAddress hotWalletAddress = hotWalletManager.GetFirstUnusedColdStakingAddress(WalletName, false);

                int maturity = (int)stratisSender.FullNode.Network.Consensus.CoinbaseMaturity;
                TestHelper.MineBlocks(stratisSender, maturity + 16, true);

                // The mining should add coins to the wallet
                long total = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount);
                Assert.Equal(Money.COIN * 98000060, total);

                int confirmations = 10;

                var  walletAccountReference = new WalletAccountReference(WalletName, Account);
                long total2 = stratisSender.FullNode.WalletManager().GetSpendableTransactionsInAccount(walletAccountReference, confirmations).Sum(s => s.Transaction.Amount);

                // Sync all nodes
                TestHelper.ConnectAndSync(stratisHotStake, stratisSender);
                TestHelper.ConnectAndSync(stratisHotStake, stratisColdStake);
                TestHelper.Connect(stratisSender, stratisColdStake);

                // Send coins to hot wallet.
                Money     amountToSend = Money.COIN * 98000059;
                HdAddress sendto       = hotWalletManager.GetUnusedAddress(new WalletAccountReference(WalletName, Account));

                Transaction transaction1 = stratisSender.FullNode.WalletTransactionHandler().BuildTransaction(CreateContext(stratisSender.FullNode.Network, new WalletAccountReference(WalletName, Account), Password, sendto.ScriptPubKey, amountToSend, FeeType.Medium, confirmations));

                // Broadcast to the other node
                stratisSender.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(transaction1.ToHex()));

                // Wait for the transaction to arrive
                TestBase.WaitLoop(() => stratisHotStake.CreateRPCClient().GetRawMempool().Length > 0);
                Assert.NotNull(stratisHotStake.CreateRPCClient().GetRawTransaction(transaction1.GetHash(), null, false));
                TestBase.WaitLoop(() => stratisHotStake.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Any());

                long receivetotal = stratisHotStake.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).Sum(s => s.Transaction.Amount);
                Assert.Equal(amountToSend, (Money)receivetotal);
                Assert.Null(stratisHotStake.FullNode.WalletManager().GetSpendableTransactionsInWallet(WalletName).First().Transaction.BlockHeight);

                // Setup cold staking from the hot wallet.
                Money       amountToSend2 = Money.COIN * 98000058;
                Transaction transaction2  = hotWalletManager.GetColdStakingSetupTransaction(stratisHotStake.FullNode.WalletTransactionHandler(),
                                                                                            coldWalletAddress.Address, hotWalletAddress.Address, WalletName, Account, Password, amountToSend2, new Money(0.02m, MoneyUnit.BTC));

                // Broadcast to the other node
                stratisHotStake.FullNode.NodeController <WalletController>().SendTransaction(new SendTransactionRequest(transaction2.ToHex()));

                // Wait for the transaction to arrive
                TestBase.WaitLoop(() => coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).Any());

                long receivetotal2 = coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).Sum(s => s.Transaction.Amount);
                Assert.Equal(amountToSend2, (Money)receivetotal2);
                Assert.Null(coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).First().Transaction.BlockHeight);

                // Allow coins to reach maturity
                TestHelper.MineBlocks(stratisSender, maturity, true);

                // Start staking.
                var hotMiningFeature = stratisHotStake.FullNode.NodeFeature <MiningFeature>();
                hotMiningFeature.StartStaking(WalletName, Password);

                TestBase.WaitLoop(() =>
                {
                    var stakingInfo = stratisHotStake.FullNode.NodeService <IPosMinting>().GetGetStakingInfoModel();
                    return(stakingInfo.Staking);
                });

                // Wait for new cold wallet transaction.
                var cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(3)).Token;
                TestBase.WaitLoop(() =>
                {
                    // Keep mining to ensure that staking outputs reach maturity.
                    TestHelper.MineBlocks(stratisSender, 1, true);
                    return(walletStore.CountForAddress(coldWalletAddress.Address) > 1);
                }, cancellationToken: cancellationToken);

                // Wait for money from staking.
                cancellationToken = new CancellationTokenSource(TimeSpan.FromMinutes(3)).Token;
                TestBase.WaitLoop(() =>
                {
                    // Keep mining to ensure that staking outputs reach maturity.
                    TestHelper.MineBlocks(stratisSender, 1, true);
                    return(coldWalletManager.GetSpendableTransactionsInColdWallet(WalletName, true).Sum(s => s.Transaction.Amount) > receivetotal2);
                }, cancellationToken: cancellationToken);
            }
        }