Commit cea9240b authored by Jeremy Bokobza's avatar Jeremy Bokobza

Fixed catching transactions in change addresses.

parent 56c9a9cf
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
"variables": [], "variables": [],
"info": { "info": {
"name": "Wallet", "name": "Wallet",
"_postman_id": "57013f2c-02dc-df32-41e9-6e4aaa14ad5e", "_postman_id": "b5720ab4-24a5-6957-0ea6-766a9cbaf488",
"description": "Requests relating to operations on the wallet", "description": "Requests relating to operations on the wallet",
"schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json" "schema": "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"
}, },
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
], ],
"body": { "body": {
"mode": "raw", "mode": "raw",
"raw": "{ \n\t\"password\": \"123456\",\n\t\"network\": \"Main\",\n\t\"folderPath\": \"Wallets\",\n\t\"name\": \"myFirstWallet\"\n}" "raw": "{ \n\t\"password\": \"123456\",\n\t\"network\": \"testnet\",\n\t\"name\": \"testwallet\"\n}"
}, },
"description": "" "description": ""
}, },
...@@ -161,7 +161,7 @@ ...@@ -161,7 +161,7 @@
], ],
"body": { "body": {
"mode": "raw", "mode": "raw",
"raw": "{\r\n \"password\": \"password\",\r\n \"address\": \"1FYp9uguYCz7DgSF9jTWDeZF8kdRKQTXPg\",\r\n \"amount\": \"0.12\",\r\n \"feeType\": \"low\",\r\n \"allowUnconfirmed\": \"true\"\r\n}" "raw": "{\r\n\t\"walletName\": \"testwallet\",\r\n\t\"accountName\": \"account 0\",\r\n\t\"coinType\": 1,\r\n \"password\": \"password\",\r\n \"destinationAddress\": \"1FYp9uguYCz7DgSF9jTWDeZF8kdRKQTXPg\",\r\n \"amount\": \"0.12\",\r\n \"feeType\": \"low\",\r\n \"allowUnconfirmed\": \"true\"\r\n}"
}, },
"description": "" "description": ""
}, },
...@@ -206,7 +206,7 @@ ...@@ -206,7 +206,7 @@
"description": "Gets all the wallets files stored in the default folder" "description": "Gets all the wallets files stored in the default folder"
}, },
"response": [] "response": []
}, },
{ {
"name": "Get unused account in wallet", "name": "Get unused account in wallet",
"request": { "request": {
......
...@@ -84,6 +84,20 @@ namespace Breeze.Wallet ...@@ -84,6 +84,20 @@ namespace Breeze.Wallet
} }
return result; return result;
} }
/// <summary>
/// Gets all the pub keys conatined in this wallet.
/// </summary>
/// <param name="coinType">Type of the coin.</param>
/// <returns></returns>
public IEnumerable<Script> GetAllPubKeysByCoinType(CoinType coinType)
{
var accounts = this.GetAccountsByCoinType(coinType).ToList();
foreach (var address in accounts.SelectMany(a => a.ExternalAddresses).Concat(accounts.SelectMany(a => a.InternalAddresses)))
{
yield return address.ScriptPubKey;
}
}
} }
/// <summary> /// <summary>
...@@ -142,6 +156,9 @@ namespace Breeze.Wallet ...@@ -142,6 +156,9 @@ namespace Breeze.Wallet
} }
return account; return account;
} }
} }
/// <summary> /// <summary>
......
...@@ -51,13 +51,13 @@ namespace Breeze.Wallet ...@@ -51,13 +51,13 @@ namespace Breeze.Wallet
} }
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.coinType = (CoinType)netwrok.Consensus.CoinType; this.coinType = (CoinType)netwrok.Consensus.CoinType;
// load data in memory for faster lookups // load data in memory for faster lookups
// TODO get the coin type from somewhere else // TODO get the coin type from somewhere else
this.PubKeys = this.LoadKeys(this.coinType); this.PubKeys = this.LoadKeys(this.coinType);
this.TrackedTransactions = this.LoadTransactions(this.coinType); this.TrackedTransactions = this.LoadTransactions(this.coinType);
this.TransactionFound += this.OnTransactionFound; this.TransactionFound += this.OnTransactionFound;
} }
/// <inheritdoc /> /// <inheritdoc />
...@@ -209,7 +209,7 @@ namespace Breeze.Wallet ...@@ -209,7 +209,7 @@ namespace Breeze.Wallet
return newAccount; return newAccount;
} }
/// <inheritdoc /> /// <inheritdoc />
public string GetUnusedAddress(string walletName, CoinType coinType, string accountName) public string GetUnusedAddress(string walletName, CoinType coinType, string accountName)
{ {
...@@ -333,11 +333,11 @@ namespace Breeze.Wallet ...@@ -333,11 +333,11 @@ namespace Breeze.Wallet
{ {
throw new Exception($"Cannot send transaction with 0 {this.coinType}"); throw new Exception($"Cannot send transaction with 0 {this.coinType}");
} }
// get the wallet and the account // get the wallet and the account
Wallet wallet = this.GetWalletByName(walletName); Wallet wallet = this.GetWalletByName(walletName);
HdAccount account = wallet.AccountsRoot.Single(a => a.CoinType == coinType).GetAccountByName(accountName); HdAccount account = wallet.AccountsRoot.Single(a => a.CoinType == coinType).GetAccountByName(accountName);
// get a list of transactions outputs that have not been spent // get a list of transactions outputs that have not been spent
IEnumerable<TransactionData> spendableTransactions = account.GetSpendableTransactions(); IEnumerable<TransactionData> spendableTransactions = account.GetSpendableTransactions();
...@@ -352,18 +352,18 @@ namespace Breeze.Wallet ...@@ -352,18 +352,18 @@ namespace Breeze.Wallet
// calculate which addresses needs to be used as well as the fee to be charged // calculate which addresses needs to be used as well as the fee to be charged
var calculationResult = this.CalculateFees(spendableTransactions, amount); var calculationResult = this.CalculateFees(spendableTransactions, amount);
// get extended private key // get extended private key
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 signingKeys = new HashSet<ISecret>(); var signingKeys = new HashSet<ISecret>();
var coins = new List<Coin>(); var coins = new List<Coin>();
foreach (var transactionToUse in calculationResult.transactionsToUse) foreach (var transactionToUse in calculationResult.transactionsToUse)
{ {
var address = account.FindAddressForTransaction(transactionToUse.Id); var address = account.FindAddressForTransaction(transactionToUse.Id);
ExtKey addressExtKey = seedExtKey.Derive(new KeyPath(address.HdPath)); ExtKey addressExtKey = seedExtKey.Derive(new KeyPath(address.HdPath));
BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(wallet.Network); BitcoinExtKey addressPrivateKey = addressExtKey.GetWif(wallet.Network);
signingKeys.Add(addressPrivateKey); signingKeys.Add(addressPrivateKey);
coins.Add(new Coin(transactionToUse.Id, (uint)transactionToUse.Index, transactionToUse.Amount, address.ScriptPubKey)); coins.Add(new Coin(transactionToUse.Id, (uint)transactionToUse.Index, transactionToUse.Amount, address.ScriptPubKey));
...@@ -474,7 +474,7 @@ namespace Breeze.Wallet ...@@ -474,7 +474,7 @@ namespace Breeze.Wallet
// 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)
{ {
AddTransactionToWallet(coinType, transaction.GetHash(), transaction.Time, null, -tTx.Amount, pubKey, blockHeight, blockTime, tTx.Hash, tTx.Index); AddTransactionToWallet(coinType, transaction.GetHash(), transaction.Time, null, -tTx.Amount, pubKey, blockHeight, blockTime, tTx.Hash, tTx.Index);
} }
} }
} }
...@@ -496,14 +496,10 @@ namespace Breeze.Wallet ...@@ -496,14 +496,10 @@ namespace Breeze.Wallet
private void AddTransactionToWallet(CoinType coinType, uint256 transactionHash, uint time, int? index, Money amount, Script script, int? blockHeight = null, uint? blockTime = null, uint256 spendingTransactionId = null, int? spendingTransactionIndex = null) private void AddTransactionToWallet(CoinType coinType, uint256 transactionHash, uint time, int? index, Money amount, Script script, int? blockHeight = null, uint? blockTime = null, uint256 spendingTransactionId = null, int? spendingTransactionIndex = null)
{ {
// selects all the transactions we already have in the wallet // selects all the transactions we already have in the wallet
var txs = this.Wallets. var txs = this.Wallets.SelectMany(w => w.GetAllTransactionsByCoinType(coinType));
SelectMany(w => w.AccountsRoot.Where(a => a.CoinType == coinType)).
SelectMany(a => a.Accounts).
SelectMany(a => a.ExternalAddresses).
SelectMany(t => t.Transactions);
// add this transaction if it is not in the list // add this transaction if it is not in the list
if (txs.All(t => t.Id != transactionHash)) if (txs.All(t => t.Id != transactionHash || t.Index != index))
{ {
foreach (var wallet in this.Wallets) foreach (var wallet in this.Wallets)
{ {
...@@ -511,7 +507,12 @@ namespace Breeze.Wallet ...@@ -511,7 +507,12 @@ 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)) var receivingAddress = account.ExternalAddresses.SingleOrDefault(a => a.ScriptPubKey == script);
var changeAddress = account.InternalAddresses.SingleOrDefault(a => a.ScriptPubKey == script);
bool isChange = receivingAddress == null && changeAddress != null;
var address = receivingAddress ?? changeAddress;
if (address != null)
{ {
address.Transactions = address.Transactions.Concat(new[] address.Transactions = address.Transactions.Concat(new[]
{ {
...@@ -520,23 +521,23 @@ namespace Breeze.Wallet ...@@ -520,23 +521,23 @@ namespace Breeze.Wallet
Amount = amount, Amount = amount,
BlockHeight = blockHeight, BlockHeight = blockHeight,
Confirmed = blockHeight.HasValue, Confirmed = blockHeight.HasValue,
Id = transactionHash, Id = transactionHash,
CreationTime = DateTimeOffset.FromUnixTimeMilliseconds(blockTime ?? time), CreationTime = DateTimeOffset.FromUnixTimeMilliseconds(blockTime ?? time),
Index = index Index = index
} }
}); });
// notify a transaction has been found // notify a transaction has been found
this.TransactionFound?.Invoke(this, new TransactionFoundEventArgs(wallet, accountRoot.CoinType, account, address, false)); this.TransactionFound?.Invoke(this, new TransactionFoundEventArgs(wallet, accountRoot.CoinType, account, address, isChange));
}
// if this is a spending transaction, mark the spent transaction as such // if this is a spending transaction, mark the spent transaction as such
if (spendingTransactionId != null) if (spendingTransactionId != null)
{
var transactions = account.GetTransactionsById(spendingTransactionId);
if (transactions.Any())
{ {
transactions.Single(t => t.Index == spendingTransactionIndex).SpentInTransaction = transactionHash; var transactions = account.GetTransactionsById(spendingTransactionId);
if (transactions.Any())
{
transactions.Single(t => t.Index == spendingTransactionIndex).SpentInTransaction = transactionHash;
}
} }
} }
} }
...@@ -725,13 +726,12 @@ namespace Breeze.Wallet ...@@ -725,13 +726,12 @@ namespace Breeze.Wallet
/// <returns></returns> /// <returns></returns>
private HashSet<Script> LoadKeys(CoinType coinType) private HashSet<Script> LoadKeys(CoinType coinType)
{ {
return new HashSet<Script>(this.Wallets. var keys = new HashSet<Script>();
SelectMany(w => w.AccountsRoot.Where(a => a.CoinType == coinType)). foreach (Wallet wallet in this.Wallets)
SelectMany(a => a.Accounts). {
SelectMany(a => a.ExternalAddresses). keys.UnionWith(wallet.GetAllPubKeysByCoinType(coinType));
Select(s => s.ScriptPubKey)); }
// uncomment the following for testing on a random address return keys;
//Select(t => (new BitcoinPubKeyAddress(t.Address, Network.Main)).ScriptPubKey));
} }
/// <summary> /// <summary>
...@@ -741,17 +741,19 @@ namespace Breeze.Wallet ...@@ -741,17 +741,19 @@ namespace Breeze.Wallet
/// <returns></returns> /// <returns></returns>
private HashSet<TransactionDetails> LoadTransactions(CoinType coinType) private HashSet<TransactionDetails> LoadTransactions(CoinType coinType)
{ {
return new HashSet<TransactionDetails>(this.Wallets. var keys = new HashSet<TransactionDetails>();
SelectMany(w => w.AccountsRoot.Where(a => a.CoinType == coinType)). foreach (Wallet wallet in this.Wallets)
SelectMany(a => a.Accounts). {
SelectMany(a => a.ExternalAddresses). keys.UnionWith(wallet.GetAllTransactionsByCoinType(coinType)
SelectMany(t => t.Transactions). .Select(t =>
Select(t => new TransactionDetails new TransactionDetails
{ {
Hash = t.Id, Hash = t.Id,
Index = t.Index, Index = t.Index,
Amount = t.Amount Amount = t.Amount
})); }));
}
return keys;
} }
/// <summary> /// <summary>
......
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