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); }
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); }
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); }
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")); } } } }
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); }
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); }
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); }
internal static string ToHdPath(this SQLiteWalletRepository repo, int accountIndex, int addressType, int addressIndex) { return($"m/44'/{repo.Network.Consensus.CoinType}'/{accountIndex}'/{addressType}/{addressIndex}"); }
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}'"); }
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 }); }
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); } } }