public void WalletMaxSpendableTest2()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            IWalletRepository walletRepository = new SQLiteWalletRepository(this.LoggerFactory.Object, dataFolder, this.Network, DateTimeProvider.Default, new ScriptAddressReader())
            {
                TestMode = true
            };

            var walletManager = new WalletManager(this.LoggerFactory.Object, this.Network, new ChainIndexer(this.Network), new WalletSettings(NodeSettings.Default(this.Network)),
                                                  dataFolder, new Mock <IWalletFeePolicy>().Object, new Mock <IAsyncProvider>().Object, new NodeLifetime(), DateTimeProvider.Default, this.scriptAddressReader, walletRepository);

            walletManager.Start();

            var reserveUtxoService = new ReserveUtxoService(this.loggerFactory, new Mock <ISignals>().Object);

            var walletTransactionHandler = new WalletTransactionHandler(this.LoggerFactory.Object, walletManager, It.IsAny <WalletFeePolicy>(), this.Network, this.standardTransactionPolicy, reserveUtxoService);

            (Wallet wallet, ExtKey extKey) = WalletTestsHelpers.GenerateBlankWalletWithExtKey("wallet1", "password", walletRepository);

            // Passing a null extpubkey into account creation causes problems later, so we need to obtain it first
            int       accountIndex  = 0;
            ExtKey    addressExtKey = extKey.Derive(new KeyPath($"m/44'/{this.Network.Consensus.CoinType}'/{accountIndex}'"));
            ExtPubKey extPubKey     = addressExtKey.Neuter();

            wallet.AddNewAccount(extPubKey, accountName: "account 1");

            (Money max, Money fee)result = walletTransactionHandler.GetMaximumSpendableAmount(new WalletAccountReference("wallet1", "account 1"), FeeType.Low, true);
            Assert.Equal(Money.Zero, result.max);
            Assert.Equal(Money.Zero, result.fee);
        }
        public void Given_AnInvalidAccountIsUsed_When_GetMaximumSpendableAmountIsCalled_Then_AnExceptionIsThrown()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            var chain = new ChainIndexer(this.Network);
            IWalletRepository walletRepository = new SQLiteWalletRepository(this.LoggerFactory.Object, dataFolder, this.Network, DateTimeProvider.Default, new ScriptAddressReader());
            var walletManager = new WalletManager(this.LoggerFactory.Object, this.Network, chain, new WalletSettings(NodeSettings.Default(this.Network)),
                                                  dataFolder, new Mock <IWalletFeePolicy>().Object, new Mock <IAsyncProvider>().Object, new NodeLifetime(), DateTimeProvider.Default, new ScriptAddressReader(), walletRepository);

            walletManager.Start();

            var reserveUtxoService = new ReserveUtxoService(this.loggerFactory, new Mock <ISignals>().Object);

            var walletTransactionHandler = new WalletTransactionHandler(this.LoggerFactory.Object, walletManager, It.IsAny <WalletFeePolicy>(), this.Network, this.standardTransactionPolicy, reserveUtxoService);

            Wallet    wallet  = WalletTestsHelpers.CreateWallet("wallet1", walletRepository);
            HdAccount account = wallet.AddNewAccount((ExtPubKey)null, accountName: "account 1");

            Exception ex = Assert.Throws <WalletException>(() => walletTransactionHandler.GetMaximumSpendableAmount(new WalletAccountReference("wallet1", "noaccount"), FeeType.Low, true));

            Assert.NotNull(ex);
            Assert.NotNull(ex.Message);
            Assert.NotEqual(string.Empty, ex.Message);
            Assert.IsType <WalletException>(ex);
        }
        internal static TransactionData ToTransactionData(this SQLiteWalletRepository repo, HDTransactionData transactionData, IEnumerable <HDPayment> payments)
        {
            TransactionData txData = new TransactionData()
            {
                Amount       = new Money(transactionData.Value, MoneyUnit.BTC),
                BlockHash    = (transactionData.OutputBlockHash == null) ? null : uint256.Parse(transactionData.OutputBlockHash),
                BlockHeight  = transactionData.OutputBlockHeight,
                CreationTime = DateTimeOffset.FromUnixTimeSeconds(transactionData.OutputTxTime),
                Id           = uint256.Parse(transactionData.OutputTxId),
                Index        = transactionData.OutputIndex,
                // These two are always updated and used in tandem so we update them from a single source value.
                IsCoinBase  = transactionData.OutputTxIsCoinBase == 1 && transactionData.OutputIndex == 0,
                IsCoinStake = transactionData.OutputTxIsCoinBase == 1 && transactionData.OutputIndex != 0,
                // IsPropagated  // Not used currently.
                ScriptPubKey    = new Script(Encoders.Hex.DecodeData(transactionData.RedeemScript)),
                SpendingDetails = (transactionData.SpendTxId == null) ? null : new SpendingDetails()
                {
                    BlockHeight = transactionData.SpendBlockHeight,
                    // BlockIndex // Not used currently.
                    CreationTime  = DateTimeOffset.FromUnixTimeSeconds((long)transactionData.SpendTxTime),
                    IsCoinStake   = transactionData.SpendTxIsCoinBase == 1,
                    TransactionId = uint256.Parse(transactionData.SpendTxId),
                    Payments      = payments.Select(p => new PaymentDetails()
                    {
                        Amount = new Money((decimal)p.SpendValue, MoneyUnit.BTC),
                        DestinationScriptPubKey = new Script(Encoders.Hex.DecodeData(p.SpendScriptPubKey)),
                        OutputIndex             = p.SpendIndex
                    }).ToList()
                }
            };

            if (txData.SpendingDetails == null || repo.ScriptAddressReader == null)
            {
                return(txData);
            }

            try
            {
                IEnumerable <PaymentDetails> allDetails = txData.SpendingDetails.Payments;
                var lookup = allDetails.Select(d => d.DestinationScriptPubKey).Distinct().ToDictionary(d => d, d => (string)null);
                foreach (Script script in lookup.Keys.ToList())
                {
                    lookup[script] = repo.ScriptAddressReader.GetAddressFromScriptPubKey(repo.Network, script);
                }

                foreach (PaymentDetails paymentDetails in allDetails)
                {
                    paymentDetails.DestinationAddress = lookup[paymentDetails.DestinationScriptPubKey];
                }
            }
            catch (Exception err)
            {
                throw;
            }

            return(txData);
        }
Beispiel #4
0
        internal static TransactionData ToTransactionData(this SQLiteWalletRepository repo, HDTransactionData transactionData, TransactionCollection transactionCollection)
        {
            // We need to examine the entire scriptPubKey of the transaction output in question in order to determine if it is a coldstaking output.
            var scriptPubKey = new Script(Encoders.Hex.DecodeData(transactionData.RedeemScript));

            // Making the actual cold staking script template available here for checking will be quite messy, so just bring in the relevant check.
            byte[] bytes         = scriptPubKey.ToBytes(true);
            bool   isColdStaking = ((bytes.Length == 51) &&
                                    (bytes[0] == (byte)0x76) && // OP_DUP
                                    (bytes[1] == (byte)0xa9) && // OP_HASH160
                                    (bytes[2] == (byte)0x7b) && // OP_ROT
                                    (bytes[3] == (byte)0x63) && // OP_IF
                                    (bytes[4] == (byte)0xb9) && // OP_CHECKCOLDSTAKEVERIFY
                                    (bytes[5] == 0x14) &&
                                    (bytes[26] == (byte)0x67) && // OP_ELSE
                                    (bytes[27] == 0x14) &&
                                    (bytes[48] == (byte)0x68) && // OP_ENDIF
                                    (bytes[49] == (byte)0x88) && // OP_EQUALVERIFY
                                    (bytes[50] == (byte)0xac));  // OP_CHECKSIG

            // TODO: Need to make a central determination of whether a UTXO is Segwit or not (i.e. it pays to a P2WPKH scriptPubKey)

            var res = new TransactionData()
            {
                Amount       = new Money(transactionData.Value),
                BlockHash    = (transactionData.OutputBlockHash == null) ? null : uint256.Parse(transactionData.OutputBlockHash),
                BlockHeight  = transactionData.OutputBlockHeight,
                CreationTime = DateTimeOffset.FromUnixTimeSeconds(transactionData.OutputTxTime),
                Id           = uint256.Parse(transactionData.OutputTxId),
                Index        = transactionData.OutputIndex,
                // These two are always updated and used in tandem so we update them from a single source value.
                IsCoinBase      = transactionData.OutputTxIsCoinBase == 1 && transactionData.OutputIndex == 0,
                IsCoinStake     = transactionData.OutputTxIsCoinBase == 1 && transactionData.OutputIndex != 0,
                IsColdCoinStake = isColdStaking,
                // IsPropagated  // Not used currently.
                ScriptPubKey        = new Script(Encoders.Hex.DecodeData(transactionData.RedeemScript)),
                AddressScriptPubKey = new Script(Encoders.Hex.DecodeData(transactionData.ScriptPubKey)),
                SpendingDetails     = (transactionData.SpendTxId == null) ? null : new SpendingDetails()
                {
                    BlockHeight = transactionData.SpendBlockHeight,
                    BlockHash   = string.IsNullOrEmpty(transactionData.SpendBlockHash) ? null : uint256.Parse(transactionData.SpendBlockHash),
                    // BlockIndex // Not used currently.
                    CreationTime  = DateTimeOffset.FromUnixTimeSeconds((long)transactionData.SpendTxTime),
                    IsCoinStake   = transactionData.SpendTxIsCoinBase == 1,
                    TransactionId = uint256.Parse(transactionData.SpendTxId)
                },
                TransactionCollection = transactionCollection
            };

            if (res.SpendingDetails != null)
            {
                res.SpendingDetails.TransactionData = res;
            }

            return(res);
        }
 internal static HdAccount ToHdAccount(this SQLiteWalletRepository repo, HDAccount account)
 {
     return(new HdAccount()
     {
         Name = account.AccountName,
         CreationTime = DateTimeOffset.FromUnixTimeSeconds(account.CreationTime),
         ExtendedPubKey = account.ExtPubKey,
         Index = account.AccountIndex,
         HdPath = repo.ToHdPath(account.AccountIndex)
     });
 }
        internal static HdAddress ToHdAddress(this SQLiteWalletRepository repo, HDAddress address)
        {
            var pubKeyScript = (address.PubKey == null) ? null : new Script(Encoders.Hex.DecodeData(address.PubKey)); // P2PK
            var scriptPubKey = new Script(Encoders.Hex.DecodeData(address.ScriptPubKey));

            var res = new HdAddress()
            {
                Address      = repo.ScriptAddressReader.GetAddressFromScriptPubKey(repo.Network, scriptPubKey),
                Index        = address.AddressIndex,
                HdPath       = repo.ToHdPath(address.AccountIndex, address.AddressType, address.AddressIndex),
                ScriptPubKey = new Script(Encoders.Hex.DecodeData(address.ScriptPubKey)),
                Pubkey       = pubKeyScript
            };

            return(res);
        }
Beispiel #7
0
        internal static HdAccount ToHdAccount(this SQLiteWalletRepository repo, HDAccount account)
        {
            var res = new HdAccount
            {
                Name           = account.AccountName,
                CreationTime   = DateTimeOffset.FromUnixTimeSeconds(account.CreationTime),
                ExtendedPubKey = account.ExtPubKey,
                Index          = account.AccountIndex,
                HdPath         = repo.ToHdPath(account.AccountIndex),
            };

            res.ExternalAddresses = new AddressCollection(res, 0);
            res.InternalAddresses = new AddressCollection(res, 1);

            return(res);
        }
Beispiel #8
0
        internal void CleanupUnusedWalletFile()
        {
            if (this.walletContainer.Wallet == null)
            {
                SQLiteWalletRepository repo = this.walletContainer.Conn.Repository;

                if (repo.DatabasePerWallet)
                {
                    string walletName = repo.Wallets.FirstOrDefault(w => ReferenceEquals(w.Value, this.walletContainer)).Key;
                    this.walletContainer.Conn.SQLiteConnection.Dispose();
                    if (walletName != null)
                    {
                        File.Delete(Path.Combine(repo.DBPath, $"{walletName}.db"));
                    }
                }
            }
        }
Beispiel #9
0
        internal static HdAddress ToHdAddress(this SQLiteWalletRepository repo, HDAddress address, Network network)
        {
            Script pubKeyScript = (address.PubKey == null) ? null : new Script(Encoders.Hex.DecodeData(address.PubKey)); // P2PK
            Script scriptPubKey = new Script(Encoders.Hex.DecodeData(address.ScriptPubKey));

            var res = new HdAddress(null)
            {
                Address       = address.Address,
                Index         = address.AddressIndex,
                AddressType   = address.AddressType,
                HdPath        = repo.ToHdPath(address.AccountIndex, address.AddressType, address.AddressIndex),
                ScriptPubKey  = scriptPubKey,
                Pubkey        = pubKeyScript,
                Bech32Address = address.Bech32Address
            };

            return(res);
        }
        public void WalletMaxSpendableTest1()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            var walletFeePolicy = new Mock <IWalletFeePolicy>();

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

            IWalletRepository walletRepository = new SQLiteWalletRepository(this.LoggerFactory.Object, dataFolder, this.Network, DateTimeProvider.Default, new ScriptAddressReader())
            {
                TestMode = true
            };

            var walletManager = new WalletManager(this.LoggerFactory.Object, this.Network, new ChainIndexer(this.Network), new WalletSettings(NodeSettings.Default(this.Network)),
                                                  dataFolder, new Mock <IWalletFeePolicy>().Object, new Mock <IAsyncProvider>().Object, new NodeLifetime(), DateTimeProvider.Default, this.scriptAddressReader, walletRepository);

            walletManager.Start();

            var reserveUtxoService = new ReserveUtxoService(this.loggerFactory, new Mock <ISignals>().Object);

            var walletTransactionHandler = new WalletTransactionHandler(this.LoggerFactory.Object, walletManager, walletFeePolicy.Object, this.Network, this.standardTransactionPolicy, reserveUtxoService);

            (Wallet wallet, ExtKey extKey) = WalletTestsHelpers.GenerateBlankWalletWithExtKey("wallet1", "password", walletRepository);

            // Passing a null extpubkey into account creation causes problems later, so we need to obtain it first
            int       accountIndex  = 0;
            ExtKey    addressExtKey = extKey.Derive(new KeyPath($"m/44'/{this.Network.Consensus.CoinType}'/{accountIndex}'"));
            ExtPubKey extPubKey     = addressExtKey.Neuter();

            HdAccount account = wallet.AddNewAccount(extPubKey, accountName: "account 1");

            HdAddress accountAddress1 = account.ExternalAddresses.First();

            accountAddress1.Transactions.Add(WalletTestsHelpers.CreateTransaction(new uint256(1), new Money(15000), null, null, null, accountAddress1.ScriptPubKey));
            accountAddress1.Transactions.Add(WalletTestsHelpers.CreateTransaction(new uint256(2), new Money(10000), null, null, null, accountAddress1.ScriptPubKey));

            HdAddress accountAddress2 = account.InternalAddresses.Skip(1).First();

            accountAddress2.Transactions.Add(WalletTestsHelpers.CreateTransaction(new uint256(3), new Money(20000), null, null, null, accountAddress2.ScriptPubKey));
            accountAddress2.Transactions.Add(WalletTestsHelpers.CreateTransaction(new uint256(4), new Money(120000), null, null, null, accountAddress2.ScriptPubKey));

            (Money max, Money fee)result = walletTransactionHandler.GetMaximumSpendableAmount(new WalletAccountReference("wallet1", "account 1"), FeeType.Low, true);
            Assert.Equal(new Money(165000), result.max + result.fee);
        }
Beispiel #11
0
        internal static TransactionData ToTransactionData(this SQLiteWalletRepository repo, HDTransactionData transactionData, TransactionCollection transactionCollection)
        {
            // We need to examine the entire scriptPubKey of the transaction output in question in order to determine if it is a coldstaking output.
            var scriptPubKey = new Script(Encoders.Hex.DecodeData(transactionData.RedeemScript));

            bool isColdStaking = scriptPubKey.IsScriptType(ScriptType.ColdStaking);

            // TODO: Need to make a central determination of whether a UTXO is Segwit or not (i.e. it pays to a P2WPKH scriptPubKey)

            var res = new TransactionData()
            {
                Amount       = new Money(transactionData.Value),
                BlockHash    = (transactionData.OutputBlockHash == null) ? null : uint256.Parse(transactionData.OutputBlockHash),
                BlockHeight  = transactionData.OutputBlockHeight,
                CreationTime = DateTimeOffset.FromUnixTimeSeconds(transactionData.OutputTxTime),
                Id           = uint256.Parse(transactionData.OutputTxId),
                Index        = transactionData.OutputIndex,
                // These two are always updated and used in tandem so we update them from a single source value.
                IsCoinBase      = transactionData.OutputTxIsCoinBase == 1 && transactionData.OutputIndex == 0,
                IsCoinStake     = transactionData.OutputTxIsCoinBase == 1 && transactionData.OutputIndex != 0,
                IsColdCoinStake = isColdStaking,
                // IsPropagated  // Not used currently.
                ScriptPubKey        = new Script(Encoders.Hex.DecodeData(transactionData.RedeemScript)),
                AddressScriptPubKey = new Script(Encoders.Hex.DecodeData(transactionData.ScriptPubKey)),
                SpendingDetails     = (transactionData.SpendTxId == null) ? null : new SpendingDetails()
                {
                    BlockHeight = transactionData.SpendBlockHeight,
                    BlockHash   = string.IsNullOrEmpty(transactionData.SpendBlockHash) ? null : uint256.Parse(transactionData.SpendBlockHash),
                    // BlockIndex // Not used currently.
                    CreationTime  = DateTimeOffset.FromUnixTimeSeconds((long)transactionData.SpendTxTime),
                    IsCoinStake   = transactionData.SpendTxIsCoinBase == 1,
                    TransactionId = uint256.Parse(transactionData.SpendTxId)
                },
                TransactionCollection = transactionCollection
            };

            if (res.SpendingDetails != null)
            {
                res.SpendingDetails.TransactionData = res;
            }

            return(res);
        }
Beispiel #12
0
        public void ProcessTransactionWithValidColdStakingSetupLoadsTransactionsIntoWalletIfMatching()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            Directory.CreateDirectory(dataFolder.WalletPath);

            var chain = new ChainIndexer(this.Network);

            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));
            IScriptAddressReader scriptAddressReader = new ColdStakingDestinationReader(new ScriptAddressReader());
            var walletRepository = new SQLiteWalletRepository(this.LoggerFactory.Object, dataFolder, this.Network, DateTimeProvider.Default, scriptAddressReader);

            walletRepository.TestMode = true;
            var walletManager = new ColdStakingManager(this.Network, chain, walletSettings, dataFolder, walletFeePolicy.Object,
                                                       new Mock <IAsyncProvider>().Object, new NodeLifetime(), new ScriptAddressReader(), this.LoggerFactory.Object, DateTimeProvider.Default,
                                                       walletRepository, new Mock <IBroadcasterManager>().Object);

            walletManager.Start();

            (Wallet.Wallet wallet, ExtKey walletExtKey)                     = this.walletFixture.GenerateBlankWallet("myWallet", "password", walletRepository);
            (Wallet.Wallet coldWallet, ExtKey coldWalletExtKey)             = this.walletFixture.GenerateBlankWallet("myColdWallet", "password", walletRepository);
            (Wallet.Wallet hotWallet, ExtKey hotWalletExtKey)               = this.walletFixture.GenerateBlankWallet("myHotWallet", "password", walletRepository);
            (Wallet.Wallet withdrawalWallet, ExtKey withdrawalWalletExtKey) = this.walletFixture.GenerateBlankWallet("myWithDrawalWallet", "password", walletRepository);

            HdAccount withdrawalAccount = withdrawalWallet.AddNewAccount("password");
            HdAddress withdrawalAddress = withdrawalAccount.ExternalAddresses.ElementAt(0);
            PubKey    withdrawalPubKey  = withdrawalAddress.Pubkey.GetDestinationPublicKeys(this.Network)[0];

            HdAccount account         = wallet.AddNewAccount("password");
            HdAddress changeAddress   = account.InternalAddresses.ElementAt(0);
            HdAddress spendingAddress = account.ExternalAddresses.ElementAt(0);

            HdAccount coldWalletAccount      = coldWallet.AddNewAccount("password", ColdStakingManager.ColdWalletAccountIndex, ColdStakingManager.ColdWalletAccountName);
            HdAddress destinationColdAddress = coldWalletAccount.ExternalAddresses.ElementAt(0);
            PubKey    destinationColdPubKey  = destinationColdAddress.Pubkey.GetDestinationPublicKeys(this.Network)[0];

            HdAccount hotWalletAccount      = hotWallet.AddNewAccount("password", ColdStakingManager.HotWalletAccountIndex, ColdStakingManager.HotWalletAccountName);
            HdAddress destinationHotAddress = hotWalletAccount.ExternalAddresses.ElementAt(0);
            PubKey    destinationHotPubKey  = destinationHotAddress.Pubkey.GetDestinationPublicKeys(this.Network)[0];

            // Generate a spendable transaction
            (uint256 blockhash, Block block)chainInfo = WalletTestsHelpers.CreateFirstBlockWithPaymentToAddress(chain, wallet.Network, spendingAddress);

            walletManager.ProcessBlock(chainInfo.block);

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

            walletManager.ProcessBlock(WalletTestsHelpers.AppendTransactionInNewBlockToChain(chain, transaction));

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

            Assert.Single(spendingAddress.Transactions);
            Assert.Equal(transaction.GetHash(), spentAddressResult.Transactions.ElementAt(0).SpendingDetails.TransactionId);
            Assert.Equal(2, transaction.Outputs.Count);
            Assert.True(spentAddressResult.Transactions.ElementAt(0).SpendingDetails.Payments.Any(p => p.DestinationScriptPubKey == transaction.Outputs[1].ScriptPubKey && p.Amount == transaction.Outputs[1].Value));

            Assert.Single(wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Transactions);
            TransactionData changeAddressResult = wallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).InternalAddresses.ElementAt(0).Transactions.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.
            var coldAccounts = coldWallet.GetAccounts(Wallet.Wallet.AllAccounts);

            Assert.Single(coldAccounts.ElementAt(0).ExternalAddresses.ElementAt(0).Transactions);
            TransactionData destinationColdAddressResult = coldAccounts.ElementAt(0).ExternalAddresses.ElementAt(0).Transactions.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.
            var hotAccounts = hotWallet.GetAccounts(Wallet.Wallet.AllAccounts);

            Assert.Single(hotAccounts.ElementAt(0).ExternalAddresses.ElementAt(0).Transactions);
            TransactionData destinationHotAddressResult = hotAccounts.ElementAt(0).ExternalAddresses.ElementAt(0).Transactions.ElementAt(0);

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

            // Will spend from the cold stake address and send the change back to the same address.
            Money       balance               = walletManager.GetSpendableTransactionsInAccount(new WalletAccountReference(coldWallet.Name, coldWalletAccount.Name), 0).Sum(x => x.Transaction.Amount);
            var         coldStakeAddress      = coldWallet.GetAccounts(Wallet.Wallet.AllAccounts).ElementAt(0).ExternalAddresses.ElementAt(0);
            Transaction withdrawalTransaction = this.CreateColdStakingWithdrawalTransaction(coldWallet, "password", coldStakeAddress,
                                                                                            withdrawalPubKey, ColdStakingScriptTemplate.Instance.GenerateScriptPubKey(destinationHotPubKey.Hash, destinationColdPubKey.Hash),
                                                                                            new Money(750), new Money(263));

            // Process the transaction.
            walletManager.ProcessBlock(WalletTestsHelpers.AppendTransactionInNewBlockToChain(chain, withdrawalTransaction));

            // Verify that the transaction has been recorded in the withdrawal wallet.
            Assert.Single(withdrawalWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Transactions);
            TransactionData withdrawalAddressResult = withdrawalWallet.AccountsRoot.ElementAt(0).Accounts.ElementAt(0).ExternalAddresses.ElementAt(0).Transactions
                                                      .Where(t => t.Id == withdrawalTransaction.GetHash()).First();

            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.GetAccounts(Wallet.Wallet.AllAccounts).ElementAt(0).ExternalAddresses.ElementAt(0).Transactions.Count);
            TransactionData coldAddressResult = coldWallet.GetAccounts(Wallet.Wallet.AllAccounts).ElementAt(0).ExternalAddresses.ElementAt(0).Transactions
                                                .Where(t => t.Id == withdrawalTransaction.GetHash()).First();

            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.GetAccounts(Wallet.Wallet.AllAccounts).ElementAt(0).ExternalAddresses.ElementAt(0).Transactions.Count);
            TransactionData hotAddressResult = hotWallet.GetAccounts(Wallet.Wallet.AllAccounts).ElementAt(0).ExternalAddresses.ElementAt(0).Transactions
                                               .Where(t => t.Id == withdrawalTransaction.GetHash()).First();

            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 = walletManager.GetBalances("myHotWallet", ColdStakingManager.HotWalletAccountName).FirstOrDefault();

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

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

            Assert.Equal(coldBalance.AmountConfirmed, coldAddressResult.Amount);
        }
Beispiel #13
0
 internal static string ToHdPath(this SQLiteWalletRepository repo, int accountIndex, int addressType, int addressIndex)
 {
     return($"m/44'/{repo.Network.Consensus.CoinType}'/{accountIndex}'/{addressType}/{addressIndex}");
 }
Beispiel #14
0
 internal static string ToHdPath(this SQLiteWalletRepository repo, int accountIndex)
 {
     // TODO: Support wallets for other derivation standards e.g. BIP84 for P2WPKH addresses
     return($"m/44'/{repo.Network.Consensus.CoinType}'/{accountIndex}'");
 }
Beispiel #15
0
        public void When_BuildTransactionIsCalledWithoutTransactionFee_Then_MultipleSubtractFeeRecipients_ThrowsException()
        {
            DataFolder dataFolder = CreateDataFolder(this);

            IWalletRepository walletRepository = new SQLiteWalletRepository(this.LoggerFactory.Object, dataFolder, this.Network, DateTimeProvider.Default, new ScriptAddressReader())
            {
                TestMode = true
            };

            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, new ChainIndexer(this.Network), new WalletSettings(NodeSettings.Default(this.Network)),
                                                  dataFolder, walletFeePolicy.Object, new Mock <IAsyncProvider>().Object, new NodeLifetime(), DateTimeProvider.Default, this.scriptAddressReader, walletRepository);

            walletManager.Start();

            var reserveUtxoService = new ReserveUtxoService(this.loggerFactory, new Mock <ISignals>().Object);

            var walletTransactionHandler = new WalletTransactionHandler(this.LoggerFactory.Object, walletManager, walletFeePolicy.Object, this.Network, this.standardTransactionPolicy, reserveUtxoService);

            (Wallet wallet, ExtKey extKey) = WalletTestsHelpers.GenerateBlankWalletWithExtKey("myWallet1", "password", walletRepository);

            walletManager.Wallets.Add(wallet);

            int       accountIndex  = 0;
            ExtKey    addressExtKey = extKey.Derive(new KeyPath($"m/44'/{this.Network.Consensus.CoinType}'/{accountIndex}'"));
            ExtPubKey extPubKey     = addressExtKey.Neuter();

            HdAccount account = wallet.AddNewAccount(extPubKey, accountName: "account1");

            var address      = account.ExternalAddresses.First();
            var destination  = account.InternalAddresses.First();
            var destination2 = account.InternalAddresses.Skip(1).First();
            var destination3 = account.InternalAddresses.Skip(2).First();

            // Wallet with 4 coinbase outputs of 50 = 200.
            var chain = new ChainIndexer(wallet.Network);

            WalletTestsHelpers.AddBlocksWithCoinbaseToChain(wallet.Network, chain, address, 4);

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

            // Create a transaction with 3 outputs 50 + 50 + 50 = 150 but with fees charged to recipients.
            var context = new TransactionBuildContext(this.Network)
            {
                AccountReference = walletReference,
                MinConfirmations = 0,
                FeeType          = FeeType.Low,
                WalletPassword   = "******",
                Recipients       = new[]
                {
                    new Recipient {
                        Amount = new Money(50, MoneyUnit.BTC), ScriptPubKey = destination.ScriptPubKey, SubtractFeeFromAmount = true
                    },
                    new Recipient {
                        Amount = new Money(50, MoneyUnit.BTC), ScriptPubKey = destination2.ScriptPubKey, SubtractFeeFromAmount = true
                    },
                    new Recipient {
                        Amount = new Money(50, MoneyUnit.BTC), ScriptPubKey = destination3.ScriptPubKey, SubtractFeeFromAmount = false
                    }
                }.ToList()
            };

            Assert.Throws <WalletException>(() => walletTransactionHandler.BuildTransaction(context));
        }
        private WalletTransactionHandlerTestContext SetupWallet(FeeRate feeRate = null, int coinBaseBlocks = 1)
        {
            DataFolder dataFolder = CreateDataFolder(this);

            IWalletRepository walletRepository = new SQLiteWalletRepository(this.LoggerFactory.Object, dataFolder, this.Network, DateTimeProvider.Default, new ScriptAddressReader())
            {
                TestMode = true
            };

            var walletFeePolicy = new Mock <IWalletFeePolicy>();

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

            var chain = new ChainIndexer(this.Network);

            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, walletRepository);

            var reserveUtxoService = new ReserveUtxoService(this.loggerFactory, new Mock <ISignals>().Object);

            var walletTransactionHandler = new WalletTransactionHandler(this.LoggerFactory.Object, walletManager, walletFeePolicy.Object, this.Network, this.standardTransactionPolicy, reserveUtxoService);

            walletManager.Start();

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

            Wallet wallet = WalletTestsHelpers.GenerateBlankWallet(walletReference.WalletName, "password", walletRepository);

            (ExtKey ExtKey, string ExtPubKey)accountKeys = WalletTestsHelpers.GenerateAccountKeys(wallet, "password", $"m/44'/{this.Network.Consensus.CoinType}'/0'");

            var account = wallet.AddNewAccount(accountKeys.ExtKey.Neuter(), accountName: walletReference.AccountName);

            var destinationAddress = account.ExternalAddresses.Skip(1).First();

            (PubKey PubKey, BitcoinPubKeyAddress Address)destinationKeys = (destinationAddress.Pubkey.GetDestinationPublicKeys(this.Network).First(), new BitcoinPubKeyAddress(destinationAddress.Address, this.Network));

            HdAddress address = account.ExternalAddresses.ElementAt(0);

            TransactionData addressTransaction = null;

            if (coinBaseBlocks != 0)
            {
                WalletTestsHelpers.AddBlocksWithCoinbaseToChain(wallet.Network, chain, address, coinBaseBlocks);
                addressTransaction = address.Transactions.First();
            }

            return(new WalletTransactionHandlerTestContext
            {
                Wallet = wallet,
                AccountKeys = accountKeys,
                DestinationKeys = destinationKeys,
                AddressTransaction = addressTransaction,
                WalletTransactionHandler = walletTransactionHandler,
                WalletReference = walletReference
            });
        }
Beispiel #17
0
        public void LogMetrics(SQLiteWalletRepository repo, DBConnection conn, ChainedHeader header, HDWallet wallet)
        {
            // Write some metrics to file.
            if (repo.WriteMetricsToFile)
            {
                string fixedWidth(object val, int width)
                {
                    return(string.Format($"{{0,{width}}}", val));
                }

                var lines = new List <string>();

                lines.Add($"--- Date/Time: {(DateTime.Now.ToString())}, Block Height: { (header?.Height) } ---");

                var processCnt    = fixedWidth(this.ProcessCount, 5);
                var processTime   = fixedWidth(((double)this.ProcessTime / 10_000_000).ToString("N06"), 8);
                var processAvgSec = fixedWidth(((double)this.ProcessTime / this.ProcessCount / 10_000_000).ToString("N06"), 8);

                var scanCnt    = fixedWidth(this.BlockCount, 5);
                var scanTime   = fixedWidth(((double)this.BlockTime / 10_000_000).ToString("N06"), 8);
                var scanAvgSec = fixedWidth(((double)this.BlockTime / this.BlockCount / 10_000_000).ToString("N06"), 8);

                var readCnt    = fixedWidth(this.ReadCount, 5);
                var readTime   = fixedWidth(((double)this.ReadTime / 10_000_000).ToString("N06"), 8);
                var readAvgSec = fixedWidth(((double)this.ReadTime / this.ReadCount / 10_000_000).ToString("N06"), 8);

                var commitTime   = fixedWidth(((double)this.CommitTime / 10_000_000).ToString("N06"), 8);
                var commitAvgSec = fixedWidth(((double)this.CommitTime / this.ProcessCount / 10_000_000).ToString("N06"), 8);

                lines.Add($"{fixedWidth("Blocks Scanned", -20)  }: Time={scanTime   }, Count={scanCnt   }, AvgSec={scanAvgSec}");
                lines.Add($"{fixedWidth("-Blocks Read", -20)     }: Time={readTime   }, Count={readCnt   }, AvgSec={readAvgSec}");
                lines.Add($"{fixedWidth("Blocks Processed", -20)}: Time={processTime}, Count={processCnt}, AvgSec={processAvgSec}");

                foreach ((string cmdName, DBCommand cmd) in conn.Commands.Select(kv => (kv.Key, kv.Value)))
                {
                    var key    = fixedWidth($"-{cmdName}", -20);
                    var time   = fixedWidth(((double)cmd.ProcessTime / 10_000_000).ToString("N06"), 8);
                    var count  = fixedWidth(cmd.ProcessCount, 5);
                    var avgsec = (cmd.ProcessCount == 0) ? null : fixedWidth(((double)cmd.ProcessTime / cmd.ProcessCount / 10_000_000).ToString("N06"), 8);

                    lines.Add($"{key}: Time={time}, Count={count}, AvgSec={avgsec}");
                }

                lines.Add($"{fixedWidth("-Commit", -20)}: Time={commitTime}, Count={processCnt}, AvgSec={commitAvgSec}");

                lines.Add("");

                this.BlockCount   = 0;
                this.BlockTime    = 0;
                this.ProcessCount = 0;
                this.ProcessTime  = 0;
                this.ReadCount    = 0;
                this.ReadTime     = 0;
                this.CommitTime   = 0;

                foreach (var kv in conn.Commands)
                {
                    kv.Value.ProcessCount = 0;
                    kv.Value.ProcessTime  = 0;
                }

                if (wallet != null)
                {
                    File.AppendAllLines(System.IO.Path.Combine(this.Path, $"Metrics_{ wallet.Name }.txt"), lines);
                }
                else
                {
                    File.AppendAllLines(System.IO.Path.Combine(this.Path, "Metrics.txt"), lines);
                }
            }
        }