Skip to content
This repository has been archived by the owner on Aug 16, 2021. It is now read-only.

Commit

Permalink
Introduce WalletAccountReference type
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasDorier committed Jul 13, 2017
1 parent 845f7b7 commit b7785f3
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 59 deletions.
20 changes: 10 additions & 10 deletions Stratis.Bitcoin.IntegrationTests/WalletTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -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);
Expand Down Expand Up @@ -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));
Expand All @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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));
Expand Down
10 changes: 5 additions & 5 deletions Stratis.Bitcoin.Tests/Wallet/WalletControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1181,7 +1181,7 @@ public void BuildTransactionWithValidRequestAllowingUnconfirmedReturnsWalletBuil
{
var mockWalletWrapper = new Mock<IWalletManager>();
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)));


Expand Down Expand Up @@ -1211,7 +1211,7 @@ public void BuildTransactionWithValidRequestNotAllowingUnconfirmedReturnsWalletB
{
var mockWalletWrapper = new Mock<IWalletManager>();
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<IWalletSyncManager>().Object, It.IsAny<ConnectionManager>(), Network.Main, new Mock<ConcurrentChain>().Object, It.IsAny<DataFolder>());
Expand Down Expand Up @@ -1262,7 +1262,7 @@ public void BuildTransactionWithExceptionReturnsBadRequest()
{
var mockWalletWrapper = new Mock<IWalletManager>();
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<IWalletSyncManager>().Object, It.IsAny<ConnectionManager>(), Network.Main, new Mock<ConcurrentChain>().Object, It.IsAny<DataFolder>());
Expand Down Expand Up @@ -1500,7 +1500,7 @@ public void GetUnusedAddressWithValidModelReturnsUnusedAddress()
{
HdAddress address = CreateAddress();
var mockWalletWrapper = new Mock<IWalletManager>();
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<IWalletSyncManager>().Object, It.IsAny<ConnectionManager>(), Network.Main, new Mock<ConcurrentChain>().Object, It.IsAny<DataFolder>());
Expand Down Expand Up @@ -1542,7 +1542,7 @@ public void GetUnusedAddressWithInvalidValidModelReturnsBadRequest()
public void GetUnusedAddressWithExceptionReturnsBadRequest()
{
var mockWalletWrapper = new Mock<IWalletManager>();
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<IWalletSyncManager>().Object, It.IsAny<ConnectionManager>(), Network.Main, new Mock<ConcurrentChain>().Object, It.IsAny<DataFolder>());
Expand Down
37 changes: 7 additions & 30 deletions Stratis.Bitcoin/RPC/Controllers/WalletRPCController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -41,39 +29,28 @@ public IWalletManager WalletManager
[ActionName("sendtoaddress")]
public uint256 SendToAddress(BitcoinAddress bitcoinAddress, Money amount)
{
var account = GetAccount();
return uint256.Zero;
}

[ActionName("generate")]
public List<uint256> Generate(int nBlock)
{
var mining = this.serviceProvider.GetRequiredService<PowMining>();
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);
}
}
}
4 changes: 2 additions & 2 deletions Stratis.Bitcoin/Wallet/Controllers/WalletController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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)
Expand Down
10 changes: 4 additions & 6 deletions Stratis.Bitcoin/Wallet/IWalletManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,9 @@ public interface IWalletManager : IDisposable
/// <summary>
/// Gets an address that contains no transaction.
/// </summary>
/// <param name="walletName">The name of the wallet in which this address is contained.</param>
/// <param name="accountName">The name of the account in which this address is contained.</param>
/// <param name="accountReference">The name of the wallet and account</param>
/// <returns>An unused address or a newly created address, in Base58 format.</returns>
HdAddress GetUnusedAddress(string walletName, string accountName);
HdAddress GetUnusedAddress(WalletAccountReference accountReference);

/// <summary>
/// Gets a collection of addresses containing transactions for this coin.
Expand Down Expand Up @@ -134,15 +133,14 @@ public interface IWalletManager : IDisposable
/// <summary>
/// Builds a transaction to be sent to the network.
/// </summary>
/// <param name="walletName">The name of the wallet in which this address is contained.</param>
/// <param name="accountName">The name of the account in which this address is contained.</param>
/// <param name="accountReference">The name of the wallet and account</param>
/// <param name="password">The password used to decrypt the private key.</param>
/// <param name="destinationAddress">The destination address to send the funds to.</param>
/// <param name="amount">The amount of funds to be sent.</param>
/// <param name="feeType">The type of fee to be included.</param>
/// <param name="minConfirmations">The minimum number of confirmations we require for unspent outputs to be included.</param>
/// <returns></returns>
(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);

/// <summary>
/// Remove all the thransactions in the wallet that are above this block height
Expand Down
70 changes: 70 additions & 0 deletions Stratis.Bitcoin/Wallet/WalletAccountReference.cs
Original file line number Diff line number Diff line change
@@ -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<string, string> GetId()
{
return Tuple.Create(this.WalletName, this.AccountName);
}

public override string ToString()
{
return $"{WalletName}:{AccountName}";
}
}
}
17 changes: 11 additions & 6 deletions Stratis.Bitcoin/Wallet/WalletManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,12 +256,12 @@ public HdAccount CreateNewAccount(Wallet wallet, string password)
}

/// <inheritdoc />
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())
Expand Down Expand Up @@ -461,16 +461,16 @@ public ISecret GetKeyForAddress(string password, HdAddress address)
}

/// <inheritdoc />
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)
{
throw new WalletException($"Cannot send transaction with 0 {this.coinType}");
}

// 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;
Expand Down Expand Up @@ -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);
}

/// <summary>
/// Calculates which outputs are to be used in the transaction, as well as the fees that will be charged.
/// </summary>
Expand Down

0 comments on commit b7785f3

Please sign in to comment.