Commit 8c4ab891 authored by Jeremy Bokobza's avatar Jeremy Bokobza

Modofied the GetUnusedAddress a bit for Wallet recovery work

parent e125985c
...@@ -173,6 +173,34 @@ namespace Breeze.Wallet ...@@ -173,6 +173,34 @@ namespace Breeze.Wallet
/// </summary> /// </summary>
[JsonProperty(PropertyName = "internalAddresses")] [JsonProperty(PropertyName = "internalAddresses")]
public IEnumerable<HdAddress> InternalAddresses { get; set; } public IEnumerable<HdAddress> InternalAddresses { get; set; }
/// <summary>
/// Gets the type of coin this account is for.
/// </summary>
/// <returns>A <see cref="CoinType"/>.</returns>
public CoinType GetCoinType()
{
string[] pathElements = this.HdPath.Split('/');
int coinType = int.Parse(pathElements[2].Replace("'", string.Empty));
return (CoinType)coinType;
}
/// <summary>
/// Gets the first receiving address that contains no transaction.
/// </summary>
/// <returns>An unused address</returns>
public HdAddress GetFirstUnusedExternalAddress()
{
var unusedAddresses = this.ExternalAddresses.Where(acc => !acc.Transactions.Any()).ToList();
if (!unusedAddresses.Any())
{
return null;
}
// gets the unused address with the lowest index
var index = unusedAddresses.Min(a => a.Index);
return unusedAddresses.Single(a => a.Index == index);
}
} }
/// <summary> /// <summary>
......
...@@ -15,13 +15,13 @@ namespace Breeze.Wallet ...@@ -15,13 +15,13 @@ namespace Breeze.Wallet
/// A manager providing operations on wallets. /// A manager providing operations on wallets.
/// </summary> /// </summary>
public class WalletManager : IWalletManager public class WalletManager : IWalletManager
{ {
public List<Wallet> Wallets { get; } public List<Wallet> Wallets { get; }
public HashSet<Script> PubKeys { get; } public HashSet<Script> PubKeys { get; set; }
public HashSet<TransactionDetails> TrackedTransactions { get; } public HashSet<TransactionDetails> TrackedTransactions { get; }
public WalletManager() public WalletManager()
{ {
this.Wallets = new List<Wallet>(); this.Wallets = new List<Wallet>();
...@@ -54,7 +54,7 @@ namespace Breeze.Wallet ...@@ -54,7 +54,7 @@ namespace Breeze.Wallet
// create a wallet file // create a wallet file
Wallet wallet = this.GenerateWalletFile(password, folderPath, name, WalletHelpers.GetNetwork(network), extendedKey); Wallet wallet = this.GenerateWalletFile(password, folderPath, name, WalletHelpers.GetNetwork(network), extendedKey);
this.Load(wallet); this.Load(wallet);
return mnemonic; return mnemonic;
} }
...@@ -107,7 +107,7 @@ namespace Breeze.Wallet ...@@ -107,7 +107,7 @@ namespace Breeze.Wallet
{ {
// get the accounts root for this type of coin // get the accounts root for this type of coin
var accountsRoot = wallet.AccountsRoot.Single(a => a.CoinType == coinType); var accountsRoot = wallet.AccountsRoot.Single(a => a.CoinType == coinType);
// check if an unused account exists // check if an unused account exists
if (accountsRoot.Accounts.Any()) if (accountsRoot.Accounts.Any())
{ {
...@@ -121,7 +121,7 @@ namespace Breeze.Wallet ...@@ -121,7 +121,7 @@ namespace Breeze.Wallet
// all accounts contain transactions, create a new one // all accounts contain transactions, create a new one
var newAccount = this.CreateNewAccount(wallet, coinType, password); var newAccount = this.CreateNewAccount(wallet, coinType, password);
// save the changes to the file // save the changes to the file
this.SaveToFile(wallet); this.SaveToFile(wallet);
return newAccount; return newAccount;
...@@ -137,12 +137,12 @@ namespace Breeze.Wallet ...@@ -137,12 +137,12 @@ namespace Breeze.Wallet
if (accounts.Any()) if (accounts.Any())
{ {
newAccountIndex = accounts.Max(a => a.Index) + 1; newAccountIndex = accounts.Max(a => a.Index) + 1;
} }
// get the extended pub key used to generate addresses for this account // get the extended pub key used to generate addresses for this account
var privateKey = Key.Parse(wallet.EncryptedSeed, password, wallet.Network); var privateKey = Key.Parse(wallet.EncryptedSeed, password, wallet.Network);
var seedExtKey = new ExtKey(privateKey, wallet.ChainCode); var seedExtKey = new ExtKey(privateKey, wallet.ChainCode);
var accountHdPath = $"m/44'/{(int) coinType}'/{newAccountIndex}'"; var accountHdPath = $"m/44'/{(int)coinType}'/{newAccountIndex}'";
KeyPath keyPath = new KeyPath(accountHdPath); KeyPath keyPath = new KeyPath(accountHdPath);
ExtKey accountExtKey = seedExtKey.Derive(keyPath); ExtKey accountExtKey = seedExtKey.Derive(keyPath);
ExtPubKey accountExtPubKey = accountExtKey.Neuter(); ExtPubKey accountExtPubKey = accountExtKey.Neuter();
...@@ -160,7 +160,7 @@ namespace Breeze.Wallet ...@@ -160,7 +160,7 @@ namespace Breeze.Wallet
accounts.Add(newAccount); accounts.Add(newAccount);
wallet.AccountsRoot.Single(a => a.CoinType == coinType).Accounts = accounts; wallet.AccountsRoot.Single(a => a.CoinType == coinType).Accounts = accounts;
return newAccount; return newAccount;
} }
...@@ -180,8 +180,6 @@ namespace Breeze.Wallet ...@@ -180,8 +180,6 @@ namespace Breeze.Wallet
throw new Exception($"No account with name {accountName} could be found."); throw new Exception($"No account with name {accountName} could be found.");
} }
int newAddressIndex = 0;
// validate address creation // validate address creation
if (account.ExternalAddresses.Any()) if (account.ExternalAddresses.Any())
{ {
...@@ -192,30 +190,63 @@ namespace Breeze.Wallet ...@@ -192,30 +190,63 @@ namespace Breeze.Wallet
{ {
return lastAddress.Address; return lastAddress.Address;
} }
newAddressIndex = lastAddressIndex + 1;
} }
// generate new receiving address // creates an address
BitcoinPubKeyAddress address = this.GenerateAddress(account.ExtendedPubKey, newAddressIndex, false, wallet.Network); this.CreateAddressesInAccount(account, wallet.Network, 1);
// add address details
account.ExternalAddresses = account.ExternalAddresses.Concat(new[] {new HdAddress
{
Index = newAddressIndex,
HdPath = CreateBip44Path(coinType, account.Index, newAddressIndex, false),
ScriptPubKey = address.ScriptPubKey,
Address = address.ToString(),
Transactions = new List<TransactionData>(),
CreationTime = DateTimeOffset.Now
}});
// persists the address to the wallet file // persists the address to the wallet file
this.SaveToFile(wallet); this.SaveToFile(wallet);
// adds the address to the list of tracked addresses // adds the address to the list of tracked addresses
this.PubKeys.Add(address.ScriptPubKey); this.PubKeys = this.LoadKeys(coinType);
return address.ToString(); return account.GetFirstUnusedExternalAddress().Address;
}
/// <summary>
/// Creates a number of addresses in the provided account.
/// </summary>
/// <param name="account">The account.</param>
/// <param name="network">The network.</param>
/// <param name="addressesQuantity">The addresses quantity.</param>
/// <returns>A list of addresses in Base58.</returns>
private List<string> CreateAddressesInAccount(HdAccount account, Network network, int addressesQuantity)
{
List<string> addressesCreated = new List<string>();
// gets the index of the last address with transactions
int indexOfLastUsedAddress = 0;
if (account.ExternalAddresses.Any())
{
indexOfLastUsedAddress = account.ExternalAddresses.Where(a => a.Transactions.Any()).Max(add => add.Index);
}
for (int i = indexOfLastUsedAddress + 1; i <= indexOfLastUsedAddress + addressesQuantity; i++)
{
// skip over addresses that already exist
if (account.ExternalAddresses.ElementAtOrDefault(i) != null)
{
continue;
}
// generate new receiving address
BitcoinPubKeyAddress address = this.GenerateAddress(account.ExtendedPubKey, i, false, network);
// add address details
account.ExternalAddresses = account.ExternalAddresses.Concat(new[] {new HdAddress
{
Index = i,
HdPath = CreateBip44Path(account.GetCoinType(), account.Index, i, false),
ScriptPubKey = address.ScriptPubKey,
Address = address.ToString(),
Transactions = new List<TransactionData>(),
CreationTime = DateTimeOffset.Now
}});
addressesCreated.Add(address.ToString());
}
return addressesCreated;
} }
public WalletGeneralInfoModel GetGeneralInfo(string name) public WalletGeneralInfoModel GetGeneralInfo(string name)
...@@ -279,7 +310,7 @@ namespace Breeze.Wallet ...@@ -279,7 +310,7 @@ namespace Breeze.Wallet
foreach (TxIn input in transaction.Inputs.Where(txIn => this.TrackedTransactions.Any(trackedTx => trackedTx.Hash == txIn.PrevOut.Hash))) foreach (TxIn input in transaction.Inputs.Where(txIn => this.TrackedTransactions.Any(trackedTx => trackedTx.Hash == txIn.PrevOut.Hash)))
{ {
TransactionDetails tTx = this.TrackedTransactions.Single(trackedTx => trackedTx.Hash == input.PrevOut.Hash); TransactionDetails tTx = this.TrackedTransactions.Single(trackedTx => trackedTx.Hash == input.PrevOut.Hash);
// compare the index of the output in its original transaction and the index references in the input // compare the index of the output in its original transaction and the index references in the input
if (input.PrevOut.N == tTx.Index) if (input.PrevOut.N == tTx.Index)
{ {
...@@ -287,7 +318,7 @@ namespace Breeze.Wallet ...@@ -287,7 +318,7 @@ namespace Breeze.Wallet
} }
} }
} }
} }
/// <summary> /// <summary>
/// Adds the transaction to the wallet. /// Adds the transaction to the wallet.
...@@ -318,7 +349,7 @@ namespace Breeze.Wallet ...@@ -318,7 +349,7 @@ namespace Breeze.Wallet
{ {
foreach (var account in accountRoot.Accounts) foreach (var account in accountRoot.Accounts)
{ {
foreach (var address in account.ExternalAddresses)//.Where(a => a.ScriptPubKey == script)) foreach (var address in account.ExternalAddresses.Where(a => a.ScriptPubKey == script))
{ {
address.Transactions = address.Transactions.Concat(new[] address.Transactions = address.Transactions.Concat(new[]
{ {
...@@ -331,7 +362,7 @@ namespace Breeze.Wallet ...@@ -331,7 +362,7 @@ namespace Breeze.Wallet
CreationTime = DateTimeOffset.FromUnixTimeMilliseconds(blockTime ?? time), CreationTime = DateTimeOffset.FromUnixTimeMilliseconds(blockTime ?? time),
Index = index Index = index
} }
}); });
} }
} }
} }
...@@ -343,7 +374,7 @@ namespace Breeze.Wallet ...@@ -343,7 +374,7 @@ namespace Breeze.Wallet
Index = index, Index = index,
Amount = amount Amount = amount
}); });
} }
} }
/// <inheritdoc /> /// <inheritdoc />
...@@ -498,9 +529,9 @@ namespace Breeze.Wallet ...@@ -498,9 +529,9 @@ namespace Breeze.Wallet
SelectMany(w => w.AccountsRoot.Where(a => a.CoinType == coinType)). SelectMany(w => w.AccountsRoot.Where(a => a.CoinType == coinType)).
SelectMany(a => a.Accounts). SelectMany(a => a.Accounts).
SelectMany(a => a.ExternalAddresses). SelectMany(a => a.ExternalAddresses).
//Select(s => s.ScriptPubKey)); Select(s => s.ScriptPubKey));
// uncomment the following for testing on a random address // uncomment the following for testing on a random address
Select(t => (new BitcoinPubKeyAddress(t.Address, Network.Main)).ScriptPubKey)); //Select(t => (new BitcoinPubKeyAddress(t.Address, Network.Main)).ScriptPubKey));
} }
/// <summary> /// <summary>
...@@ -521,7 +552,7 @@ namespace Breeze.Wallet ...@@ -521,7 +552,7 @@ namespace Breeze.Wallet
Index = t.Index, Index = t.Index,
Amount = t.Amount Amount = t.Amount
})); }));
} }
} }
public class TransactionDetails public class TransactionDetails
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment