From b7785f36fd221e50e89ec751cb2e8f071d1610a0 Mon Sep 17 00:00:00 2001 From: NicolasDorier Date: Thu, 13 Jul 2017 14:47:10 +0900 Subject: [PATCH] Introduce WalletAccountReference type --- .../WalletTests.cs | 20 +++--- .../Wallet/WalletControllerTest.cs | 10 +-- .../RPC/Controllers/WalletRPCController.cs | 37 ++-------- .../Wallet/Controllers/WalletController.cs | 4 +- Stratis.Bitcoin/Wallet/IWalletManager.cs | 10 ++- .../Wallet/WalletAccountReference.cs | 70 +++++++++++++++++++ Stratis.Bitcoin/Wallet/WalletManager.cs | 17 +++-- 7 files changed, 109 insertions(+), 59 deletions(-) create mode 100644 Stratis.Bitcoin/Wallet/WalletAccountReference.cs diff --git a/Stratis.Bitcoin.IntegrationTests/WalletTests.cs b/Stratis.Bitcoin.IntegrationTests/WalletTests.cs index 9c4b3ccbdd8..4221f76453a 100644 --- a/Stratis.Bitcoin.IntegrationTests/WalletTests.cs +++ b/Stratis.Bitcoin.IntegrationTests/WalletTests.cs @@ -31,7 +31,7 @@ public void WalletCanReceiveAndSendCorrectly() var mnemonic2 = stratisReceiver.FullNode.WalletManager.CreateWallet("123456", "mywallet"); Assert.Equal(12, mnemonic1.Words.Length); Assert.Equal(12, mnemonic2.Words.Length); - var addr = stratisSender.FullNode.WalletManager.GetUnusedAddress("mywallet", "account 0"); + var addr = stratisSender.FullNode.WalletManager.GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var key = stratisSender.FullNode.WalletManager.GetKeyForAddress("123456", addr).PrivateKey; stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); @@ -50,8 +50,8 @@ public void WalletCanReceiveAndSendCorrectly() TestHelper.WaitLoop(() => TestHelper.AreNodesSynced(stratisReceiver, stratisSender)); // send coins to the receiver - var sendto = stratisReceiver.FullNode.WalletManager.GetUnusedAddress("mywallet", "account 0"); - var trx = stratisSender.FullNode.WalletManager.BuildTransaction("mywallet", "account 0", "123456", sendto.Address, Money.COIN * 100, FeeType.Medium, 101); + var sendto = stratisReceiver.FullNode.WalletManager.GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); + var trx = stratisSender.FullNode.WalletManager.BuildTransaction(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.Address, Money.COIN * 100, FeeType.Medium, 101); // broadcast to the other node stratisSender.FullNode.WalletManager.SendTransaction(trx.hex); @@ -130,7 +130,7 @@ public void WalletCanReorg() var mnemonic2 = stratisReceiver.FullNode.WalletManager.CreateWallet("123456", "mywallet"); Assert.Equal(12, mnemonic1.Words.Length); Assert.Equal(12, mnemonic2.Words.Length); - var addr = stratisSender.FullNode.WalletManager.GetUnusedAddress("mywallet", "account 0"); + var addr = stratisSender.FullNode.WalletManager.GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var key = stratisSender.FullNode.WalletManager.GetKeyForAddress("123456", addr).PrivateKey; stratisSender.SetDummyMinerSecret(new BitcoinSecret(key, stratisSender.FullNode.Network)); @@ -157,8 +157,8 @@ public void WalletCanReorg() // Build Transaction 1 // ==================== // send coins to the receiver - var sendto = stratisReceiver.FullNode.WalletManager.GetUnusedAddress("mywallet", "account 0"); - var transaction1 = stratisSender.FullNode.WalletManager.BuildTransaction("mywallet", "account 0", "123456", sendto.Address, Money.COIN * 100, FeeType.Medium, 101); + var sendto = stratisReceiver.FullNode.WalletManager.GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); + var transaction1 = stratisSender.FullNode.WalletManager.BuildTransaction(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.Address, Money.COIN * 100, FeeType.Medium, 101); // broadcast to the other node stratisSender.FullNode.WalletManager.SendTransaction(transaction1.hex); @@ -193,8 +193,8 @@ public void WalletCanReorg() var forkblock = stratisReceiver.FullNode.Chain.Tip; // send more coins to the wallet - sendto = stratisReceiver.FullNode.WalletManager.GetUnusedAddress("mywallet", "account 0"); - var transaction2 = stratisSender.FullNode.WalletManager.BuildTransaction("mywallet", "account 0", "123456", sendto.Address, Money.COIN * 10, FeeType.Medium, 101); + sendto = stratisReceiver.FullNode.WalletManager.GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); + var transaction2 = stratisSender.FullNode.WalletManager.BuildTransaction(new WalletAccountReference("mywallet", "account 0"), "123456", sendto.Address, Money.COIN * 10, FeeType.Medium, 101); stratisSender.FullNode.WalletManager.SendTransaction(transaction2.hex); // wait for the trx to arrive TestHelper.WaitLoop(() => stratisReceiver.CreateRPCClient().GetRawMempool().Length > 0); @@ -270,7 +270,7 @@ public void WalletCanCatchupWithBestChain() // get a key from the wallet var mnemonic = stratisminer.FullNode.WalletManager.CreateWallet("123456", "mywallet"); Assert.Equal(12, mnemonic.Words.Length); - var addr = stratisminer.FullNode.WalletManager.GetUnusedAddress("mywallet", "account 0"); + var addr = stratisminer.FullNode.WalletManager.GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var key = stratisminer.FullNode.WalletManager.GetKeyForAddress("123456", addr).PrivateKey; stratisminer.SetDummyMinerSecret(key.GetBitcoinSecret(stratisminer.FullNode.Network)); @@ -299,7 +299,7 @@ public void WalletCanRecoverOnStartup() // get a key from the wallet var mnemonic = stratisNodeSync.FullNode.WalletManager.CreateWallet("123456", "mywallet"); Assert.Equal(12, mnemonic.Words.Length); - var addr = stratisNodeSync.FullNode.WalletManager.GetUnusedAddress("mywallet", "account 0"); + var addr = stratisNodeSync.FullNode.WalletManager.GetUnusedAddress(new WalletAccountReference("mywallet", "account 0")); var key = stratisNodeSync.FullNode.WalletManager.GetKeyForAddress("123456", addr).PrivateKey; stratisNodeSync.SetDummyMinerSecret(key.GetBitcoinSecret(stratisNodeSync.FullNode.Network)); diff --git a/Stratis.Bitcoin.Tests/Wallet/WalletControllerTest.cs b/Stratis.Bitcoin.Tests/Wallet/WalletControllerTest.cs index 03eddda30cc..c6c82e76e86 100644 --- a/Stratis.Bitcoin.Tests/Wallet/WalletControllerTest.cs +++ b/Stratis.Bitcoin.Tests/Wallet/WalletControllerTest.cs @@ -1181,7 +1181,7 @@ public void BuildTransactionWithValidRequestAllowingUnconfirmedReturnsWalletBuil { var mockWalletWrapper = new Mock(); var key = new Key(); - mockWalletWrapper.Setup(m => m.BuildTransaction("myWallet", "Account 1", "test", key.PubKey.GetAddress(Network.Main).ToString(), new Money(150000), FeeType.High, 0)) + mockWalletWrapper.Setup(m => m.BuildTransaction(new WalletAccountReference("myWallet", "Account 1"), "test", key.PubKey.GetAddress(Network.Main).ToString(), new Money(150000), FeeType.High, 0)) .Returns(("hexString", new uint256(15), new Money(150))); @@ -1211,7 +1211,7 @@ public void BuildTransactionWithValidRequestNotAllowingUnconfirmedReturnsWalletB { var mockWalletWrapper = new Mock(); var key = new Key(); - mockWalletWrapper.Setup(m => m.BuildTransaction("myWallet", "Account 1", "test", key.PubKey.GetAddress(Network.Main).ToString(), new Money(150000), FeeType.High, 1)) + mockWalletWrapper.Setup(m => m.BuildTransaction(new WalletAccountReference("myWallet", "Account 1"), "test", key.PubKey.GetAddress(Network.Main).ToString(), new Money(150000), FeeType.High, 1)) .Returns(("hexString", new uint256(15), new Money(150))); var controller = new WalletController(mockWalletWrapper.Object, new Mock().Object, It.IsAny(), Network.Main, new Mock().Object, It.IsAny()); @@ -1262,7 +1262,7 @@ public void BuildTransactionWithExceptionReturnsBadRequest() { var mockWalletWrapper = new Mock(); var key = new Key(); - mockWalletWrapper.Setup(m => m.BuildTransaction("myWallet", "Account 1", "test", key.PubKey.GetAddress(Network.Main).ToString(), new Money(150000), FeeType.High, 1)) + mockWalletWrapper.Setup(m => m.BuildTransaction(new WalletAccountReference("myWallet", "Account 1"), "test", key.PubKey.GetAddress(Network.Main).ToString(), new Money(150000), FeeType.High, 1)) .Throws(new InvalidOperationException("Issue building transaction.")); var controller = new WalletController(mockWalletWrapper.Object, new Mock().Object, It.IsAny(), Network.Main, new Mock().Object, It.IsAny()); @@ -1500,7 +1500,7 @@ public void GetUnusedAddressWithValidModelReturnsUnusedAddress() { HdAddress address = CreateAddress(); var mockWalletWrapper = new Mock(); - mockWalletWrapper.Setup(m => m.GetUnusedAddress("myWallet", "Account 1")) + mockWalletWrapper.Setup(m => m.GetUnusedAddress(new WalletAccountReference("myWallet", "Account 1"))) .Returns(address); var controller = new WalletController(mockWalletWrapper.Object, new Mock().Object, It.IsAny(), Network.Main, new Mock().Object, It.IsAny()); @@ -1542,7 +1542,7 @@ public void GetUnusedAddressWithInvalidValidModelReturnsBadRequest() public void GetUnusedAddressWithExceptionReturnsBadRequest() { var mockWalletWrapper = new Mock(); - mockWalletWrapper.Setup(m => m.GetUnusedAddress("myWallet", "Account 1")) + mockWalletWrapper.Setup(m => m.GetUnusedAddress(new WalletAccountReference("myWallet", "Account 1"))) .Throws(new InvalidOperationException("Wallet not found.")); var controller = new WalletController(mockWalletWrapper.Object, new Mock().Object, It.IsAny(), Network.Main, new Mock().Object, It.IsAny()); diff --git a/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs b/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs index 31eefc2212f..0bafbfb6afb 100644 --- a/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs +++ b/Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs @@ -12,18 +12,6 @@ namespace Stratis.Bitcoin.RPC.Controllers { public class WalletRPCController : BaseRPCController { - public class UsedWallet - { - public string WalletName - { - get; set; - } - public HdAccount Account - { - get; - set; - } - } public WalletRPCController(IServiceProvider serviceProvider, IWalletManager walletManager) { this.WalletManager = walletManager; @@ -41,6 +29,7 @@ public IWalletManager WalletManager [ActionName("sendtoaddress")] public uint256 SendToAddress(BitcoinAddress bitcoinAddress, Money amount) { + var account = GetAccount(); return uint256.Zero; } @@ -48,32 +37,20 @@ public uint256 SendToAddress(BitcoinAddress bitcoinAddress, Money amount) public List Generate(int nBlock) { var mining = this.serviceProvider.GetRequiredService(); - var wallet = GetWallet(); - var address = this.WalletManager.GetUnusedAddress(wallet.WalletName, wallet.Account.Name); + var account = GetAccount(); + var address = this.WalletManager.GetUnusedAddress(account); return mining.GenerateBlocks(new ReserveScript(address.Pubkey), (ulong)nBlock, int.MaxValue); } - private UsedWallet GetWallet() + + private WalletAccountReference GetAccount() { + //TODO: Support multi wallet like core by mapping passed RPC credentials to a wallet/account var w = this.WalletManager.GetWallets().FirstOrDefault(); if(w == null) throw new RPCServerException(NBitcoin.RPC.RPCErrorCode.RPC_INVALID_REQUEST, "No wallet found"); var account = this.WalletManager.GetAccounts(w).FirstOrDefault(); - return new UsedWallet() - { - Account = account, - WalletName = w - }; - } - - private string GetAccountName() - { - return this.WalletManager.GetAccounts(GetWalletName()).FirstOrDefault().Name; - } - - private string GetWalletName() - { - return this.WalletManager.GetWallets().FirstOrDefault(); + return new WalletAccountReference(w, account.Name); } } } diff --git a/Stratis.Bitcoin/Wallet/Controllers/WalletController.cs b/Stratis.Bitcoin/Wallet/Controllers/WalletController.cs index c9001025c1c..d81dd65577b 100644 --- a/Stratis.Bitcoin/Wallet/Controllers/WalletController.cs +++ b/Stratis.Bitcoin/Wallet/Controllers/WalletController.cs @@ -421,7 +421,7 @@ public IActionResult BuildTransaction([FromBody] BuildTransactionRequest request try { - var transactionResult = this.walletManager.BuildTransaction(request.WalletName, request.AccountName, request.Password, request.DestinationAddress, request.Amount, FeeParser.Parse(request.FeeType), request.AllowUnconfirmed ? 0 : 1); + var transactionResult = this.walletManager.BuildTransaction(new WalletAccountReference(request.WalletName, request.AccountName), request.Password, request.DestinationAddress, request.Amount, FeeParser.Parse(request.FeeType), request.AllowUnconfirmed ? 0 : 1); var model = new WalletBuildTransactionModel { Hex = transactionResult.hex, @@ -542,7 +542,7 @@ public IActionResult GetUnusedAddress([FromQuery]GetUnusedAddressModel request) try { - var result = this.walletManager.GetUnusedAddress(request.WalletName, request.AccountName); + var result = this.walletManager.GetUnusedAddress(new WalletAccountReference(request.WalletName, request.AccountName)); return this.Json(result.Address); } catch (Exception e) diff --git a/Stratis.Bitcoin/Wallet/IWalletManager.cs b/Stratis.Bitcoin/Wallet/IWalletManager.cs index 9bd9735bfde..027e3ed929f 100644 --- a/Stratis.Bitcoin/Wallet/IWalletManager.cs +++ b/Stratis.Bitcoin/Wallet/IWalletManager.cs @@ -98,10 +98,9 @@ public interface IWalletManager : IDisposable /// /// Gets an address that contains no transaction. /// - /// The name of the wallet in which this address is contained. - /// The name of the account in which this address is contained. + /// The name of the wallet and account /// An unused address or a newly created address, in Base58 format. - HdAddress GetUnusedAddress(string walletName, string accountName); + HdAddress GetUnusedAddress(WalletAccountReference accountReference); /// /// Gets a collection of addresses containing transactions for this coin. @@ -134,15 +133,14 @@ public interface IWalletManager : IDisposable /// /// Builds a transaction to be sent to the network. /// - /// The name of the wallet in which this address is contained. - /// The name of the account in which this address is contained. + /// The name of the wallet and account /// The password used to decrypt the private key. /// The destination address to send the funds to. /// The amount of funds to be sent. /// The type of fee to be included. /// The minimum number of confirmations we require for unspent outputs to be included. /// - (string hex, uint256 transactionId, Money fee) BuildTransaction(string walletName, string accountName, string password, string destinationAddress, Money amount, FeeType feeType, int minConfirmations); + (string hex, uint256 transactionId, Money fee) BuildTransaction(WalletAccountReference accountReference, string password, string destinationAddress, Money amount, FeeType feeType, int minConfirmations); /// /// Remove all the thransactions in the wallet that are above this block height diff --git a/Stratis.Bitcoin/Wallet/WalletAccountReference.cs b/Stratis.Bitcoin/Wallet/WalletAccountReference.cs new file mode 100644 index 00000000000..016419d4687 --- /dev/null +++ b/Stratis.Bitcoin/Wallet/WalletAccountReference.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Stratis.Bitcoin.Wallet +{ + public class WalletAccountReference + { + public WalletAccountReference() + { + + } + public WalletAccountReference(string walletName, string accountName) + { + if(walletName == null) + throw new ArgumentNullException("walletName"); + if(accountName == null) + throw new ArgumentNullException("accountName"); + this.WalletName = walletName; + this.AccountName = accountName; + } + + public string WalletName + { + get; set; + } + + public string AccountName + { + get; set; + } + + + public override bool Equals(object obj) + { + WalletAccountReference item = obj as WalletAccountReference; + if(item == null) + return false; + return GetId().Equals(item.GetId()); + } + public static bool operator ==(WalletAccountReference a, WalletAccountReference b) + { + if(System.Object.ReferenceEquals(a, b)) + return true; + if(((object)a == null) || ((object)b == null)) + return false; + return a.GetId().Equals(b.GetId()); + } + + public static bool operator !=(WalletAccountReference a, WalletAccountReference b) + { + return !(a == b); + } + + public override int GetHashCode() + { + return GetId().GetHashCode(); + } + + Tuple GetId() + { + return Tuple.Create(this.WalletName, this.AccountName); + } + + public override string ToString() + { + return $"{WalletName}:{AccountName}"; + } + } +} diff --git a/Stratis.Bitcoin/Wallet/WalletManager.cs b/Stratis.Bitcoin/Wallet/WalletManager.cs index c79abe3921d..9ab0482bc2a 100644 --- a/Stratis.Bitcoin/Wallet/WalletManager.cs +++ b/Stratis.Bitcoin/Wallet/WalletManager.cs @@ -256,12 +256,12 @@ public HdAccount CreateNewAccount(Wallet wallet, string password) } /// - public HdAddress GetUnusedAddress(string walletName, string accountName) + public HdAddress GetUnusedAddress(WalletAccountReference accountReference) { - Wallet wallet = this.GetWalletByName(walletName); + Wallet wallet = this.GetWalletByName(accountReference.WalletName); // get the account - HdAccount account = wallet.AccountsRoot.Single(a => a.CoinType == this.coinType).GetAccountByName(accountName); + HdAccount account = GetAccounts(wallet).GetAccountByName(accountReference.AccountName); // validate address creation if (account.ExternalAddresses.Any()) @@ -461,7 +461,7 @@ public ISecret GetKeyForAddress(string password, HdAddress address) } /// - public (string hex, uint256 transactionId, Money fee) BuildTransaction(string walletName, string accountName, string password, string destinationAddress, Money amount, FeeType feeType, int minConfirmations) + public (string hex, uint256 transactionId, Money fee) BuildTransaction(WalletAccountReference accountReference, string password, string destinationAddress, Money amount, FeeType feeType, int minConfirmations) { if (amount == Money.Zero) { @@ -469,8 +469,8 @@ public ISecret GetKeyForAddress(string password, HdAddress address) } // get the wallet and the account - Wallet wallet = this.GetWalletByName(walletName); - HdAccount account = wallet.AccountsRoot.Single(a => a.CoinType == this.coinType).GetAccountByName(accountName); + Wallet wallet = this.GetWalletByName(accountReference.WalletName); + HdAccount account = this.GetAccounts(wallet).GetAccountByName(accountReference.AccountName); // get script destination address Script destinationScript = null; @@ -539,6 +539,11 @@ public ISecret GetKeyForAddress(string password, HdAddress address) return (tx.ToHex(), tx.GetHash(), calculationResult.fee); } + private AccountRoot GetAccounts(Wallet wallet) + { + return wallet.AccountsRoot.Single(a => a.CoinType == this.coinType); + } + /// /// Calculates which outputs are to be used in the transaction, as well as the fees that will be charged. ///