Commit 73763b96 authored by Jeremy Bokobza's avatar Jeremy Bokobza

import of HBitcoin's Tracker and its dependencies

parent c5350c48
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ConcurrentHashSet" Version="1.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Mvc.Versioning" Version="1.0.3" />
<PackageReference Include="Stratis.Bitcoin" Version="1.0.1.5-alpha" /> <PackageReference Include="Stratis.Bitcoin" Version="1.0.1.5-alpha" />
</ItemGroup> </ItemGroup>
......
//from https://github.com/brianchance/MonoTouchMVVMCrossValidationTester/blob/master/Validation.Core/ConcurrentObservableDictionary.cs
//modified
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace System.Collections.ObjectModel
{
public class ConcurrentObservableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
private const string CountString = "Count";
private const string IndexerName = "Item[]";
private const string KeysName = "Keys";
private const string ValuesName = "Values";
private readonly object Lock = new object();
protected ConcurrentDictionary<TKey, TValue> ConcurrentDictionary { get; private set; }
#region Constructors
public ConcurrentObservableDictionary()
{
ConcurrentDictionary = new ConcurrentDictionary<TKey, TValue>();
}
public ConcurrentObservableDictionary(ConcurrentDictionary<TKey, TValue> dictionary)
{
ConcurrentDictionary = new ConcurrentDictionary<TKey, TValue>(dictionary);
}
public ConcurrentObservableDictionary(IEqualityComparer<TKey> comparer)
{
ConcurrentDictionary = new ConcurrentDictionary<TKey, TValue>(comparer);
}
public ConcurrentObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
{
ConcurrentDictionary = new ConcurrentDictionary<TKey, TValue>(dictionary, comparer);
}
#endregion
#region IDictionary<TKey,TValue> Members
public void Add(TKey key, TValue value) => Insert(key, value, true);
public bool ContainsKey(TKey key) => ConcurrentDictionary.ContainsKey(key);
public ICollection<TKey> Keys => ConcurrentDictionary.Keys;
public bool Remove(TKey key) => Remove(key, suppressNotifications: false);
private bool Remove(TKey key, bool suppressNotifications)
{
lock(Lock)
{
TValue value;
var ret = ConcurrentDictionary.TryRemove(key, out value);
if(ret && !suppressNotifications) OnCollectionChanged();
return ret;
}
}
public bool TryGetValue(TKey key, out TValue value) => ConcurrentDictionary.TryGetValue(key, out value);
public ICollection<TValue> Values => ConcurrentDictionary.Values;
public TValue this[TKey key]
{
get
{
TValue value;
return TryGetValue(key, out value) ? value : default(TValue);
}
set
{
Insert(key, value, false);
}
}
#endregion
#region ICollection<KeyValuePair<TKey,TValue>> Members
public void Add(KeyValuePair<TKey, TValue> item) => Insert(item.Key, item.Value, true);
public void Clear()
{
lock(Lock)
{
if (ConcurrentDictionary.Count > 0)
{
ConcurrentDictionary.Clear();
OnCollectionChanged();
}
}
}
public bool Contains(KeyValuePair<TKey, TValue> item) => ConcurrentDictionary.Contains(item);
/// <summary>
/// NotImplementedException
/// </summary>
/// <param name="array"></param>
/// <param name="arrayIndex"></param>
public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
/// <summary>
/// NotImplementedException
/// </summary>
public bool IsReadOnly
{
get { throw new NotImplementedException(); }
}
public int Count => ConcurrentDictionary.Count;
public bool Remove(KeyValuePair<TKey, TValue> item) => Remove(item.Key);
#endregion
#region IEnumerable<KeyValuePair<TKey,TValue>> Members
public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => ConcurrentDictionary.GetEnumerator();
#endregion
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)ConcurrentDictionary).GetEnumerator();
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
public void AddOrReplace(TKey key, TValue value)
{
if (ContainsKey(key))
{
Remove(key, suppressNotifications: true);
Add(key, value);
}
else
{
Add(key, value);
}
}
/// <summary>
/// NotImplementedException
/// </summary>
/// <param name="items"></param>
public void AddRange(IDictionary<TKey, TValue> items)
{
throw new NotImplementedException();
}
private void Insert(TKey key, TValue value, bool add)
{
lock(Lock)
{
if (key == null) throw new ArgumentNullException(nameof(key));
TValue item;
if (ConcurrentDictionary.TryGetValue(key, out item))
{
if (add) throw new ArgumentException("An item with the same key has already been added.");
if (Equals(item, value)) return;
ConcurrentDictionary[key] = value;
OnCollectionChanged(NotifyCollectionChangedAction.Replace, new KeyValuePair<TKey, TValue>(key, value), new KeyValuePair<TKey, TValue>(key, item));
OnPropertyChanged(key.ToString());
}
else
{
ConcurrentDictionary[key] = value;
OnCollectionChanged(NotifyCollectionChangedAction.Add, new KeyValuePair<TKey, TValue>(key, value));
OnPropertyChanged(key.ToString());
}
}
}
private void OnPropertyChanged()
{
OnPropertyChanged(CountString);
OnPropertyChanged(IndexerName);
OnPropertyChanged(KeysName);
OnPropertyChanged(ValuesName);
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private void OnCollectionChanged()
{
OnPropertyChanged();
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> changedItem)
{
OnPropertyChanged();
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action, changedItem, 0));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, KeyValuePair<TKey, TValue> newItem, KeyValuePair<TKey, TValue> oldItem)
{
OnPropertyChanged();
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action, newItem, oldItem, 0));
}
private void OnCollectionChanged(NotifyCollectionChangedAction action, IList newItems)
{
OnPropertyChanged();
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(action, newItems, 0));
}
}
}
\ No newline at end of file
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Linq;
using System.ComponentModel;
using System.Collections.Generic;
using System.Collections.Specialized;
using ConcurrentCollections;
namespace System.Collections.ObjectModel
{
public class ConcurrentObservableHashSet<T> : INotifyCollectionChanged, IReadOnlyCollection<T>
{
protected ConcurrentHashSet<T> ConcurrentHashSet { get; }
private readonly object Lock = new object();
public ConcurrentObservableHashSet()
{
ConcurrentHashSet = new ConcurrentHashSet<T>();
}
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void OnCollectionChanged()
{
CollectionChanged?.Invoke(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerator<T> GetEnumerator() => ConcurrentHashSet.GetEnumerator();
public bool TryAdd(T item)
{
lock(Lock)
{
if(ConcurrentHashSet.Add(item))
{
OnCollectionChanged();
return true;
}
return false;
}
}
public void Clear()
{
lock(Lock)
{
if(ConcurrentHashSet.Count > 0)
{
ConcurrentHashSet.Clear();
OnCollectionChanged();
}
}
}
public bool Contains(T item) => ConcurrentHashSet.Contains(item);
public bool TryRemove(T item)
{
lock(Lock)
{
if(ConcurrentHashSet.TryRemove(item))
{
OnCollectionChanged();
return true;
}
return false;
}
}
public int Count => ConcurrentHashSet.Count;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace HBitcoin.Models
{
public struct Height : IEquatable<Height>, IEquatable<int>, IComparable<Height>, IComparable<int>
{
public HeightType Type { get; }
private readonly int _value;
public int Value
{
get
{
if (Type == HeightType.Chain)
return _value;
if (Type == HeightType.MemPool)
return int.MaxValue - 1;
//if(Type == HeightType.Unknown)
return int.MaxValue;
}
}
public static Height MemPool => new Height(HeightType.MemPool);
public static Height Unknown => new Height(HeightType.Unknown);
public Height(int height)
{
if(height < 0) throw new ArgumentException($"{nameof(height)} : {height} cannot be < 0");
if(height == int.MaxValue) Type = HeightType.Unknown;
else if(height == int.MaxValue - 1) Type = HeightType.MemPool;
else Type = HeightType.Chain;
_value = height;
}
public Height(string heightOrHeightType)
{
var trimmed = heightOrHeightType.Trim();
if (trimmed == HeightType.MemPool.ToString())
this = MemPool;
else if (trimmed == HeightType.Unknown.ToString())
this = Unknown;
else this = new Height(int.Parse(trimmed));
}
public Height(HeightType type)
{
if(type == HeightType.Chain) throw new NotSupportedException($"For {type} height must be specified");
Type = type;
if (Type == HeightType.MemPool)
_value = int.MaxValue - 1;
else _value = int.MaxValue; // HeightType.Unknown
}
public override string ToString()
{
if(Type == HeightType.Chain) return Value.ToString();
else return Type.ToString();
}
#region EqualityAndComparison
public override bool Equals(object obj) => obj is Height && this == (Height) obj;
public bool Equals(Height other) => this == other;
public override int GetHashCode() => Value.GetHashCode();
public static bool operator ==(Height x, Height y) => x.Value == y.Value;
public static bool operator !=(Height x, Height y) => !(x == y);
public bool Equals(int other) => Value == other;
public static bool operator ==(int x, Height y) => x == y.Value;
public static bool operator ==(Height x, int y) => x.Value == y;
public static bool operator !=(int x, Height y) => !(x == y);
public static bool operator !=(Height x, int y) => !(x == y);
public int CompareTo(Height other) => Value.CompareTo(other.Value);
public int CompareTo(int other)
{
if (Value > other) return -1;
if (Value == other) return 0;
return 1;
}
public static bool operator >(Height x, Height y) => x.Value > y.Value;
public static bool operator <(Height x, Height y) => x.Value < y.Value;
public static bool operator >=(Height x, Height y) => x.Value >= y.Value;
public static bool operator <=(Height x, Height y) => x.Value <= y.Value;
public static bool operator >(int x, Height y) => x > y.Value;
public static bool operator >(Height x, int y) => x.Value > y;
public static bool operator <(int x, Height y) => x < y.Value;
public static bool operator <(Height x, int y) => x.Value < y;
public static bool operator >=(int x, Height y) => x >= y.Value;
public static bool operator <=(int x, Height y) => x <= y.Value;
public static bool operator >=(Height x, int y) => x.Value >= y;
public static bool operator <=(Height x, int y) => x.Value <= y;
#endregion
}
public enum HeightType
{
Chain,
MemPool,
Unknown
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using ConcurrentCollections;
using HBitcoin.Models;
using NBitcoin;
namespace HBitcoin.Models
{
public class SmartMerkleBlock : IEquatable<SmartMerkleBlock>, IComparable<SmartMerkleBlock>
{
#region Members
public Height Height { get; }
public MerkleBlock MerkleBlock { get; }
public IEnumerable<uint256> GetMatchedTransactions() => MerkleBlock.PartialMerkleTree.GetMatchedTransactions();
public uint TransactionCount => MerkleBlock.PartialMerkleTree.TransactionCount;
#endregion
#region Constructors
public SmartMerkleBlock()
{
}
public SmartMerkleBlock(Height height, Block block, params uint256[] interestedTransactionIds)
{
Height = height;
MerkleBlock = interestedTransactionIds == null || interestedTransactionIds.Length == 0 ? block.Filter() : block.Filter(interestedTransactionIds);
}
public SmartMerkleBlock(int height, Block block, params uint256[] interestedTransactionIds)
{
Height = new Height(height);
MerkleBlock = interestedTransactionIds == null || interestedTransactionIds.Length == 0 ? block.Filter() : block.Filter(interestedTransactionIds);
}
public SmartMerkleBlock(Height height, MerkleBlock merkleBlock)
{
Height = height;
MerkleBlock = merkleBlock;
}
#endregion
#region Formatting
public static byte[] ToBytes(SmartMerkleBlock smartMerkleBlock) =>
BitConverter.GetBytes(smartMerkleBlock.Height.Value) // 4bytes
.Concat(smartMerkleBlock.MerkleBlock.ToBytes())
.ToArray();
public byte[] ToBytes() => ToBytes(this);
public static SmartMerkleBlock FromBytes(byte[] bytes)
{
var heightBytes = bytes.Take(4).ToArray();
var merkleBlockBytes = bytes.Skip(4).ToArray();
var height = new Height(BitConverter.ToInt32(heightBytes, startIndex: 0));
// Bypass NBitcoin bug
var merkleBlock = new MerkleBlock();
if(!merkleBlock.ToBytes().SequenceEqual(merkleBlockBytes)) // if not default MerkleBlock
{
merkleBlock.FromBytes(merkleBlockBytes);
}
return new SmartMerkleBlock(height, merkleBlock);
}
#endregion
#region EqualityAndComparison
public override bool Equals(object obj) => obj is SmartMerkleBlock && this == (SmartMerkleBlock)obj;
public bool Equals(SmartMerkleBlock other) => this == other;
public override int GetHashCode()
{
var hash = Height.GetHashCode();
hash = hash ^ MerkleBlock.Header.GetHash().GetHashCode();
hash = hash ^ MerkleBlock.Header.HashPrevBlock.GetHashCode();
hash = hash ^ MerkleBlock.Header.HashMerkleRoot.GetHashCode();
foreach(uint256 txhash in GetMatchedTransactions())
hash = hash ^ txhash.GetHashCode();
return hash;
}
public static bool operator ==(SmartMerkleBlock x, SmartMerkleBlock y)
{
if (x.Height != y.Height)
return false;
if(x.MerkleBlock.Header.GetHash() != y.MerkleBlock.Header.GetHash())
return false;
if (x.MerkleBlock.Header.HashPrevBlock != y.MerkleBlock.Header.HashPrevBlock)
return false;
if (x.MerkleBlock.Header.HashMerkleRoot != y.MerkleBlock.Header.HashMerkleRoot)
return false;
if (x.TransactionCount != y.TransactionCount)
return false;
if(x.TransactionCount == 0) return true;
if (!x.GetMatchedTransactions().SequenceEqual(y.GetMatchedTransactions()))
return false;
return true;
}
public static bool operator !=(SmartMerkleBlock x, SmartMerkleBlock y) => !(x == y);
public int CompareTo(SmartMerkleBlock other) => Height.CompareTo(other.Height);
public static bool operator >(SmartMerkleBlock x, SmartMerkleBlock y) => x.Height > y.Height;
public static bool operator <(SmartMerkleBlock x, SmartMerkleBlock y) => x.Height < y.Height;
public static bool operator >=(SmartMerkleBlock x, SmartMerkleBlock y) => x.Height >= y.Height;
public static bool operator <=(SmartMerkleBlock x, SmartMerkleBlock y) => x.Height <= y.Height;
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using HBitcoin.Models;
using NBitcoin;
namespace HBitcoin.Models
{
public class SmartTransaction: IEquatable<SmartTransaction>
{
#region Members
public Height Height { get; }
public Transaction Transaction { get; }
public bool Confirmed => Height.Type == HeightType.Chain;
public uint256 GetHash() => Transaction.GetHash();
#endregion
#region Constructors
public SmartTransaction()
{
}
public SmartTransaction(Transaction transaction, Height height)
{
Height = height;
Transaction = transaction;
}
#endregion
#region Equality
public bool Equals(SmartTransaction other) => GetHash().Equals(other.GetHash());
public bool Equals(Transaction other) => GetHash().Equals(other.GetHash());
public override bool Equals(object obj)
{
bool rc = false;
if (obj is SmartTransaction)
{
var transaction = (SmartTransaction)obj;
rc = GetHash().Equals(transaction.GetHash());
}
else if (obj is Transaction)
{
var transaction = (Transaction)obj;
rc = GetHash().Equals(transaction.GetHash());
}
return rc;
}
public override int GetHashCode()
{
return GetHash().GetHashCode();
}
public static bool operator !=(SmartTransaction tx1, SmartTransaction tx2)
{
return !(tx1 == tx2);
}
public static bool operator ==(SmartTransaction tx1, SmartTransaction tx2)
{
bool rc;
if(ReferenceEquals(tx1, tx2)) rc = true;
else if((object) tx1 == null || (object) tx2 == null)
{
rc = false;
}
else
{
rc = tx1.GetHash().Equals(tx2.GetHash());
}
return rc;
}
public static bool operator ==(Transaction tx1, SmartTransaction tx2)
{
bool rc;
if ((object)tx1 == null || (object)tx2 == null)
{
rc = false;
}
else
{
rc = tx1.GetHash().Equals(tx2.GetHash());
}
return rc;
}
public static bool operator !=(Transaction tx1, SmartTransaction tx2)
{
return !(tx1 == tx2);
}
public static bool operator ==(SmartTransaction tx1, Transaction tx2)
{
bool rc;
if ((object)tx1 == null || (object)tx2 == null)
{
rc = false;
}
else
{
rc = tx1.GetHash().Equals(tx2.GetHash());
}
return rc;
}
public static bool operator !=(SmartTransaction tx1, Transaction tx2)
{
return !(tx1 == tx2);
}
#endregion
}
}
This diff is collapsed.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using HBitcoin.Models;
using NBitcoin;
namespace HBitcoin.FullBlockSpv
{
public class UnprocessedBlockBuffer
{
public const int Capacity = 50;
private readonly ConcurrentObservableDictionary<Height, Block> _blocks = new ConcurrentObservableDictionary<Height, Block>();
public event EventHandler HaveBlocks;
private void OnHaveBlocks() => HaveBlocks?.Invoke(this, EventArgs.Empty);
/// <summary>
///
/// </summary>
/// <param name="height"></param>
/// <param name="block"></param>
/// <returns>false if we have more than UnprocessedBlockBuffer.Capacity blocks in memory already</returns>
public bool TryAddOrReplace(Height height, Block block)
{
if (_blocks.Count > Capacity) return false;
_blocks.AddOrReplace(height, block);
if (_blocks.Count == 1) OnHaveBlocks();
return true;
}
public bool Full => _blocks.Count == Capacity;
public Height BestHeight => _blocks.Count == 0 ? Height.Unknown : _blocks.Keys.Max();
/// <summary>
///
/// </summary>
/// <returns>false if empty</returns>
public bool TryGetAndRemoveOldest(out Height height, out Block block)
{
height = Height.Unknown;
block = default(Block);
if(_blocks.Count == 0) return false;
height = _blocks.Keys.Min();
block = _blocks[height];
_blocks.Remove(height);
return true;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace HBitcoin
{
internal static class Util
{
internal static byte[][] Separate(byte[] source, byte[] separator)
{
var Parts = new List<byte[]>();
var Index = 0;
byte[] Part;
for (var I = 0; I < source.Length; ++I)
{
if (Equals(source, separator, I))
{
Part = new byte[I - Index];
Array.Copy(source, Index, Part, 0, Part.Length);
Parts.Add(Part);
Index = I + separator.Length;
I += separator.Length - 1;
}
}
Part = new byte[source.Length - Index];
Array.Copy(source, Index, Part, 0, Part.Length);
Parts.Add(Part);
return Parts.ToArray();
}
private static bool Equals(byte[] source, byte[] separator, int index)
{
for (int i = 0; i < separator.Length; ++i)
if (index + i >= source.Length || source[index + i] != separator[i])
return false;
return true;
}
/// <summary>
/// Splits an array into several smaller arrays.
/// </summary>
/// <typeparam name="T">The type of the array.</typeparam>
/// <param name="array">The array to split.</param>
/// <param name="size">The size of the smaller arrays.</param>
/// <returns>An array containing smaller arrays.</returns>
public static IEnumerable<IEnumerable<T>> Split<T>(T[] array, int size)
{
for (var i = 0; i < (float)array.Length / size; i++)
{
yield return array.Skip(i * size).Take(size);
}
}
}
}
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