Commit f8eff20e authored by Jeremy Bokobza's avatar Jeremy Bokobza

Added Merkel Proof to receiving transactions

parent 662503f8
...@@ -141,7 +141,7 @@ namespace Breeze.Wallet ...@@ -141,7 +141,7 @@ namespace Breeze.Wallet
/// <param name="allowUnconfirmed">Whether or not we allow this transaction to rely on unconfirmed outputs.</param> /// <param name="allowUnconfirmed">Whether or not we allow this transaction to rely on unconfirmed outputs.</param>
/// <returns></returns> /// <returns></returns>
(string hex, uint256 transactionId, Money fee) BuildTransaction(string walletName, string accountName, CoinType coinType, string password, string destinationAddress, Money amount, string feeType, bool allowUnconfirmed); (string hex, uint256 transactionId, Money fee) BuildTransaction(string walletName, string accountName, CoinType coinType, string password, string destinationAddress, Money amount, string feeType, bool allowUnconfirmed);
/// <summary> /// <summary>
/// Sends a transaction to the network. /// Sends a transaction to the network.
/// </summary> /// </summary>
...@@ -161,8 +161,8 @@ namespace Breeze.Wallet ...@@ -161,8 +161,8 @@ namespace Breeze.Wallet
/// </summary> /// </summary>
/// <param name="transaction">The transaction.</param> /// <param name="transaction">The transaction.</param>
/// <param name="blockHeight">The height of the block this transaction came from. Null if it was not a transaction included in a block.</param> /// <param name="blockHeight">The height of the block this transaction came from. Null if it was not a transaction included in a block.</param>
/// <param name="blockTime">The block time.</param> /// <param name="block">The block in which this transaction was included.</param>
void ProcessTransaction(Transaction transaction, int? blockHeight = null, uint? blockTime = null); void ProcessTransaction(Transaction transaction, int? blockHeight = null, Block block = null);
/// <summary> /// <summary>
/// Saves the wallet into the file system. /// Saves the wallet into the file system.
...@@ -188,4 +188,4 @@ namespace Breeze.Wallet ...@@ -188,4 +188,4 @@ namespace Breeze.Wallet
/// <param name="height">The height of the last block synced.</param> /// <param name="height">The height of the last block synced.</param>
void UpdateLastBlockSyncedHeight(int height); void UpdateLastBlockSyncedHeight(int height);
} }
} }
\ No newline at end of file
...@@ -31,7 +31,7 @@ namespace Breeze.Wallet ...@@ -31,7 +31,7 @@ namespace Breeze.Wallet
[JsonProperty(PropertyName = "chainCode")] [JsonProperty(PropertyName = "chainCode")]
[JsonConverter(typeof(ByteArrayConverter))] [JsonConverter(typeof(ByteArrayConverter))]
public byte[] ChainCode { get; set; } public byte[] ChainCode { get; set; }
/// <summary> /// <summary>
/// The network this wallet is for. /// The network this wallet is for.
/// </summary> /// </summary>
...@@ -77,7 +77,7 @@ namespace Breeze.Wallet ...@@ -77,7 +77,7 @@ namespace Breeze.Wallet
{ {
List<TransactionData> result = new List<TransactionData>(); List<TransactionData> result = new List<TransactionData>();
var accounts = this.GetAccountsByCoinType(coinType).ToList(); var accounts = this.GetAccountsByCoinType(coinType).ToList();
foreach (var address in accounts.SelectMany(a => a.ExternalAddresses).Concat(accounts.SelectMany(a => a.InternalAddresses))) foreach (var address in accounts.SelectMany(a => a.ExternalAddresses).Concat(accounts.SelectMany(a => a.InternalAddresses)))
{ {
result.AddRange(address.Transactions); result.AddRange(address.Transactions);
...@@ -91,12 +91,12 @@ namespace Breeze.Wallet ...@@ -91,12 +91,12 @@ namespace Breeze.Wallet
/// <param name="coinType">Type of the coin.</param> /// <param name="coinType">Type of the coin.</param>
/// <returns></returns> /// <returns></returns>
public IEnumerable<Script> GetAllPubKeysByCoinType(CoinType coinType) public IEnumerable<Script> GetAllPubKeysByCoinType(CoinType coinType)
{ {
var accounts = this.GetAccountsByCoinType(coinType).ToList(); var accounts = this.GetAccountsByCoinType(coinType).ToList();
foreach (var address in accounts.SelectMany(a => a.ExternalAddresses).Concat(accounts.SelectMany(a => a.InternalAddresses))) foreach (var address in accounts.SelectMany(a => a.ExternalAddresses).Concat(accounts.SelectMany(a => a.InternalAddresses)))
{ {
yield return address.ScriptPubKey; yield return address.ScriptPubKey;
} }
} }
} }
...@@ -116,7 +116,7 @@ namespace Breeze.Wallet ...@@ -116,7 +116,7 @@ namespace Breeze.Wallet
/// </summary> /// </summary>
[JsonProperty(PropertyName = "lastBlockSyncedHeight", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty(PropertyName = "lastBlockSyncedHeight", NullValueHandling = NullValueHandling.Ignore)]
public int? LastBlockSyncedHeight { get; set; } public int? LastBlockSyncedHeight { get; set; }
/// <summary> /// <summary>
/// The accounts used in the wallet. /// The accounts used in the wallet.
/// </summary> /// </summary>
...@@ -155,7 +155,7 @@ namespace Breeze.Wallet ...@@ -155,7 +155,7 @@ 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.");
} }
return account; return account;
} }
} }
/// <summary> /// <summary>
...@@ -315,7 +315,7 @@ namespace Breeze.Wallet ...@@ -315,7 +315,7 @@ namespace Breeze.Wallet
/// <returns></returns> /// <returns></returns>
public IEnumerable<TransactionData> GetSpendableTransactions() public IEnumerable<TransactionData> GetSpendableTransactions()
{ {
var addresses = this.ExternalAddresses.Concat(this.InternalAddresses); var addresses = this.ExternalAddresses.Concat(this.InternalAddresses);
return addresses.SelectMany(a => a.Transactions.Where(t => t.SpentInTransaction == null && t.Amount > Money.Zero)); return addresses.SelectMany(a => a.Transactions.Where(t => t.SpentInTransaction == null && t.Amount > Money.Zero));
} }
...@@ -344,7 +344,7 @@ namespace Breeze.Wallet ...@@ -344,7 +344,7 @@ namespace Breeze.Wallet
/// </summary> /// </summary>
[JsonProperty(PropertyName = "index")] [JsonProperty(PropertyName = "index")]
public int Index { get; set; } public int Index { get; set; }
/// <summary> /// <summary>
/// The script pub key for this address. /// The script pub key for this address.
/// </summary> /// </summary>
...@@ -355,7 +355,7 @@ namespace Breeze.Wallet ...@@ -355,7 +355,7 @@ namespace Breeze.Wallet
/// <summary> /// <summary>
/// The Base58 representation of this address. /// The Base58 representation of this address.
/// </summary> /// </summary>
[JsonProperty(PropertyName = "address")] [JsonProperty(PropertyName = "address")]
public string Address { get; set; } public string Address { get; set; }
/// <summary> /// <summary>
...@@ -383,7 +383,7 @@ namespace Breeze.Wallet ...@@ -383,7 +383,7 @@ namespace Breeze.Wallet
/// <c>true</c> if it is a change address; otherwise, <c>false</c>. /// <c>true</c> if it is a change address; otherwise, <c>false</c>.
/// </returns> /// </returns>
public bool IsChangeAddress() public bool IsChangeAddress()
{ {
return int.Parse(this.HdPath.Split('/')[4]) == 1; return int.Parse(this.HdPath.Split('/')[4]) == 1;
} }
} }
...@@ -431,7 +431,7 @@ namespace Breeze.Wallet ...@@ -431,7 +431,7 @@ namespace Breeze.Wallet
/// </summary> /// </summary>
[JsonProperty(PropertyName = "blockHeight", NullValueHandling = NullValueHandling.Ignore)] [JsonProperty(PropertyName = "blockHeight", NullValueHandling = NullValueHandling.Ignore)]
public int? BlockHeight { get; set; } public int? BlockHeight { get; set; }
/// <summary> /// <summary>
/// Gets or sets the creation time. /// Gets or sets the creation time.
/// </summary> /// </summary>
...@@ -439,6 +439,13 @@ namespace Breeze.Wallet ...@@ -439,6 +439,13 @@ namespace Breeze.Wallet
[JsonConverter(typeof(DateTimeOffsetConverter))] [JsonConverter(typeof(DateTimeOffsetConverter))]
public DateTimeOffset CreationTime { get; set; } public DateTimeOffset CreationTime { get; set; }
/// <summary>
/// Gets or sets the Merkle proof for this transaction.
/// </summary>
[JsonProperty(PropertyName = "merkleProof")]
public MerkleProof MerkleProof { get; set; }
/// <summary> /// <summary>
/// Determines whether this transaction is confirmed. /// Determines whether this transaction is confirmed.
/// </summary> /// </summary>
...@@ -473,4 +480,23 @@ namespace Breeze.Wallet ...@@ -473,4 +480,23 @@ namespace Breeze.Wallet
[JsonConverter(typeof(MoneyJsonConverter))] [JsonConverter(typeof(MoneyJsonConverter))]
public Money Amount { get; set; } public Money Amount { get; set; }
} }
/// <summary>
/// An object representing a Merkle proof
/// </summary>
public class MerkleProof
{
/// <summary>
/// Gets or sets the merkle root.
/// </summary>
[JsonProperty(PropertyName = "merkleRoot")]
[JsonConverter(typeof(UInt256JsonConverter))]
public uint256 MerkleRoot { get; set; }
/// <summary>
/// Gets or sets the merkle path.
/// </summary>
[JsonProperty(PropertyName = "merklePath", ItemConverterType = typeof(UInt256JsonConverter))]
public ICollection<uint256> MerklePath { get; set; }
}
} }
\ No newline at end of file
...@@ -451,7 +451,7 @@ namespace Breeze.Wallet ...@@ -451,7 +451,7 @@ namespace Breeze.Wallet
foreach (Transaction transaction in block.Transactions) foreach (Transaction transaction in block.Transactions)
{ {
this.ProcessTransaction(transaction, height, block.Header.Time); this.ProcessTransaction(transaction, height, block);
} }
// update the wallets with the last processed block height // update the wallets with the last processed block height
...@@ -459,7 +459,7 @@ namespace Breeze.Wallet ...@@ -459,7 +459,7 @@ namespace Breeze.Wallet
} }
/// <inheritdoc /> /// <inheritdoc />
public void ProcessTransaction(Transaction transaction, int? blockHeight = null, uint? blockTime = null) public void ProcessTransaction(Transaction transaction, int? blockHeight = null, Block block = null)
{ {
Console.WriteLine($"transaction notification: tx hash {transaction.GetHash()}, coin type: {this.coinType}"); Console.WriteLine($"transaction notification: tx hash {transaction.GetHash()}, coin type: {this.coinType}");
...@@ -470,7 +470,7 @@ namespace Breeze.Wallet ...@@ -470,7 +470,7 @@ namespace Breeze.Wallet
var utxo = transaction.Outputs.SingleOrDefault(o => pubKey == o.ScriptPubKey); var utxo = transaction.Outputs.SingleOrDefault(o => pubKey == o.ScriptPubKey);
if (utxo != null) if (utxo != null)
{ {
AddTransactionToWallet(transaction.GetHash(), transaction.Time, transaction.Outputs.IndexOf(utxo), utxo.Value, pubKey, blockHeight, blockTime); AddTransactionToWallet(transaction.GetHash(), transaction.Time, transaction.Outputs.IndexOf(utxo), utxo.Value, pubKey, blockHeight, block);
} }
} }
...@@ -486,7 +486,7 @@ namespace Breeze.Wallet ...@@ -486,7 +486,7 @@ namespace Breeze.Wallet
// We first include the keys we don't hold and then we include the keys we do hold but that are for receiving addresses (which would mean the user paid itself). // We first include the keys we don't hold and then we include the keys we do hold but that are for receiving addresses (which would mean the user paid itself).
IEnumerable<TxOut> paidoutto = transaction.Outputs.Where(o => !this.keysLookup.Keys.Contains(o.ScriptPubKey) || (this.keysLookup.ContainsKey(o.ScriptPubKey) && !this.keysLookup[o.ScriptPubKey].IsChangeAddress())); IEnumerable<TxOut> paidoutto = transaction.Outputs.Where(o => !this.keysLookup.Keys.Contains(o.ScriptPubKey) || (this.keysLookup.ContainsKey(o.ScriptPubKey) && !this.keysLookup[o.ScriptPubKey].IsChangeAddress()));
AddTransactionToWallet(transaction.GetHash(), transaction.Time, null, -tTx.Amount, keyToSpend, blockHeight, blockTime, tTx.Id, tTx.Index, paidoutto); AddTransactionToWallet(transaction.GetHash(), transaction.Time, null, -tTx.Amount, keyToSpend, blockHeight, block, tTx.Id, tTx.Index, paidoutto);
} }
} }
...@@ -499,14 +499,15 @@ namespace Breeze.Wallet ...@@ -499,14 +499,15 @@ namespace Breeze.Wallet
/// <param name="amount">The amount.</param> /// <param name="amount">The amount.</param>
/// <param name="script">The script.</param> /// <param name="script">The script.</param>
/// <param name="blockHeight">Height of the block.</param> /// <param name="blockHeight">Height of the block.</param>
/// <param name="blockTime">The block time.</param> /// <param name="block">The block containing the transaction to add.</param>
/// <param name="spendingTransactionId">The id of the transaction containing the output being spent, if this is a spending transaction.</param> /// <param name="spendingTransactionId">The id of the transaction containing the output being spent, if this is a spending transaction.</param>
/// <param name="spendingTransactionIndex">The index of the output in the transaction being referenced, if this is a spending transaction.</param> /// <param name="spendingTransactionIndex">The index of the output in the transaction being referenced, if this is a spending transaction.</param>
private void AddTransactionToWallet(uint256 transactionHash, uint time, int? index, Money amount, Script script, int? blockHeight = null, uint? blockTime = null, uint256 spendingTransactionId = null, int? spendingTransactionIndex = null, IEnumerable<TxOut> paidToOutputs = null) private void AddTransactionToWallet(uint256 transactionHash, uint time, int? index, Money amount, Script script, int? blockHeight = null, Block block = null, uint256 spendingTransactionId = null, int? spendingTransactionIndex = null, IEnumerable<TxOut> paidToOutputs = null)
{ {
// get the collection of transactions to add to. // get the collection of transactions to add to.
this.keysLookup.TryGetValue(script, out HdAddress address); this.keysLookup.TryGetValue(script, out HdAddress address);
var isSpendingTransaction = paidToOutputs != null && paidToOutputs.Any();
var trans = address.Transactions; var trans = address.Transactions;
// if it's the first time we see this transaction // if it's the first time we see this transaction
...@@ -517,13 +518,24 @@ namespace Breeze.Wallet ...@@ -517,13 +518,24 @@ namespace Breeze.Wallet
Amount = amount, Amount = amount,
BlockHeight = blockHeight, BlockHeight = blockHeight,
Id = transactionHash, Id = transactionHash,
CreationTime = DateTimeOffset.FromUnixTimeSeconds(blockTime ?? time), CreationTime = DateTimeOffset.FromUnixTimeSeconds(block?.Header.Time ?? time),
Index = index Index = index
}; };
trans.Add(newTransaction);
// add the Merkle proof to the (non-spending) transaction
if (block != null && !isSpendingTransaction)
{
MerkleBlock merkleBlock = new MerkleBlock(block, new[] { transactionHash });
newTransaction.MerkleProof = new MerkleProof
{
MerkleRoot = block.Header.HashMerkleRoot,
MerklePath = merkleBlock.PartialMerkleTree.Hashes
};
}
// if this is a spending transaction, keep a record of the payments made out to other scripts. // if this is a spending transaction, keep a record of the payments made out to other scripts.
if (paidToOutputs != null && paidToOutputs.Any()) if (isSpendingTransaction)
{ {
List<PaymentDetails> payments = new List<PaymentDetails>(); List<PaymentDetails> payments = new List<PaymentDetails>();
foreach (var paidToOutput in paidToOutputs) foreach (var paidToOutput in paidToOutputs)
...@@ -539,6 +551,8 @@ namespace Breeze.Wallet ...@@ -539,6 +551,8 @@ namespace Breeze.Wallet
newTransaction.Payments = payments; newTransaction.Payments = payments;
} }
trans.Add(newTransaction);
// 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)
{ {
...@@ -560,9 +574,9 @@ namespace Breeze.Wallet ...@@ -560,9 +574,9 @@ namespace Breeze.Wallet
} }
// update the block time // update the block time
if (blockTime != null) if (block != null)
{ {
foundTransaction.CreationTime = DateTimeOffset.FromUnixTimeSeconds(blockTime.Value); foundTransaction.CreationTime = DateTimeOffset.FromUnixTimeSeconds(block.Header.Time);
} }
} }
...@@ -835,4 +849,4 @@ namespace Breeze.Wallet ...@@ -835,4 +849,4 @@ namespace Breeze.Wallet
this.TransactionHash = transactionHash; this.TransactionHash = transactionHash;
} }
} }
} }
\ No newline at end of file
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