From c522b8de595e31a6e806fbdeead1536fdef868e5 Mon Sep 17 00:00:00 2001 From: dangershony Date: Tue, 17 Mar 2020 14:46:26 +0000 Subject: [PATCH 1/7] initil commit --- src/NBitcoin/UInt256.cs | 440 +++++++++++++++++----------------------- 1 file changed, 187 insertions(+), 253 deletions(-) diff --git a/src/NBitcoin/UInt256.cs b/src/NBitcoin/UInt256.cs index b15db964c..542d01739 100644 --- a/src/NBitcoin/UInt256.cs +++ b/src/NBitcoin/UInt256.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Runtime.InteropServices; using NBitcoin.DataEncoders; namespace NBitcoin @@ -28,6 +29,7 @@ public MutableUint256() { this._Value = Zero; } + public MutableUint256(uint256 value) { this._Value = value; @@ -51,12 +53,14 @@ public void ReadWrite(BitcoinStream stream) } private static readonly uint256 _Zero = new uint256(); + public static uint256 Zero { get { return _Zero; } } private static readonly uint256 _One = new uint256(1); + public static uint256 One { get { return _One; } @@ -68,74 +72,69 @@ public uint256() public uint256(uint256 b) { - this.pn0 = b.pn0; - this.pn1 = b.pn1; - this.pn2 = b.pn2; - this.pn3 = b.pn3; - this.pn4 = b.pn4; - this.pn5 = b.pn5; - this.pn6 = b.pn6; - this.pn7 = b.pn7; + this.part1 = b.part1; + this.part2 = b.part2; + this.part3 = b.part3; + this.part4 = b.part4; } - private const int WIDTH = 256 / 32; - - private uint256(uint[] array) - { - if (array.Length != WIDTH) - throw new ArgumentOutOfRangeException(); - - this.pn0 = array[0]; - this.pn1 = array[1]; - this.pn2 = array[2]; - this.pn3 = array[3]; - this.pn4 = array[4]; - this.pn5 = array[5]; - this.pn6 = array[6]; - this.pn7 = array[7]; - } + private const int EXPECTED_SIZE = 32; - private uint[] ToArray() - { - return new uint[] { this.pn0, this.pn1, this.pn2, this.pn3, this.pn4, this.pn5, this.pn6, this.pn7 }; - } + private const int WIDTH = 256 / 32; - public static uint256 operator <<(uint256 a, int shift) + public uint256(ReadOnlySpan input) { - uint[] source = a.ToArray(); - var target = new uint[source.Length]; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) + if (input.Length != EXPECTED_SIZE) { - if (i + k + 1 < WIDTH && shift != 0) - target[i + k + 1] |= (source[i] >> (32 - shift)); - if (i + k < WIDTH) - target[i + k] |= (target[i] << shift); + throw new FormatException("the byte array should be 32 bytes long"); } - return new uint256(target); - } - public static uint256 operator >>(uint256 a, int shift) - { - uint[] source = a.ToArray(); - var target = new uint[source.Length]; - int k = shift / 32; - shift = shift % 32; - for (int i = 0; i < WIDTH; i++) - { - if (i - k - 1 >= 0 && shift != 0) - target[i - k - 1] |= (source[i] << (32 - shift)); - if (i - k >= 0) - target[i - k] |= (source[i] >> shift); - } - return new uint256(target); - } + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); + input.CopyTo(dst); + } + + //private uint[] ToArray() + //{ + // return new ul[] { this.part1, this.part2, this.part3, this.part4, }; + //} + + //public static uint256 operator <<(uint256 a, int shift) + //{ + // uint[] source = a.ToArray(); + // var target = new uint[source.Length]; + // int k = shift / 32; + // shift = shift % 32; + // for (int i = 0; i < WIDTH; i++) + // { + // if (i + k + 1 < WIDTH && shift != 0) + // target[i + k + 1] |= (source[i] >> (32 - shift)); + // if (i + k < WIDTH) + // target[i + k] |= (target[i] << shift); + // } + // return new uint256(target); + //} + + //public static uint256 operator >>(uint256 a, int shift) + //{ + // uint[] source = a.ToArray(); + // var target = new uint[source.Length]; + // int k = shift / 32; + // shift = shift % 32; + // for (int i = 0; i < WIDTH; i++) + // { + // if (i - k - 1 >= 0 && shift != 0) + // target[i - k - 1] |= (source[i] << (32 - shift)); + // if (i - k >= 0) + // target[i - k] |= (source[i] >> shift); + // } + // return new uint256(target); + //} public static uint256 Parse(string hex) { return new uint256(hex); } + public static bool TryParse(string hex, out uint256 result) { if (hex == null) @@ -153,50 +152,40 @@ public static bool TryParse(string hex, out uint256 result) private static readonly HexEncoder Encoder = new HexEncoder(); private const int WIDTH_BYTE = 256 / 8; - internal readonly UInt32 pn0; - internal readonly UInt32 pn1; - internal readonly UInt32 pn2; - internal readonly UInt32 pn3; - internal readonly UInt32 pn4; - internal readonly UInt32 pn5; - internal readonly UInt32 pn6; - internal readonly UInt32 pn7; +#pragma warning disable IDE0044 // Add readonly modifier + private ulong part1; + private ulong part2; + private ulong part3; + private ulong part4; +#pragma warning restore IDE0044 // Add readonly modifier public byte GetByte(int index) { - int uintIndex = index / sizeof(uint); - int byteIndex = index % sizeof(uint); - UInt32 value; + int uintIndex = index / sizeof(ulong); + int byteIndex = index % sizeof(ulong); + ulong value; switch (uintIndex) { case 0: - value = this.pn0; + value = this.part1; break; + case 1: - value = this.pn1; + value = this.part2; break; + case 2: - value = this.pn2; + value = this.part3; break; + case 3: - value = this.pn3; - break; - case 4: - value = this.pn4; - break; - case 5: - value = this.pn5; - break; - case 6: - value = this.pn6; - break; - case 7: - value = this.pn7; + value = this.part4; break; + default: throw new ArgumentOutOfRangeException("index"); } - return (byte)(value >> (byteIndex * 8)); + return (byte)(value >> (byteIndex * 16)); } public override string ToString() @@ -208,14 +197,10 @@ public override string ToString() public uint256(ulong b) { - this.pn0 = (uint)b; - this.pn1 = (uint)(b >> 32); - this.pn2 = 0; - this.pn3 = 0; - this.pn4 = 0; - this.pn5 = 0; - this.pn6 = 0; - this.pn7 = 0; + this.part1 = (uint)b; + this.part2 = 0; + this.part3 = 0; + this.part4 = 0; } public uint256(byte[] vch, bool lendian = true) @@ -228,61 +213,77 @@ public uint256(byte[] vch, bool lendian = true) if (!lendian) vch = vch.Reverse().ToArray(); - this.pn0 = Utils.ToUInt32(vch, 4 * 0, true); - this.pn1 = Utils.ToUInt32(vch, 4 * 1, true); - this.pn2 = Utils.ToUInt32(vch, 4 * 2, true); - this.pn3 = Utils.ToUInt32(vch, 4 * 3, true); - this.pn4 = Utils.ToUInt32(vch, 4 * 4, true); - this.pn5 = Utils.ToUInt32(vch, 4 * 5, true); - this.pn6 = Utils.ToUInt32(vch, 4 * 6, true); - this.pn7 = Utils.ToUInt32(vch, 4 * 7, true); + var input = new ReadOnlySpan(vch); + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); + input.CopyTo(dst); } - public uint256(ReadOnlySpan bytes) + public uint256(string hexString) { - if (bytes.Length != WIDTH_BYTE) + if (hexString is null) { - throw new FormatException("the byte array should be 32 bytes long"); + throw new ArgumentNullException(nameof(hexString)); } - this.pn0 = Utils.ToUInt32(bytes, 4 * 0, true); - this.pn1 = Utils.ToUInt32(bytes, 4 * 1, true); - this.pn2 = Utils.ToUInt32(bytes, 4 * 2, true); - this.pn3 = Utils.ToUInt32(bytes, 4 * 3, true); - this.pn4 = Utils.ToUInt32(bytes, 4 * 4, true); - this.pn5 = Utils.ToUInt32(bytes, 4 * 5, true); - this.pn6 = Utils.ToUInt32(bytes, 4 * 6, true); - this.pn7 = Utils.ToUInt32(bytes, 4 * 7, true); - } + //account for 0x prefix + if (hexString.Length < EXPECTED_SIZE * 2) + { + throw new FormatException($"the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); + } - public uint256(string str) - { - this.pn0 = 0; - this.pn1 = 0; - this.pn2 = 0; - this.pn3 = 0; - this.pn4 = 0; - this.pn5 = 0; - this.pn6 = 0; - this.pn7 = 0; - str = str.Trim(); + ReadOnlySpan hexAsSpan = (hexString[0] == '0' && hexString[1] == 'X') ? hexString.AsSpan(2) : hexString.AsSpan(); - if (str.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) - str = str.Substring(2); + if (hexString.Length != EXPECTED_SIZE * 2) + { + throw new FormatException($"the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); + } - byte[] bytes = Encoder.DecodeData(str).Reverse().ToArray(); - if (bytes.Length != WIDTH_BYTE) - throw new FormatException("Invalid hex length"); - this.pn0 = Utils.ToUInt32(bytes, 4 * 0, true); - this.pn1 = Utils.ToUInt32(bytes, 4 * 1, true); - this.pn2 = Utils.ToUInt32(bytes, 4 * 2, true); - this.pn3 = Utils.ToUInt32(bytes, 4 * 3, true); - this.pn4 = Utils.ToUInt32(bytes, 4 * 4, true); - this.pn5 = Utils.ToUInt32(bytes, 4 * 5, true); - this.pn6 = Utils.ToUInt32(bytes, 4 * 6, true); - this.pn7 = Utils.ToUInt32(bytes, 4 * 7, true); + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); + + int i = hexString.Length - 1; + int j = 0; + + while (i > 0) + { + char c = hexAsSpan[i--]; + if (c >= '0' && c <= '9') + { + dst[j] = (byte)(c - '0'); + } + else if (c >= 'a' && c <= 'f') + { + dst[j] = (byte)(c - ('a' - 10)); + } + else if (c >= 'A' && c <= 'F') + { + dst[j] = (byte)(c - ('A' - 10)); + } + else + { + throw new ArgumentException("Invalid nibble: " + c); + } + c = hexAsSpan[i--]; + if (c >= '0' && c <= '9') + { + dst[j] |= (byte)((c - '0') << 4); + } + else if (c >= 'a' && c <= 'f') + { + dst[j] |= (byte)((c - ('a' - 10)) << 4); + } + else if (c >= 'A' && c <= 'F') + { + dst[j] |= (byte)((c - ('A' - 10)) << 4); + } + else + { + throw new ArgumentException("Invalid nibble: " + c); + } + + j++; + } } public uint256(byte[] vch) @@ -290,29 +291,30 @@ public uint256(byte[] vch) { } - public override bool Equals(object obj) + public ReadOnlySpan GetBytes() { - var item = obj as uint256; - return this.Equals(item); + return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); } - public bool Equals(uint256 other) + public override int GetHashCode() { - if (other is null) - { - return false; - } + return (int)this.part1; + } - bool equals = true; - equals &= this.pn0 == other.pn0; - equals &= this.pn1 == other.pn1; - equals &= this.pn2 == other.pn2; - equals &= this.pn3 == other.pn3; - equals &= this.pn4 == other.pn4; - equals &= this.pn5 == other.pn5; - equals &= this.pn6 == other.pn6; - equals &= this.pn7 == other.pn7; - return equals; + public override bool Equals(object? obj) => ReferenceEquals(this, obj) ? true : this.Equals(obj as uint256); + + public static bool operator !=(uint256? a, uint256? b) => !(a == b); + + public static bool operator ==(uint256? a, uint256? b) => a == null ? false : a.Equals(b); + + public bool Equals(uint256? other) + { + if (other is null) return false; + + return this.part1 == other.part1 + && this.part2 == other.part2 + && this.part3 == other.part3 + && this.part4 == other.part4; } public int CompareTo(uint256 other) @@ -326,25 +328,6 @@ public int CompareTo(object obj) obj is null ? CompareTo(null as uint256) : throw new ArgumentException($"Object is not an instance of uint256", nameof(obj)); } - public static bool operator ==(uint256 a, uint256 b) - { - if (ReferenceEquals(a, b)) - return true; - if (((object)a == null) || ((object)b == null)) - return false; - - bool equals = true; - equals &= a.pn0 == b.pn0; - equals &= a.pn1 == b.pn1; - equals &= a.pn2 == b.pn2; - equals &= a.pn3 == b.pn3; - equals &= a.pn4 == b.pn4; - equals &= a.pn5 == b.pn5; - equals &= a.pn6 == b.pn6; - equals &= a.pn7 == b.pn7; - return equals; - } - public static bool operator <(uint256 a, uint256 b) { return Comparison(a, b) < 0; @@ -373,46 +356,17 @@ public static int Comparison(uint256 a, uint256 b) return -1; if (!(a is null) && b is null) return 1; - if (a.pn7 < b.pn7) - return -1; - if (a.pn7 > b.pn7) - return 1; - if (a.pn6 < b.pn6) - return -1; - if (a.pn6 > b.pn6) - return 1; - if (a.pn5 < b.pn5) - return -1; - if (a.pn5 > b.pn5) - return 1; - if (a.pn4 < b.pn4) - return -1; - if (a.pn4 > b.pn4) - return 1; - if (a.pn3 < b.pn3) - return -1; - if (a.pn3 > b.pn3) - return 1; - if (a.pn2 < b.pn2) - return -1; - if (a.pn2 > b.pn2) - return 1; - if (a.pn1 < b.pn1) + if (a.part1 < b.part1) return -1; - if (a.pn1 > b.pn1) + if (a.part2 > b.part2) return 1; - if (a.pn0 < b.pn0) + if (a.part3 < b.part3) return -1; - if (a.pn0 > b.pn0) + if (a.part4 > b.part4) return 1; return 0; } - public static bool operator !=(uint256 a, uint256 b) - { - return !(a == b); - } - public static bool operator ==(uint256 a, ulong b) { return (a == new uint256(b)); @@ -428,52 +382,29 @@ public static implicit operator uint256(ulong value) return new uint256(value); } - public byte[] ToBytes(bool lendian = true) { - var arr = new byte[WIDTH_BYTE]; - this.ToBytes(arr); - if (!lendian) - Array.Reverse(arr); - return arr; - } + var span = this.GetBytes(); - public void ToBytes(byte[] output) - { - Buffer.BlockCopy(Utils.ToBytes(this.pn0, true), 0, output, 4 * 0, 4); - Buffer.BlockCopy(Utils.ToBytes(this.pn1, true), 0, output, 4 * 1, 4); - Buffer.BlockCopy(Utils.ToBytes(this.pn2, true), 0, output, 4 * 2, 4); - Buffer.BlockCopy(Utils.ToBytes(this.pn3, true), 0, output, 4 * 3, 4); - Buffer.BlockCopy(Utils.ToBytes(this.pn4, true), 0, output, 4 * 4, 4); - Buffer.BlockCopy(Utils.ToBytes(this.pn5, true), 0, output, 4 * 5, 4); - Buffer.BlockCopy(Utils.ToBytes(this.pn6, true), 0, output, 4 * 6, 4); - Buffer.BlockCopy(Utils.ToBytes(this.pn7, true), 0, output, 4 * 7, 4); + if (lendian) + return span.ToArray(); + + var reverseSpan = new Span(); + span.CopyTo(reverseSpan); + reverseSpan.Reverse(); + return reverseSpan.ToArray(); } public void ToBytes(Span output, bool lendian = true) { - if(output.Length < WIDTH_BYTE) + if (output.Length < WIDTH_BYTE) throw new ArgumentException(message: $"The array should be at least of size {WIDTH_BYTE}", paramName: nameof(output)); - Span initial = output; - Utils.ToBytes(this.pn0, true, output); - output = output.Slice(4); - Utils.ToBytes(this.pn1, true, output); - output = output.Slice(4); - Utils.ToBytes(this.pn2, true, output); - output = output.Slice(4); - Utils.ToBytes(this.pn3, true, output); - output = output.Slice(4); - Utils.ToBytes(this.pn4, true, output); - output = output.Slice(4); - Utils.ToBytes(this.pn5, true, output); - output = output.Slice(4); - Utils.ToBytes(this.pn6, true, output); - output = output.Slice(4); - Utils.ToBytes(this.pn7, true, output); - - if(!lendian) - initial.Reverse(); + var span = this.GetBytes(); + span.CopyTo(output); + + if (!lendian) + output.Reverse(); } public MutableUint256 AsBitcoinSerializable() @@ -496,17 +427,12 @@ public int Size public ulong GetLow64() { - return this.pn0 | (ulong) this.pn1 << 32; + return this.part1; } public uint GetLow32() { - return this.pn0; - } - - public override int GetHashCode() - { - return (int)this.pn0; + return (uint)(this.part1 & uint.MaxValue); } } @@ -515,6 +441,7 @@ public class uint160 : IComparable, IEquatable, IComparable public class MutableUint160 : IBitcoinSerializable { private uint160 _Value; + public uint160 Value { get @@ -526,10 +453,12 @@ public uint160 Value this._Value = value; } } + public MutableUint160() { this._Value = Zero; } + public MutableUint160(uint160 value) { this._Value = value; @@ -552,12 +481,14 @@ public void ReadWrite(BitcoinStream stream) } private static readonly uint160 _Zero = new uint160(); + public static uint160 Zero { get { return _Zero; } } private static readonly uint160 _One = new uint160(1); + public static uint160 One { get { return _One; } @@ -580,6 +511,7 @@ public static uint160 Parse(string hex) { return new uint160(hex); } + public static bool TryParse(string hex, out uint160 result) { if (hex == null) @@ -613,18 +545,23 @@ public byte GetByte(int index) case 0: value = this.pn0; break; + case 1: value = this.pn1; break; + case 2: value = this.pn2; break; + case 3: value = this.pn3; break; + case 4: value = this.pn4; break; + default: throw new ArgumentOutOfRangeException("index"); } @@ -660,7 +597,6 @@ public uint160(byte[] vch, bool lendian = true) this.pn2 = Utils.ToUInt32(vch, 4 * 2, true); this.pn3 = Utils.ToUInt32(vch, 4 * 3, true); this.pn4 = Utils.ToUInt32(vch, 4 * 4, true); - } public uint160(string str) @@ -683,7 +619,6 @@ public uint160(string str) this.pn2 = Utils.ToUInt32(bytes, 4 * 2, true); this.pn3 = Utils.ToUInt32(bytes, 4 * 3, true); this.pn4 = Utils.ToUInt32(bytes, 4 * 4, true); - } public uint160(byte[] vch) @@ -808,7 +743,6 @@ public static implicit operator uint160(ulong value) return new uint160(value); } - public byte[] ToBytes(bool lendian = true) { var arr = new byte[WIDTH_BYTE]; @@ -842,7 +776,7 @@ public int Size public ulong GetLow64() { - return this.pn0 | (ulong) this.pn1 << 32; + return this.pn0 | (ulong)this.pn1 << 32; } public uint GetLow32() From 4d64392b97db87665ee57e064e45b19e6f103e33 Mon Sep 17 00:00:00 2001 From: dangershony Date: Thu, 19 Mar 2020 18:11:29 +0000 Subject: [PATCH 2/7] Optimize constructors and little endian --- .../TxMemPoolEntry.cs | 8 +- .../TxMemPoolModifiedEntry.cs | 4 +- src/NBitcoin/Crypto/Hashes.cs | 57 ++- src/NBitcoin/UInt256.cs | 406 ++++++++++-------- 4 files changed, 276 insertions(+), 199 deletions(-) diff --git a/src/Blockcore.Features.MemoryPool/TxMemPoolEntry.cs b/src/Blockcore.Features.MemoryPool/TxMemPoolEntry.cs index 5cdc97371..cf432d07c 100644 --- a/src/Blockcore.Features.MemoryPool/TxMemPoolEntry.cs +++ b/src/Blockcore.Features.MemoryPool/TxMemPoolEntry.cs @@ -43,7 +43,7 @@ public interface ITxMempoolFees /// /// A transaction entry in the memory pool. /// - public class TxMempoolEntry:IComparable, ITxMempoolFees + public class TxMempoolEntry : IComparable, ITxMempoolFees { /// Index in memory pools vTxHashes. public volatile uint vTxHashesIdx; @@ -304,7 +304,7 @@ public override string ToString() /// Result of comparison function. public int CompareTo(object other) { - return uint256.Comparison(this.TransactionHash, (other as TxMempoolEntry).TransactionHash); + return uint256.CompareTypes(this.TransactionHash, (other as TxMempoolEntry).TransactionHash); } /// @@ -314,7 +314,7 @@ public int CompareTo(object other) /// The first object to compare. /// The second object to compare. /// Returns -1 if a less than b, 0 if a equals b, and 1 if a greater than b. - public static int CompareFees(T a, T b) where T:IComparable,ITxMempoolFees + public static int CompareFees(T a, T b) where T : IComparable, ITxMempoolFees { // Avoid division by rewriting (a/b > c/d) as (a*d > c*b). Money f1 = a.ModFeesWithAncestors * b.SizeWithAncestors; @@ -326,4 +326,4 @@ public static int CompareFees(T a, T b) where T:IComparable,ITxMempoolFees return (f1 < f2) ? 1 : -1; } } -} +} \ No newline at end of file diff --git a/src/Blockcore.Features.Miner/TxMemPoolModifiedEntry.cs b/src/Blockcore.Features.Miner/TxMemPoolModifiedEntry.cs index 4fe25e8e1..714deff73 100644 --- a/src/Blockcore.Features.Miner/TxMemPoolModifiedEntry.cs +++ b/src/Blockcore.Features.Miner/TxMemPoolModifiedEntry.cs @@ -7,7 +7,7 @@ namespace Blockcore.Features.Miner /// /// Container for tracking updates to ancestor feerate as we include (parent) transactions in a block. /// - public sealed class TxMemPoolModifiedEntry:IComparable, ITxMempoolFees + public sealed class TxMemPoolModifiedEntry : IComparable, ITxMempoolFees { public readonly TxMempoolEntry MempoolEntry; @@ -35,7 +35,7 @@ public TxMemPoolModifiedEntry(TxMempoolEntry entry) /// Result of comparison function. public int CompareTo(object other) { - return uint256.Comparison(this.MempoolEntry.TransactionHash, (other as TxMemPoolModifiedEntry).MempoolEntry.TransactionHash); + return uint256.CompareTypes(this.MempoolEntry.TransactionHash, (other as TxMemPoolModifiedEntry).MempoolEntry.TransactionHash); } } } \ No newline at end of file diff --git a/src/NBitcoin/Crypto/Hashes.cs b/src/NBitcoin/Crypto/Hashes.cs index 8450c18ff..b693393d4 100644 --- a/src/NBitcoin/Crypto/Hashes.cs +++ b/src/NBitcoin/Crypto/Hashes.cs @@ -9,6 +9,7 @@ namespace NBitcoin.Crypto public static class Hashes { #region Hash256 + public static uint256 Hash256(byte[] data) { return Hash256(data, 0, data.Length); @@ -19,6 +20,17 @@ public static uint256 Hash256(byte[] data, int count) return Hash256(data, 0, count); } + public static ReadOnlySpan Hash256(ReadOnlySpan data) + { + using (var sha = new SHA256Managed()) + { + Span result = stackalloc byte[32]; + sha.TryComputeHash(data, result, out _); + sha.TryComputeHash(result, result, out _); + return result.ToArray(); + } + } + public static uint256 Hash256(byte[] data, int offset, int count) { #if NETCORE @@ -37,9 +49,11 @@ public static uint256 Hash256(byte[] data, int offset, int count) } #endif } - #endregion + + #endregion Hash256 #region Hash160 + public static uint160 Hash160(byte[] data) { return Hash160(data, 0, data.Length); @@ -54,9 +68,11 @@ public static uint160 Hash160(byte[] data, int offset, int count) { return new uint160(RIPEMD160(SHA256(data, offset, count))); } - #endregion + + #endregion Hash160 #region RIPEMD160 + private static byte[] RIPEMD160(byte[] data) { return RIPEMD160(data, 0, data.Length); @@ -83,7 +99,7 @@ public static byte[] RIPEMD160(byte[] data, int offset, int count) #endif } - #endregion + #endregion RIPEMD160 public class SipHasher { @@ -93,6 +109,7 @@ public class SipHasher private ulong v_3; private ulong count; private ulong tmp; + public SipHasher(ulong k0, ulong k1) { this.v_0 = 0x736f6d6570736575UL ^ k0; @@ -128,11 +145,11 @@ public SipHasher Write(byte[] data) ulong c = this.count; int offset = 0; - while(size-- != 0) + while (size-- != 0) { t |= ((ulong)((data[offset++]))) << (int)(8 * (c % 8)); c++; - if((c & 7) == 0) + if ((c & 7) == 0) { v3 ^= t; SIPROUND(ref v0, ref v1, ref v2, ref v3); @@ -156,7 +173,7 @@ public ulong Finalize() { ulong v0 = this.v_0, v1 = this.v_1, v2 = this.v_2, v3 = this.v_3; - ulong t = this.tmp | (((ulong) this.count) << 56); + ulong t = this.tmp | (((ulong)this.count) << 56); v3 ^= t; SIPROUND(ref v0, ref v1, ref v2, ref v3); @@ -212,16 +229,20 @@ public static ulong SipHashUint256(ulong k0, ulong k1, uint256 val) public static ulong GetULong(uint256 val, int position) { - switch(position) + switch (position) { case 0: - return (ulong)val.pn0 + (ulong)((ulong)val.pn1 << 32); + return val.Part1; + case 1: - return (ulong)val.pn2 + (ulong)((ulong)val.pn3 << 32); + return val.Part2; + case 2: - return (ulong)val.pn4 + (ulong)((ulong)val.pn5 << 32); + return val.Part3; + case 3: - return (ulong)val.pn6 + (ulong)((ulong)val.pn7 << 32); + return val.Part4; + default: throw new ArgumentOutOfRangeException("position should be less than 4", "position"); } @@ -281,11 +302,11 @@ public static byte[] SHA256(byte[] data, int offset, int count) #endif } - private static uint rotl32(uint x, byte r) { return (x << r) | (x >> (32 - r)); } + private static ulong rotl64(ulong x, byte b) { return (((x) << (b)) | ((x) >> (64 - (b)))); @@ -300,6 +321,7 @@ private static uint fmix(uint h) h ^= h >> 16; return h; } + public static uint MurmurHash3(uint nHashSeed, byte[] vDataToHash) { // The following is MurmurHash3 (x86_32), see https://gist.github.com/automatonic/3725443 @@ -310,13 +332,13 @@ public static uint MurmurHash3(uint nHashSeed, byte[] vDataToHash) uint k1 = 0; uint streamLength = 0; - using(var reader = new BinaryReader(new MemoryStream(vDataToHash))) + using (var reader = new BinaryReader(new MemoryStream(vDataToHash))) { byte[] chunk = reader.ReadBytes(4); - while(chunk.Length > 0) + while (chunk.Length > 0) { streamLength += (uint)chunk.Length; - switch(chunk.Length) + switch (chunk.Length) { case 4: /* Get four bytes from the input into an uint */ @@ -335,6 +357,7 @@ public static uint MurmurHash3(uint nHashSeed, byte[] vDataToHash) h1 = rotl32(h1, 13); h1 = h1 * 5 + 0xe6546b64; break; + case 3: k1 = (uint) (chunk[0] @@ -345,6 +368,7 @@ public static uint MurmurHash3(uint nHashSeed, byte[] vDataToHash) k1 *= c2; h1 ^= k1; break; + case 2: k1 = (uint) (chunk[0] @@ -354,6 +378,7 @@ public static uint MurmurHash3(uint nHashSeed, byte[] vDataToHash) k1 *= c2; h1 ^= k1; break; + case 1: k1 = (uint)(chunk[0]); k1 *= c1; @@ -394,4 +419,4 @@ public static byte[] BIP32Hash(byte[] chainCode, uint nChild, byte header, byte[ .Concat(num).ToArray()); } } -} +} \ No newline at end of file diff --git a/src/NBitcoin/UInt256.cs b/src/NBitcoin/UInt256.cs index 542d01739..a0b05a0b9 100644 --- a/src/NBitcoin/UInt256.cs +++ b/src/NBitcoin/UInt256.cs @@ -1,25 +1,31 @@ using System; using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using NBitcoin.DataEncoders; namespace NBitcoin { + /// + /// An implementation of uint256 based on https://github.com/MithrilMan/MithrilShards + /// Link to type https://github.com/MithrilMan/MithrilShards/blob/master/src/MithrilShards.Core/DataTypes/Uint256.cs + /// Big credit to @MithrilMan for making this optimization + /// public class uint256 : IEquatable, IComparable, IComparable { public class MutableUint256 : IBitcoinSerializable { - private uint256 _Value; + private uint256 value; public uint256 Value { get { - return this._Value; + return this.value; } set { - this._Value = value; + this.value = value; } } @@ -27,44 +33,51 @@ public uint256 Value public MutableUint256() { - this._Value = Zero; + this.value = Zero; } public MutableUint256(uint256 value) { - this._Value = value; + this.value = value; } public void ReadWrite(BitcoinStream stream) { if (stream.Serializing) { - Span b = stackalloc byte[WIDTH_BYTE]; - this.Value.ToBytes(b); + Span b = this.Value.ToSpan(); stream.ReadWrite(ref b); } else { Span b = stackalloc byte[WIDTH_BYTE]; stream.ReadWrite(ref b); - this._Value = new uint256(b); + this.value = new uint256(b); } } } - private static readonly uint256 _Zero = new uint256(); + private const int EXPECTED_SIZE = 32; - public static uint256 Zero - { - get { return _Zero; } - } + private const int WIDTH = 256 / 32; - private static readonly uint256 _One = new uint256(1); + private const int WIDTH_BYTE = 256 / 8; - public static uint256 One - { - get { return _One; } - } +#pragma warning disable IDE0044 // Add readonly modifier + private ulong part1; + private ulong part2; + private ulong part3; + private ulong part4; +#pragma warning restore IDE0044 // Add readonly modifier + + internal ulong Part1 => this.part1; + internal ulong Part2 => this.part2; + internal ulong Part3 => this.part3; + internal ulong Part4 => this.part4; + + public static uint256 Zero { get; } = new uint256(); + + public static uint256 One { get; } = new uint256(1); public uint256() { @@ -78,10 +91,6 @@ public uint256(uint256 b) this.part4 = b.part4; } - private const int EXPECTED_SIZE = 32; - - private const int WIDTH = 256 / 32; - public uint256(ReadOnlySpan input) { if (input.Length != EXPECTED_SIZE) @@ -90,135 +99,41 @@ public uint256(ReadOnlySpan input) } Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); - input.CopyTo(dst); - } - - //private uint[] ToArray() - //{ - // return new ul[] { this.part1, this.part2, this.part3, this.part4, }; - //} - - //public static uint256 operator <<(uint256 a, int shift) - //{ - // uint[] source = a.ToArray(); - // var target = new uint[source.Length]; - // int k = shift / 32; - // shift = shift % 32; - // for (int i = 0; i < WIDTH; i++) - // { - // if (i + k + 1 < WIDTH && shift != 0) - // target[i + k + 1] |= (source[i] >> (32 - shift)); - // if (i + k < WIDTH) - // target[i + k] |= (target[i] << shift); - // } - // return new uint256(target); - //} - - //public static uint256 operator >>(uint256 a, int shift) - //{ - // uint[] source = a.ToArray(); - // var target = new uint[source.Length]; - // int k = shift / 32; - // shift = shift % 32; - // for (int i = 0; i < WIDTH; i++) - // { - // if (i - k - 1 >= 0 && shift != 0) - // target[i - k - 1] |= (source[i] << (32 - shift)); - // if (i - k >= 0) - // target[i - k] |= (source[i] >> shift); - // } - // return new uint256(target); - //} - - public static uint256 Parse(string hex) - { - return new uint256(hex); - } - - public static bool TryParse(string hex, out uint256 result) - { - if (hex == null) - throw new ArgumentNullException("hex"); - if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) - hex = hex.Substring(2); - result = null; - if (hex.Length != WIDTH_BYTE * 2) - return false; - if (!((HexEncoder)Encoders.Hex).IsValid(hex)) - return false; - result = new uint256(hex); - return true; - } - - private static readonly HexEncoder Encoder = new HexEncoder(); - private const int WIDTH_BYTE = 256 / 8; -#pragma warning disable IDE0044 // Add readonly modifier - private ulong part1; - private ulong part2; - private ulong part3; - private ulong part4; -#pragma warning restore IDE0044 // Add readonly modifier - - public byte GetByte(int index) - { - int uintIndex = index / sizeof(ulong); - int byteIndex = index % sizeof(ulong); - ulong value; - switch (uintIndex) - { - case 0: - value = this.part1; - break; - - case 1: - value = this.part2; - break; - case 2: - value = this.part3; - break; - - case 3: - value = this.part4; - break; - - default: - throw new ArgumentOutOfRangeException("index"); - } - return (byte)(value >> (byteIndex * 16)); - } - - public override string ToString() - { - var bytes = ToBytes(); - Array.Reverse(bytes); - return Encoder.EncodeData(bytes); + input.CopyTo(dst); } public uint256(ulong b) { - this.part1 = (uint)b; + this.part1 = b; this.part2 = 0; this.part3 = 0; this.part4 = 0; } - public uint256(byte[] vch, bool lendian = true) + public uint256(byte[] payload, bool littleEndian = true) { - if (vch.Length != WIDTH_BYTE) + if (payload.Length != WIDTH_BYTE) { throw new FormatException("the byte array should be 256 byte long"); } - if (!lendian) - vch = vch.Reverse().ToArray(); + var input = new Span(payload); - var input = new ReadOnlySpan(vch); + if (!littleEndian) + { + input.Reverse(); + } Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); + input.CopyTo(dst); } + public uint256(byte[] payload) : this(payload, true) + { + } + public uint256(string hexString) { if (hexString is null) @@ -232,16 +147,16 @@ public uint256(string hexString) throw new FormatException($"the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); } - ReadOnlySpan hexAsSpan = (hexString[0] == '0' && hexString[1] == 'X') ? hexString.AsSpan(2) : hexString.AsSpan(); + ReadOnlySpan hexAsSpan = (hexString[0] == '0' && (hexString[1] == 'x' || hexString[1] == 'X')) ? hexString.AsSpan(2) : hexString.AsSpan(); - if (hexString.Length != EXPECTED_SIZE * 2) + if (hexAsSpan.Length != EXPECTED_SIZE * 2) { throw new FormatException($"the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); } Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); - int i = hexString.Length - 1; + int i = hexAsSpan.Length - 1; int j = 0; while (i > 0) @@ -286,30 +201,159 @@ public uint256(string hexString) } } - public uint256(byte[] vch) - : this(vch, true) + public static uint256 Parse(string hexString) { + return new uint256(hexString); } - public ReadOnlySpan GetBytes() + //private uint[] ToArray() + //{ + // return new ul[] { this.part1, this.part2, this.part3, this.part4, }; + //} + + public static uint256 operator <<(uint256 a, int shift) + { + throw new NotImplementedException(); + // uint[] source = a.ToArray(); + // var target = new uint[source.Length]; + // int k = shift / 32; + // shift = shift % 32; + // for (int i = 0; i < WIDTH; i++) + // { + // if (i + k + 1 < WIDTH && shift != 0) + // target[i + k + 1] |= (source[i] >> (32 - shift)); + // if (i + k < WIDTH) + // target[i + k] |= (target[i] << shift); + // } + // return new uint256(target); + } + + public static uint256 operator >>(uint256 a, int shift) + { + throw new NotImplementedException(); + // uint[] source = a.ToArray(); + // var target = new uint[source.Length]; + // int k = shift / 32; + // shift = shift % 32; + // for (int i = 0; i < WIDTH; i++) + // { + // if (i - k - 1 >= 0 && shift != 0) + // target[i - k - 1] |= (source[i] << (32 - shift)); + // if (i - k >= 0) + // target[i - k] |= (source[i] >> shift); + // } + // return new uint256(target); + } + + public static bool TryParse(string hexString, out uint256? result) + { + try + { + result = new uint256(hexString); + return true; + } + catch (Exception) + { + result = null; + } + + return false; + } + + public byte GetByte(int index) + { + int uintIndex = index / sizeof(ulong); + int byteIndex = index % sizeof(ulong); + ulong value; + + switch (uintIndex) + { + case 0: + value = this.part1; + break; + + case 1: + value = this.part2; + break; + + case 2: + value = this.part3; + break; + + case 3: + value = this.part4; + break; + + default: + throw new ArgumentOutOfRangeException("index"); + } + + return (byte)(value >> (byteIndex * 8)); + } + + public ReadOnlySpan ToReadOnlySpan() { return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); } + public Span ToSpan() + { + return MemoryMarshal.CreateSpan(ref Unsafe.As(ref this.part1), EXPECTED_SIZE); + } + + public byte[] ToBytes(bool littleEndian = true) + { + var span = this.ToSpan(); + + if (!littleEndian) + { + span.Reverse(); + } + + return span.ToArray(); + } + + //public void ToBytes(Span output, bool lendian = true) + //{ + // if (output.Length < WIDTH_BYTE) + // throw new ArgumentException(message: $"The array should be at least of size {WIDTH_BYTE}", paramName: nameof(output)); + + // output = this.GetWritableBytes(); + + // if (!lendian) + // output.Reverse(); + //} + public override int GetHashCode() { return (int)this.part1; } - public override bool Equals(object? obj) => ReferenceEquals(this, obj) ? true : this.Equals(obj as uint256); + public override bool Equals(object? obj) + { + return this.Equals(obj as uint256); + } - public static bool operator !=(uint256? a, uint256? b) => !(a == b); + public static bool operator !=(uint256? a, uint256? b) + { + return !(a == b); + } - public static bool operator ==(uint256? a, uint256? b) => a == null ? false : a.Equals(b); + public static bool operator ==(uint256? a, uint256? b) + { + if (ReferenceEquals(a, b)) + return true; + + if (((object)a == null) || ((object)b == null)) + return false; + + return a.Equals(b); + } public bool Equals(uint256? other) { - if (other is null) return false; + if (other is null) + return false; return this.part1 == other.part1 && this.part2 == other.part2 @@ -319,51 +363,64 @@ public bool Equals(uint256? other) public int CompareTo(uint256 other) { - return Comparison(this, other); + return CompareTypes(this, other); } public int CompareTo(object obj) { - return obj is uint256 v ? CompareTo(v) : - obj is null ? CompareTo(null as uint256) : throw new ArgumentException($"Object is not an instance of uint256", nameof(obj)); + switch (obj) + { + case uint256 target: + return this.CompareTo(target); + + case null: + return this.CompareTo(null as uint256); + + default: + throw new ArgumentException($"Object is not an instance of uint256", nameof(obj)); + } } public static bool operator <(uint256 a, uint256 b) { - return Comparison(a, b) < 0; + return CompareTypes(a, b) < 0; } public static bool operator >(uint256 a, uint256 b) { - return Comparison(a, b) > 0; + return CompareTypes(a, b) > 0; } public static bool operator <=(uint256 a, uint256 b) { - return Comparison(a, b) <= 0; + return CompareTypes(a, b) <= 0; } public static bool operator >=(uint256 a, uint256 b) { - return Comparison(a, b) >= 0; + return CompareTypes(a, b) >= 0; } - public static int Comparison(uint256 a, uint256 b) + public static int CompareTypes(uint256 a, uint256 b) { if (a is null && b is null) return 0; + if (a is null && !(b is null)) return -1; + if (!(a is null) && b is null) return 1; - if (a.part1 < b.part1) - return -1; - if (a.part2 > b.part2) - return 1; - if (a.part3 < b.part3) - return -1; - if (a.part4 > b.part4) - return 1; + + if (a.part4 < b.part4) return -1; + if (a.part4 > b.part4) return 1; + if (a.part3 < b.part3) return -1; + if (a.part3 > b.part3) return 1; + if (a.part2 < b.part2) return -1; + if (a.part2 > b.part2) return 1; + if (a.part1 < b.part1) return -1; + if (a.part1 > b.part1) return 1; + return 0; } @@ -382,34 +439,29 @@ public static implicit operator uint256(ulong value) return new uint256(value); } - public byte[] ToBytes(bool lendian = true) + public MutableUint256 AsBitcoinSerializable() { - var span = this.GetBytes(); - - if (lendian) - return span.ToArray(); - - var reverseSpan = new Span(); - span.CopyTo(reverseSpan); - reverseSpan.Reverse(); - return reverseSpan.ToArray(); + return new MutableUint256(this); } - public void ToBytes(Span output, bool lendian = true) + public override string ToString() { - if (output.Length < WIDTH_BYTE) - throw new ArgumentException(message: $"The array should be at least of size {WIDTH_BYTE}", paramName: nameof(output)); + return string.Create(EXPECTED_SIZE * 2, this, (dst, src) => + { + ReadOnlySpan rawData = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref src.part1, EXPECTED_SIZE / sizeof(ulong))); - var span = this.GetBytes(); - span.CopyTo(output); + const string HexValues = "0123456789abcdef"; - if (!lendian) - output.Reverse(); - } + int i = rawData.Length - 1; + int j = 0; - public MutableUint256 AsBitcoinSerializable() - { - return new MutableUint256(this); + while (i >= 0) + { + byte b = rawData[i--]; + dst[j++] = HexValues[b >> 4]; + dst[j++] = HexValues[b & 0xF]; + } + }); } public int GetSerializeSize() From 35bc980a8488372fe66cf2176bd515b9123b4e70 Mon Sep 17 00:00:00 2001 From: dangershony Date: Thu, 26 Mar 2020 23:17:32 +0000 Subject: [PATCH 3/7] Fix tests --- src/NBitcoin/UInt256.cs | 82 +++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 36 deletions(-) diff --git a/src/NBitcoin/UInt256.cs b/src/NBitcoin/UInt256.cs index a0b05a0b9..ade9ddf18 100644 --- a/src/NBitcoin/UInt256.cs +++ b/src/NBitcoin/UInt256.cs @@ -59,7 +59,7 @@ public void ReadWrite(BitcoinStream stream) private const int EXPECTED_SIZE = 32; - private const int WIDTH = 256 / 32; + private const int WIDTH = 256 / 64; private const int WIDTH_BYTE = 256 / 8; @@ -144,14 +144,14 @@ public uint256(string hexString) //account for 0x prefix if (hexString.Length < EXPECTED_SIZE * 2) { - throw new FormatException($"the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); + throw new FormatException($"Invalid Hex String, the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); } - ReadOnlySpan hexAsSpan = (hexString[0] == '0' && (hexString[1] == 'x' || hexString[1] == 'X')) ? hexString.AsSpan(2) : hexString.AsSpan(); + ReadOnlySpan hexAsSpan = (hexString[0] == '0' && (hexString[1] == 'x' || hexString[1] == 'X')) ? hexString.Trim().AsSpan(2) : hexString.Trim().AsSpan(); if (hexAsSpan.Length != EXPECTED_SIZE * 2) { - throw new FormatException($"the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); + throw new FormatException($"Invalid Hex String, the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); } Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); @@ -206,43 +206,52 @@ public static uint256 Parse(string hexString) return new uint256(hexString); } - //private uint[] ToArray() - //{ - // return new ul[] { this.part1, this.part2, this.part3, this.part4, }; - //} + private uint256(ulong[] array) + { + if (array.Length != WIDTH) + throw new ArgumentOutOfRangeException(); + + this.part1 = array[0]; + this.part2 = array[1]; + this.part3 = array[2]; + this.part4 = array[3]; + } + + private ulong[] ToArray() + { + return new ulong[] { this.part1, this.part2, this.part3, this.part4, }; + } public static uint256 operator <<(uint256 a, int shift) { - throw new NotImplementedException(); - // uint[] source = a.ToArray(); - // var target = new uint[source.Length]; - // int k = shift / 32; - // shift = shift % 32; - // for (int i = 0; i < WIDTH; i++) - // { - // if (i + k + 1 < WIDTH && shift != 0) - // target[i + k + 1] |= (source[i] >> (32 - shift)); - // if (i + k < WIDTH) - // target[i + k] |= (target[i] << shift); - // } - // return new uint256(target); + ulong[] source = a.ToArray(); + var target = new ulong[source.Length]; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i + k + 1 < WIDTH && shift != 0) + target[i + k + 1] |= (source[i] >> (32 - shift)); + if (i + k < WIDTH) + target[i + k] |= (target[i] << shift); + } + return new uint256(target); } public static uint256 operator >>(uint256 a, int shift) { - throw new NotImplementedException(); - // uint[] source = a.ToArray(); - // var target = new uint[source.Length]; - // int k = shift / 32; - // shift = shift % 32; - // for (int i = 0; i < WIDTH; i++) - // { - // if (i - k - 1 >= 0 && shift != 0) - // target[i - k - 1] |= (source[i] << (32 - shift)); - // if (i - k >= 0) - // target[i - k] |= (source[i] >> shift); - // } - // return new uint256(target); + ulong[] source = a.ToArray(); + var target = new ulong[source.Length]; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i - k - 1 >= 0 && shift != 0) + target[i - k - 1] |= (source[i] << (32 - shift)); + if (i - k >= 0) + target[i - k] |= (source[i] >> shift); + } + return new uint256(target); } public static bool TryParse(string hexString, out uint256? result) @@ -303,14 +312,15 @@ public Span ToSpan() public byte[] ToBytes(bool littleEndian = true) { - var span = this.ToSpan(); + var output = this.ToSpan().ToArray(); if (!littleEndian) { + var span = output.AsSpan(); span.Reverse(); } - return span.ToArray(); + return output; } //public void ToBytes(Span output, bool lendian = true) From 9772ec50fd99168ca44c9abad8f2572115290b83 Mon Sep 17 00:00:00 2001 From: dangershony Date: Fri, 27 Mar 2020 23:42:48 +0000 Subject: [PATCH 4/7] Style fixes --- src/NBitcoin/UInt256.cs | 90 ++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 55 deletions(-) diff --git a/src/NBitcoin/UInt256.cs b/src/NBitcoin/UInt256.cs index ade9ddf18..bd3a34de3 100644 --- a/src/NBitcoin/UInt256.cs +++ b/src/NBitcoin/UInt256.cs @@ -50,18 +50,18 @@ public void ReadWrite(BitcoinStream stream) } else { - Span b = stackalloc byte[WIDTH_BYTE]; + Span b = stackalloc byte[WidthByte]; stream.ReadWrite(ref b); this.value = new uint256(b); } } } - private const int EXPECTED_SIZE = 32; + private const int ExpectedSize = 32; - private const int WIDTH = 256 / 64; + private const int Width = 256 / 64; - private const int WIDTH_BYTE = 256 / 8; + private const int WidthByte = 256 / 8; #pragma warning disable IDE0044 // Add readonly modifier private ulong part1; @@ -93,12 +93,12 @@ public uint256(uint256 b) public uint256(ReadOnlySpan input) { - if (input.Length != EXPECTED_SIZE) + if (input.Length != ExpectedSize) { throw new FormatException("the byte array should be 32 bytes long"); } - Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, ExpectedSize / sizeof(ulong))); input.CopyTo(dst); } @@ -113,7 +113,7 @@ public uint256(ulong b) public uint256(byte[] payload, bool littleEndian = true) { - if (payload.Length != WIDTH_BYTE) + if (payload.Length != WidthByte) { throw new FormatException("the byte array should be 256 byte long"); } @@ -125,7 +125,7 @@ public uint256(byte[] payload, bool littleEndian = true) input.Reverse(); } - Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, ExpectedSize / sizeof(ulong))); input.CopyTo(dst); } @@ -142,19 +142,19 @@ public uint256(string hexString) } //account for 0x prefix - if (hexString.Length < EXPECTED_SIZE * 2) + if (hexString.Length < ExpectedSize * 2) { - throw new FormatException($"Invalid Hex String, the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); + throw new FormatException($"Invalid Hex String, the hex string should be {ExpectedSize * 2} chars long or {(ExpectedSize * 2) + 4} if prefixed with 0x."); } ReadOnlySpan hexAsSpan = (hexString[0] == '0' && (hexString[1] == 'x' || hexString[1] == 'X')) ? hexString.Trim().AsSpan(2) : hexString.Trim().AsSpan(); - if (hexAsSpan.Length != EXPECTED_SIZE * 2) + if (hexAsSpan.Length != ExpectedSize * 2) { - throw new FormatException($"Invalid Hex String, the hex string should be {EXPECTED_SIZE * 2} chars long or {(EXPECTED_SIZE * 2) + 4} if prefixed with 0x."); + throw new FormatException($"Invalid Hex String, the hex string should be {ExpectedSize * 2} chars long or {(ExpectedSize * 2) + 4} if prefixed with 0x."); } - Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, ExpectedSize / sizeof(ulong))); int i = hexAsSpan.Length - 1; int j = 0; @@ -208,8 +208,10 @@ public static uint256 Parse(string hexString) private uint256(ulong[] array) { - if (array.Length != WIDTH) + if (array.Length != Width) + { throw new ArgumentOutOfRangeException(); + } this.part1 = array[0]; this.part2 = array[1]; @@ -228,11 +230,11 @@ private ulong[] ToArray() var target = new ulong[source.Length]; int k = shift / 32; shift = shift % 32; - for (int i = 0; i < WIDTH; i++) + for (int i = 0; i < Width; i++) { - if (i + k + 1 < WIDTH && shift != 0) + if (i + k + 1 < Width && shift != 0) target[i + k + 1] |= (source[i] >> (32 - shift)); - if (i + k < WIDTH) + if (i + k < Width) target[i + k] |= (target[i] << shift); } return new uint256(target); @@ -244,7 +246,7 @@ private ulong[] ToArray() var target = new ulong[source.Length]; int k = shift / 32; shift = shift % 32; - for (int i = 0; i < WIDTH; i++) + for (int i = 0; i < Width; i++) { if (i - k - 1 >= 0 && shift != 0) target[i - k - 1] |= (source[i] << (32 - shift)); @@ -302,12 +304,12 @@ public byte GetByte(int index) public ReadOnlySpan ToReadOnlySpan() { - return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref this.part1, EXPECTED_SIZE / sizeof(ulong))); + return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref this.part1, ExpectedSize / sizeof(ulong))); } public Span ToSpan() { - return MemoryMarshal.CreateSpan(ref Unsafe.As(ref this.part1), EXPECTED_SIZE); + return MemoryMarshal.CreateSpan(ref Unsafe.As(ref this.part1), ExpectedSize); } public byte[] ToBytes(bool littleEndian = true) @@ -456,9 +458,9 @@ public MutableUint256 AsBitcoinSerializable() public override string ToString() { - return string.Create(EXPECTED_SIZE * 2, this, (dst, src) => + return string.Create(ExpectedSize * 2, this, (dst, src) => { - ReadOnlySpan rawData = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref src.part1, EXPECTED_SIZE / sizeof(ulong))); + ReadOnlySpan rawData = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref src.part1, ExpectedSize / sizeof(ulong))); const string HexValues = "0123456789abcdef"; @@ -474,19 +476,6 @@ public override string ToString() }); } - public int GetSerializeSize() - { - return WIDTH_BYTE; - } - - public int Size - { - get - { - return WIDTH_BYTE; - } - } - public ulong GetLow64() { return this.part1; @@ -535,26 +524,16 @@ public void ReadWrite(BitcoinStream stream) } else { - var b = new byte[WIDTH_BYTE]; + var b = new byte[WidthByte]; stream.ReadWrite(ref b); this._Value = new uint160(b); } } } - private static readonly uint160 _Zero = new uint160(); - - public static uint160 Zero - { - get { return _Zero; } - } - - private static readonly uint160 _One = new uint160(1); + public static uint160 Zero { get; } = new uint160(); - public static uint160 One - { - get { return _One; } - } + public static uint160 One { get; } = new uint160(1); public uint160() { @@ -581,7 +560,7 @@ public static bool TryParse(string hex, out uint160 result) if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) hex = hex.Substring(2); result = null; - if (hex.Length != WIDTH_BYTE * 2) + if (hex.Length != WidthByte * 2) return false; if (!((HexEncoder)Encoders.Hex).IsValid(hex)) return false; @@ -590,7 +569,8 @@ public static bool TryParse(string hex, out uint160 result) } private static readonly HexEncoder Encoder = new HexEncoder(); - private const int WIDTH_BYTE = 160 / 8; + private const int WidthByte = 160 / 8; + internal readonly UInt32 pn0; internal readonly UInt32 pn1; internal readonly UInt32 pn2; @@ -646,7 +626,7 @@ public uint160(ulong b) public uint160(byte[] vch, bool lendian = true) { - if (vch.Length != WIDTH_BYTE) + if (vch.Length != WidthByte) { throw new FormatException("the byte array should be 160 byte long"); } @@ -674,7 +654,7 @@ public uint160(string str) str = str.Substring(2); byte[] bytes = Encoder.DecodeData(str).Reverse().ToArray(); - if (bytes.Length != WIDTH_BYTE) + if (bytes.Length != WidthByte) throw new FormatException("Invalid hex length"); this.pn0 = Utils.ToUInt32(bytes, 4 * 0, true); this.pn1 = Utils.ToUInt32(bytes, 4 * 1, true); @@ -807,7 +787,7 @@ public static implicit operator uint160(ulong value) public byte[] ToBytes(bool lendian = true) { - var arr = new byte[WIDTH_BYTE]; + var arr = new byte[WidthByte]; Buffer.BlockCopy(Utils.ToBytes(this.pn0, true), 0, arr, 4 * 0, 4); Buffer.BlockCopy(Utils.ToBytes(this.pn1, true), 0, arr, 4 * 1, 4); Buffer.BlockCopy(Utils.ToBytes(this.pn2, true), 0, arr, 4 * 2, 4); @@ -825,14 +805,14 @@ public MutableUint160 AsBitcoinSerializable() public int GetSerializeSize() { - return WIDTH_BYTE; + return WidthByte; } public int Size { get { - return WIDTH_BYTE; + return WidthByte; } } From 0b75705f0c02b3ae06cc1d45319fb7405f8afd66 Mon Sep 17 00:00:00 2001 From: dangershony Date: Fri, 27 Mar 2020 23:45:37 +0000 Subject: [PATCH 5/7] Fix mempool comment --- src/Blockcore.Features.MemoryPool/MempoolValidator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Blockcore.Features.MemoryPool/MempoolValidator.cs b/src/Blockcore.Features.MemoryPool/MempoolValidator.cs index 294c90275..cd66355de 100644 --- a/src/Blockcore.Features.MemoryPool/MempoolValidator.cs +++ b/src/Blockcore.Features.MemoryPool/MempoolValidator.cs @@ -338,7 +338,7 @@ await this.mempoolLock.WriteAsync(() => // Remove conflicting transactions from the mempool foreach (TxMempoolEntry it in context.AllConflicting) - this.logger.LogInformation($"Replacing tx {it.TransactionHash} with {context.TransactionHash} for {context.ModifiedFees - context.ConflictingFees} BTC additional fees, {context.EntrySize - context.ConflictingSize} delta bytes"); + this.logger.LogDebug($"Replacing tx {it.TransactionHash} with {context.TransactionHash} for {context.ModifiedFees - context.ConflictingFees} BTC additional fees, {context.EntrySize - context.ConflictingSize} delta bytes"); this.memPool.RemoveStaged(context.AllConflicting, false); @@ -568,4 +568,4 @@ private bool IsCurrentForFeeEstimation() return true; } } -} +} \ No newline at end of file From 249bc3cb3baa1696a765ec00f7af5a4d9965f47f Mon Sep 17 00:00:00 2001 From: dangershony Date: Sat, 28 Mar 2020 00:13:49 +0000 Subject: [PATCH 6/7] Reverse little endian on target not source --- src/NBitcoin/UInt256.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/NBitcoin/UInt256.cs b/src/NBitcoin/UInt256.cs index bd3a34de3..cc140a151 100644 --- a/src/NBitcoin/UInt256.cs +++ b/src/NBitcoin/UInt256.cs @@ -120,14 +120,14 @@ public uint256(byte[] payload, bool littleEndian = true) var input = new Span(payload); - if (!littleEndian) - { - input.Reverse(); - } - Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, ExpectedSize / sizeof(ulong))); input.CopyTo(dst); + + if (!littleEndian) + { + dst.Reverse(); + } } public uint256(byte[] payload) : this(payload, true) From 968e2fc90daa6e0bb2f70ff8fc385b94c0b6cc18 Mon Sep 17 00:00:00 2001 From: dangershony Date: Thu, 2 Apr 2020 00:07:27 +0100 Subject: [PATCH 7/7] benchmark uint256 --- .gitignore | 1 + .../Blockcore.Benchmark.csproj | 33 +- .../Faster/FasterStorageTypes.cs | 162 ------ .../LiteDb/LiteDbStorageTypes.cs | 16 - .../NetworkBenchmarkBase.cs | 53 -- .../Persistence/DBreeze/FetchCoin.cs | 184 ------ .../Persistence/Faster/FetchCoin.cs | 106 ---- .../Persistence/LiteDb/FetchCoin.cs | 102 ---- src/Blockcore.Benchmark/Program.cs | 9 +- src/Blockcore.Benchmark/Uint256/UInt256New.cs | 490 ++++++++++++++++ src/Blockcore.Benchmark/Uint256/UInt256Old.cs | 522 ++++++++++++++++++ .../Uint256/Uint256_CreateNew.cs | 42 ++ .../Uint256/Uint256_Serialize.cs | 57 ++ .../Uint256/Uint256_ToBytes.cs | 47 ++ src/Blockcore.sln | 7 + 15 files changed, 1175 insertions(+), 656 deletions(-) delete mode 100644 src/Blockcore.Benchmark/Infrastructure/Faster/FasterStorageTypes.cs delete mode 100644 src/Blockcore.Benchmark/Infrastructure/LiteDb/LiteDbStorageTypes.cs delete mode 100644 src/Blockcore.Benchmark/NetworkBenchmarkBase.cs delete mode 100644 src/Blockcore.Benchmark/Persistence/DBreeze/FetchCoin.cs delete mode 100644 src/Blockcore.Benchmark/Persistence/Faster/FetchCoin.cs delete mode 100644 src/Blockcore.Benchmark/Persistence/LiteDb/FetchCoin.cs create mode 100644 src/Blockcore.Benchmark/Uint256/UInt256New.cs create mode 100644 src/Blockcore.Benchmark/Uint256/UInt256Old.cs create mode 100644 src/Blockcore.Benchmark/Uint256/Uint256_CreateNew.cs create mode 100644 src/Blockcore.Benchmark/Uint256/Uint256_Serialize.cs create mode 100644 src/Blockcore.Benchmark/Uint256/Uint256_ToBytes.cs diff --git a/.gitignore b/.gitignore index 82a697c2c..1ddb78ab6 100644 --- a/.gitignore +++ b/.gitignore @@ -263,3 +263,4 @@ src/.idea/.idea.Stratis.Bitcoin.FullNode/.idea/ .DS_Store *.iml +/src/Blockcore.Benchmark/BenchmarkDotNet.Artifacts/results diff --git a/src/Blockcore.Benchmark/Blockcore.Benchmark.csproj b/src/Blockcore.Benchmark/Blockcore.Benchmark.csproj index 13022e105..560b8c843 100644 --- a/src/Blockcore.Benchmark/Blockcore.Benchmark.csproj +++ b/src/Blockcore.Benchmark/Blockcore.Benchmark.csproj @@ -2,8 +2,8 @@ Exe - netcoreapp2.2 - 2.2.7 + netcoreapp3.1 + 3.1.0 @@ -13,35 +13,10 @@ - - - + - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Infrastructure/Faster/FasterStorageTypes.cs b/src/Blockcore.Benchmark/Infrastructure/Faster/FasterStorageTypes.cs deleted file mode 100644 index aa556323d..000000000 --- a/src/Blockcore.Benchmark/Infrastructure/Faster/FasterStorageTypes.cs +++ /dev/null @@ -1,162 +0,0 @@ -using System; -using System.Text; -using FASTER.core; - -namespace Stratis.Benchmark.Infrastructure.Faster -{ - public class CacheKey : IFasterEqualityComparer - { - public string Key; - public string Table; - - public string PartitionedKey => $"{this.Table}_{this.Key}"; - - public CacheKey() - { - } - - public CacheKey(string table, string key) - { - this.Key = key; - this.Table = table; - } - - public long GetHashCode64(ref CacheKey k) - { - long hashCode = 0; - if (!string.IsNullOrEmpty(k.PartitionedKey)) - { - byte[] byteContents = Encoding.Unicode.GetBytes(k.PartitionedKey); - System.Security.Cryptography.SHA256 hash = - new System.Security.Cryptography.SHA256CryptoServiceProvider(); - byte[] hashText = hash.ComputeHash(byteContents); - long hashCodeStart = BitConverter.ToInt64(hashText, 0); - long hashCodeMedium = BitConverter.ToInt64(hashText, 8); - long hashCodeEnd = BitConverter.ToInt64(hashText, 24); - hashCode = hashCodeStart ^ hashCodeMedium ^ hashCodeEnd; - } - - return hashCode; - } - - public static (string table, string key) ParseKey(string combinedKey) - { - if (string.IsNullOrEmpty(combinedKey)) - throw new ArgumentNullException(nameof(combinedKey)); - - var tokens = combinedKey.Split('_'); - if (tokens.Length < 2) - throw new ArgumentException(nameof(combinedKey)); - - return (tokens[0], tokens[1]); - } - - public bool Equals(ref CacheKey k1, ref CacheKey k2) - { - return k1.PartitionedKey == k2.PartitionedKey; - } - } - - public class CacheKeySerializer : BinaryObjectSerializer - { - public override void Deserialize(ref CacheKey obj) - { - string combinedKey = this.reader.ReadString(); - (string table, string key) = CacheKey.ParseKey(combinedKey); - obj.Key = key; - obj.Table = table; - } - - public override void Serialize(ref CacheKey obj) - { - this.writer.Write(obj.PartitionedKey); - } - } - - public class CacheValue - { - public byte[] Value; - - public CacheValue() - { - } - - public CacheValue(byte[] first) - { - this.Value = first; - } - } - - public class CacheValueSerializer : BinaryObjectSerializer - { - public override void Deserialize(ref CacheValue obj) - { - obj.Value = Encoding.UTF8.GetBytes(this.reader.ReadString()); - } - - public override void Serialize(ref CacheValue obj) - { - this.writer.Write(Encoding.UTF8.GetString(obj.Value)); - } - } - - public struct CacheInput - { - } - - public struct CacheOutput - { - public CacheValue Value; - } - - public class CacheFunctions : IFunctions - { - public void ConcurrentReader(ref CacheKey key, ref CacheInput input, ref CacheValue value, ref CacheOutput dst) - { - dst.Value = value; - } - - public void ConcurrentWriter(ref CacheKey key, ref CacheValue src, ref CacheValue dst) - { - dst = src; - } - - public void CopyUpdater(ref CacheKey key, ref CacheInput input, ref CacheValue oldValue, ref CacheValue newValue) - { - } - - public void InitialUpdater(ref CacheKey key, ref CacheInput input, ref CacheValue value) - { - } - - public void InPlaceUpdater(ref CacheKey key, ref CacheInput input, ref CacheValue value) - { - } - - public void CheckpointCompletionCallback(Guid sessionId, long serialNum) - { - } - - public void ReadCompletionCallback(ref CacheKey key, ref CacheInput input, ref CacheOutput output, Empty ctx, Status status) - { - } - - public void RMWCompletionCallback(ref CacheKey key, ref CacheInput input, Empty ctx, Status status) - { - } - - public void SingleReader(ref CacheKey key, ref CacheInput input, ref CacheValue value, ref CacheOutput dst) - { - dst.Value = value; - } - - public void SingleWriter(ref CacheKey key, ref CacheValue src, ref CacheValue dst) - { - dst = src; - } - - public void UpsertCompletionCallback(ref CacheKey key, ref CacheValue value, Empty ctx) - { - } - } -} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Infrastructure/LiteDb/LiteDbStorageTypes.cs b/src/Blockcore.Benchmark/Infrastructure/LiteDb/LiteDbStorageTypes.cs deleted file mode 100644 index da75dc494..000000000 --- a/src/Blockcore.Benchmark/Infrastructure/LiteDb/LiteDbStorageTypes.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace Stratis.Benchmark.Infrastructure.LiteDb -{ - public class CacheRecord - { - public string Id { get; set; } - - public byte[] Value { get; set; } - } - - public class CacheCoin - { - public string Id { get; set; } - - public byte[] Value { get; set; } - } -} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/NetworkBenchmarkBase.cs b/src/Blockcore.Benchmark/NetworkBenchmarkBase.cs deleted file mode 100644 index 028bde0d9..000000000 --- a/src/Blockcore.Benchmark/NetworkBenchmarkBase.cs +++ /dev/null @@ -1,53 +0,0 @@ -using DBreeze; -using FASTER.core; -using LiteDB; -using NBitcoin; -using Stratis.Benchmark.Infrastructure.Faster; -using Stratis.Bitcoin.Utilities; - -namespace Stratis.Benchmark -{ - /// - /// Base class to use as Network benchmark. - /// - public abstract class NetworkBenchmarkBase - { - protected readonly string dataFolder; - protected readonly Network network; - protected readonly DBreezeSerializer dBreezeSerializer; - protected const int DataSize = 1_000_000; - - public NetworkBenchmarkBase(Network network) - { - this.network = Guard.NotNull(network, nameof(network)); - this.dBreezeSerializer = new DBreezeSerializer(this.network.Consensus.ConsensusFactory); - - this.dataFolder = System.IO.Path.GetDirectoryName(this.GetType().Assembly.Location); - } - - protected DBreezeEngine CreateDBreezeEngine(string DBName = "DB") - { - return new DBreezeEngine($"{this.dataFolder}/{this.GetType().Name}{DBName}"); - } - - protected LiteDatabase CreateLiteDb(string DBName = "DB") - { - return new LiteDatabase($"FileName={this.dataFolder}/{this.GetType().Name}{DBName}.db;Mode=Exclusive;"); - } - - protected (IDevice log, FasterKV db) CreateFasterLog() - { - var logSize = 1L << 20; - var log = Devices.CreateLogDevice($"{this.dataFolder}/{this.GetType().Name}-hlog.log"); - - var fht = new FasterKV - ( - logSize, new CacheFunctions(), new LogSettings { LogDevice = log }, - null, - new SerializerSettings { keySerializer = () => new CacheKeySerializer(), valueSerializer = () => new CacheValueSerializer() } - ); - - return (log, fht); - } - } -} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Persistence/DBreeze/FetchCoin.cs b/src/Blockcore.Benchmark/Persistence/DBreeze/FetchCoin.cs deleted file mode 100644 index b4ffaeb86..000000000 --- a/src/Blockcore.Benchmark/Persistence/DBreeze/FetchCoin.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using BenchmarkDotNet.Attributes; -using DBreeze; -using DBreeze.DataTypes; -using NBitcoin; -using Stratis.Bitcoin.Features.Consensus; -using Stratis.Bitcoin.Networks; -using Transaction = DBreeze.Transactions.Transaction; - -namespace Stratis.Benchmark.Persistence.DBreeze -{ - [RankColumn] - public class DBreezeFetchCoin : NetworkBenchmarkBase - { - [Params(10, 100)] - public int N; - - private uint256[] data; - - public DBreezeFetchCoin() - : base(new BitcoinMain()) - { - } - - [GlobalSetup] - public void GenerateData() - { - Random rnd = new Random(); - this.data = new uint256[this.N]; - for (int i = 0; i < this.N; i++) - { - this.data[i] = new uint256((ulong)rnd.Next()); - } - Array.Sort(this.data, new UInt256Comparer()); - - PopulateDB(); - } - - private DBreezeEngine GetDBEngine() - { - return this.CreateDBreezeEngine(this.N.ToString()); - } - - private void PopulateDB() - { - using (var engine = this.GetDBEngine()) - { - // Store data. - using (Transaction tx = engine.GetTransaction()) - { - tx.Insert("BlockHash", new byte[0], this.data[0].ToBytes()); - - foreach (uint256 d in this.data) - { - tx.Insert("Coins", d.ToBytes(false), d.ToBytes()); - } - - tx.Commit(); - } - } - } - - [Benchmark(Baseline = true)] - public List FetchData() - { - using (var engine = this.GetDBEngine()) - { - using (Transaction transaction = engine.GetTransaction()) - { - transaction.ValuesLazyLoadingIsOn = false; - - var result = new List(this.data.Length); - - foreach (uint256 input in this.data) - { - Row row = transaction.Select("Coins", input.ToBytes(false)); - uint256 outputs = row.Exists ? this.dBreezeSerializer.Deserialize(row.Value) : null; - - result.Add(outputs); - } - - return result; - } - } - } - - [Benchmark] - public List ParallelRangePartitioner() - { - using (var engine = this.GetDBEngine()) - { - var result = new List(this.data.Length); - var rangePartitioner = Partitioner.Create(0, this.data.Length); - Parallel.ForEach(rangePartitioner, (range, loopState) => - { - using (Transaction transaction = engine.GetTransaction()) - { - transaction.ValuesLazyLoadingIsOn = false; - - for (int i = range.Item1; i < range.Item2; i++) - { - Row row = transaction.Select("Coins", this.data[i].ToBytes(false)); - uint256 outputs = row.Exists ? this.dBreezeSerializer.Deserialize(row.Value) : null; - - result.Add(outputs); - } - } - }); - - return result; - } - } - - [Benchmark] - public List FetchAllRawDataThenParallelDeserialize() - { - using (var engine = this.GetDBEngine()) - { - using (Transaction transaction = engine.GetTransaction()) - { - transaction.ValuesLazyLoadingIsOn = false; - - var result = new uint256[this.data.Length]; - var rawValues = new List(this.data.Length); - - foreach (uint256 input in this.data) - { - Row row = transaction.Select("Coins", input.ToBytes(false)); - - rawValues.Add(row.Value); - } - - return rawValues.AsParallel().Select(rawValue => this.dBreezeSerializer.Deserialize(rawValue)).ToList(); - } - } - } - - [Benchmark] - public List FetchAllRawDataThenParallelDeserializeWithPartitioner() - { - using (var engine = this.GetDBEngine()) - { - using (Transaction transaction = engine.GetTransaction()) - { - transaction.ValuesLazyLoadingIsOn = false; - - var result = new uint256[this.data.Length]; - var rawValues = new List(this.data.Length); - - foreach (uint256 input in this.data) - { - Row row = transaction.Select("Coins", input.ToBytes(false)); - - rawValues.Add(row.Value); - } - - object innerLock = new object(); - var results = new List(this.data.Length); - - var rangePartitioner = Partitioner.Create(0, this.data.Length); - Parallel.ForEach(rangePartitioner, (range, loopState) => - { - List partialResult = new List(range.Item2 - range.Item1); - for (int i = range.Item1; i < range.Item2; i++) - { - partialResult.Add(this.dBreezeSerializer.Deserialize(rawValues[i])); - } - - lock (innerLock) - { - results.AddRange(partialResult); - } - }); - - return results; - } - } - } - } -} diff --git a/src/Blockcore.Benchmark/Persistence/Faster/FetchCoin.cs b/src/Blockcore.Benchmark/Persistence/Faster/FetchCoin.cs deleted file mode 100644 index b5fb6db7a..000000000 --- a/src/Blockcore.Benchmark/Persistence/Faster/FetchCoin.cs +++ /dev/null @@ -1,106 +0,0 @@ -namespace Stratis.Benchmark.Persistence.Faster -{ - using System; - using System.Collections.Generic; - using System.Linq; - using BenchmarkDotNet.Attributes; - using Bitcoin.Networks; - using FASTER.core; - using NBitcoin; - using Stratis.Benchmark.Infrastructure.Faster; - using Stratis.Bitcoin.Features.Consensus; - - [RankColumn] - public class MsFasterFetchCoin : NetworkBenchmarkBase - { - [Params(1000)] - public int N; - - private uint256[] data; - private IDevice log; - private FasterKV db; - - public MsFasterFetchCoin() - : base(new BitcoinMain()) - { - } - - [GlobalSetup] - public void GenerateData() - { - Random rnd = new Random(); - this.data = new uint256[DataSize]; - for (int i = 0; i < DataSize; i++) - { - this.data[i] = new uint256((ulong)i); - } - - Array.Sort(this.data, new UInt256Comparer()); - - (IDevice log, FasterKV db) result = this.CreateFasterLog(); - this.db = result.db; - this.log = result.log; - this.PopulateDB(); - } - - private void PopulateDB() - { - this.db.StartSession(); - - var blockHashKey = new CacheKey("BlockHash", string.Empty); - var blockHashValue = new CacheValue(this.data[0].ToBytes()); - this.db.Upsert(ref blockHashKey, ref blockHashValue, Empty.Default, 1L); - - foreach (uint256 d in this.data) - { - var coinKey = new CacheKey("Coins", d.ToString()); - var coinValue = new CacheValue(this.data[0].ToBytes()); - this.db.Upsert(ref coinKey, ref coinValue, Empty.Default, 1L); - } - - this.db.Log.Flush(true); - this.db.StopSession(); - } - - [Benchmark(Baseline = true)] - public List FetchData() - { - this.db.StartSession(); - - var result = new List(this.N); - - foreach (uint256 input in this.data.OrderBy(d => Guid.NewGuid()).Take(this.N)) - { - var coinKey = new CacheKey("Coins", input.ToString()); - var output = new CacheOutput(); - var dbInput = default(CacheInput); - Status status = this.db.Read(ref coinKey, ref dbInput, ref output, Empty.Default, 1); - if (status == Status.PENDING) this.db.CompletePending(true); - - uint256 outputs = status == Status.OK ? this.dBreezeSerializer.Deserialize(output.Value.Value) : null; - - result.Add(outputs); - } - - this.db.StopSession(); - - return result; - } - - [Benchmark] - public void DeleteData() - { - this.db.StartSession(); - - foreach (uint256 input in this.data.OrderBy(d => Guid.NewGuid()).Take(this.N)) - { - var coinKey = new CacheKey("Coins", input.ToString()); - Status status = this.db.DeleteFromMemory(ref coinKey, 1); - if (status == Status.ERROR) - throw new ApplicationException("Failed to delete data"); - } - - this.db.StopSession(); - } - } -} diff --git a/src/Blockcore.Benchmark/Persistence/LiteDb/FetchCoin.cs b/src/Blockcore.Benchmark/Persistence/LiteDb/FetchCoin.cs deleted file mode 100644 index bc8090f52..000000000 --- a/src/Blockcore.Benchmark/Persistence/LiteDb/FetchCoin.cs +++ /dev/null @@ -1,102 +0,0 @@ -namespace Stratis.Benchmark.Persistence.LiteDb -{ - using System; - using System.Collections.Generic; - using System.Linq; - using BenchmarkDotNet.Attributes; - using Bitcoin.Networks; - using LiteDB; - using NBitcoin; - using Stratis.Benchmark.Infrastructure.LiteDb; - using Stratis.Bitcoin.Features.Consensus; - - [RankColumn] - public class LiteDbFetchCoin : NetworkBenchmarkBase - { - [Params(1000)] - public int N; - - private uint256[] data; - - public LiteDbFetchCoin() : base(new BitcoinMain()) - { - } - - [GlobalSetup] - public void GenerateData() - { - Random rnd = new Random(); - this.data = new uint256[DataSize]; - for (int i = 0; i < DataSize; i++) - { - this.data[i] = new uint256((ulong) i); - } - - Array.Sort(this.data, new UInt256Comparer()); - - this.PopulateDB(); - } - - private LiteDatabase GetDBEngine() - { - var db = this.CreateLiteDb(this.N.ToString()); - return db; - } - - private void PopulateDB() - { - using (var engine = this.GetDBEngine()) - { - engine.DropCollection("blockhash"); - engine.DropCollection("coins"); - var blockHash = engine.GetCollection("blockhash"); - var record = new CacheRecord { Id = Guid.NewGuid().ToString(), Value = this.data[0].ToBytes() }; - - blockHash.Upsert(record); - - var coins = engine.GetCollection("coins"); - var mapper = BsonMapper.Global; - mapper.Entity().Id(p => p.Id); - - var coinData = this.data.Select(d => mapper.ToDocument(new CacheCoin { Id = d.ToString(), Value = d.ToBytes() })).ToList(); - coins.InsertBulk(coinData); - } - } - - - [Benchmark(Baseline = true)] - public List FetchData() - { - using (var engine = this.GetDBEngine()) - { - var result = new List(this.N); - - var collection = engine.GetCollection("coins"); - foreach (uint256 input in this.data.OrderBy(d => Guid.NewGuid()).Take(this.N)) - { - var coin = collection.FindById(input.ToString()); - uint256 outputs = coin != null ? this.dBreezeSerializer.Deserialize(coin.Value) : null; - - result.Add(outputs); - } - - return result; - } - } - - [Benchmark] - public void DeleteData() - { - using (var engine = this.GetDBEngine()) - { - var collection = engine.GetCollection("coins"); - foreach (uint256 input in this.data.OrderBy(d => Guid.NewGuid()).Take(this.N)) - { - var success = collection.Delete(input.ToString()); - if (!success) - throw new ApplicationException("Failed to delete data"); - } - } - } - } -} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Program.cs b/src/Blockcore.Benchmark/Program.cs index dd53eb629..3abd3b3e8 100644 --- a/src/Blockcore.Benchmark/Program.cs +++ b/src/Blockcore.Benchmark/Program.cs @@ -1,10 +1,11 @@ using BenchmarkDotNet.Running; +using Blockcore.Benchmark.Uint256; -namespace Stratis.Benchmark +namespace Blockcore.Benchmark { - class Program + internal class Program { - static void Main(string[] args) + private static void Main(string[] args) { BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args); @@ -12,4 +13,4 @@ static void Main(string[] args) //BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); } } -} +} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Uint256/UInt256New.cs b/src/Blockcore.Benchmark/Uint256/UInt256New.cs new file mode 100644 index 000000000..7011ab9f4 --- /dev/null +++ b/src/Blockcore.Benchmark/Uint256/UInt256New.cs @@ -0,0 +1,490 @@ +using System; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using NBitcoin; +using NBitcoin.DataEncoders; + +namespace Blockcore.Benchmark.Uint256.New +{ + /// + /// An implementation of uint256 based on https://github.com/MithrilMan/MithrilShards + /// Link to type https://github.com/MithrilMan/MithrilShards/blob/master/src/MithrilShards.Core/DataTypes/Uint256.cs + /// Big credit to @MithrilMan for making this optimization + /// + public class uint256 : IEquatable, IComparable, IComparable + { + public class MutableUint256 : IBitcoinSerializable + { + private uint256 value; + + public uint256 Value + { + get + { + return this.value; + } + set + { + this.value = value; + } + } + + public uint256 MaxValue => uint256.Parse("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + + public MutableUint256() + { + this.value = Zero; + } + + public MutableUint256(uint256 value) + { + this.value = value; + } + + public void ReadWrite(BitcoinStream stream) + { + if (stream.Serializing) + { + Span b = this.Value.ToSpan(); + stream.ReadWrite(ref b); + } + else + { + Span b = stackalloc byte[WidthByte]; + stream.ReadWrite(ref b); + this.value = new uint256(b); + } + } + } + + private const int ExpectedSize = 32; + + private const int Width = 256 / 64; + + private const int WidthByte = 256 / 8; + +#pragma warning disable IDE0044 // Add readonly modifier + private ulong part1; + private ulong part2; + private ulong part3; + private ulong part4; +#pragma warning restore IDE0044 // Add readonly modifier + + internal ulong Part1 => this.part1; + internal ulong Part2 => this.part2; + internal ulong Part3 => this.part3; + internal ulong Part4 => this.part4; + + public static uint256 Zero { get; } = new uint256(); + + public static uint256 One { get; } = new uint256(1); + + public uint256() + { + } + + public uint256(uint256 b) + { + this.part1 = b.part1; + this.part2 = b.part2; + this.part3 = b.part3; + this.part4 = b.part4; + } + + public uint256(ReadOnlySpan input) + { + if (input.Length != ExpectedSize) + { + throw new FormatException("the byte array should be 32 bytes long"); + } + + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, ExpectedSize / sizeof(ulong))); + + input.CopyTo(dst); + } + + public uint256(ulong b) + { + this.part1 = b; + this.part2 = 0; + this.part3 = 0; + this.part4 = 0; + } + + public uint256(byte[] payload, bool littleEndian = true) + { + if (payload.Length != WidthByte) + { + throw new FormatException("the byte array should be 256 byte long"); + } + + var input = new Span(payload); + + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, ExpectedSize / sizeof(ulong))); + + input.CopyTo(dst); + + if (!littleEndian) + { + dst.Reverse(); + } + } + + public uint256(byte[] payload) : this(payload, true) + { + } + + public uint256(string hexString) + { + if (hexString is null) + { + throw new ArgumentNullException(nameof(hexString)); + } + + //account for 0x prefix + if (hexString.Length < ExpectedSize * 2) + { + throw new FormatException($"Invalid Hex String, the hex string should be {ExpectedSize * 2} chars long or {(ExpectedSize * 2) + 4} if prefixed with 0x."); + } + + ReadOnlySpan hexAsSpan = (hexString[0] == '0' && (hexString[1] == 'x' || hexString[1] == 'X')) ? hexString.Trim().AsSpan(2) : hexString.Trim().AsSpan(); + + if (hexAsSpan.Length != ExpectedSize * 2) + { + throw new FormatException($"Invalid Hex String, the hex string should be {ExpectedSize * 2} chars long or {(ExpectedSize * 2) + 4} if prefixed with 0x."); + } + + Span dst = MemoryMarshal.AsBytes(MemoryMarshal.CreateSpan(ref this.part1, ExpectedSize / sizeof(ulong))); + + int i = hexAsSpan.Length - 1; + int j = 0; + + while (i > 0) + { + char c = hexAsSpan[i--]; + if (c >= '0' && c <= '9') + { + dst[j] = (byte)(c - '0'); + } + else if (c >= 'a' && c <= 'f') + { + dst[j] = (byte)(c - ('a' - 10)); + } + else if (c >= 'A' && c <= 'F') + { + dst[j] = (byte)(c - ('A' - 10)); + } + else + { + throw new ArgumentException("Invalid nibble: " + c); + } + + c = hexAsSpan[i--]; + if (c >= '0' && c <= '9') + { + dst[j] |= (byte)((c - '0') << 4); + } + else if (c >= 'a' && c <= 'f') + { + dst[j] |= (byte)((c - ('a' - 10)) << 4); + } + else if (c >= 'A' && c <= 'F') + { + dst[j] |= (byte)((c - ('A' - 10)) << 4); + } + else + { + throw new ArgumentException("Invalid nibble: " + c); + } + + j++; + } + } + + public static uint256 Parse(string hexString) + { + return new uint256(hexString); + } + + private uint256(ulong[] array) + { + if (array.Length != Width) + { + throw new ArgumentOutOfRangeException(); + } + + this.part1 = array[0]; + this.part2 = array[1]; + this.part3 = array[2]; + this.part4 = array[3]; + } + + private ulong[] ToArray() + { + return new ulong[] { this.part1, this.part2, this.part3, this.part4, }; + } + + public static uint256 operator <<(uint256 a, int shift) + { + ulong[] source = a.ToArray(); + var target = new ulong[source.Length]; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < Width; i++) + { + if (i + k + 1 < Width && shift != 0) + target[i + k + 1] |= (source[i] >> (32 - shift)); + if (i + k < Width) + target[i + k] |= (target[i] << shift); + } + return new uint256(target); + } + + public static uint256 operator >>(uint256 a, int shift) + { + ulong[] source = a.ToArray(); + var target = new ulong[source.Length]; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < Width; i++) + { + if (i - k - 1 >= 0 && shift != 0) + target[i - k - 1] |= (source[i] << (32 - shift)); + if (i - k >= 0) + target[i - k] |= (source[i] >> shift); + } + return new uint256(target); + } + + public static bool TryParse(string hexString, out uint256? result) + { + try + { + result = new uint256(hexString); + return true; + } + catch (Exception) + { + result = null; + } + + return false; + } + + public byte GetByte(int index) + { + int uintIndex = index / sizeof(ulong); + int byteIndex = index % sizeof(ulong); + ulong value; + + switch (uintIndex) + { + case 0: + value = this.part1; + break; + + case 1: + value = this.part2; + break; + + case 2: + value = this.part3; + break; + + case 3: + value = this.part4; + break; + + default: + throw new ArgumentOutOfRangeException("index"); + } + + return (byte)(value >> (byteIndex * 8)); + } + + public ReadOnlySpan ToReadOnlySpan() + { + return MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref this.part1, ExpectedSize / sizeof(ulong))); + } + + public Span ToSpan() + { + return MemoryMarshal.CreateSpan(ref Unsafe.As(ref this.part1), ExpectedSize); + } + + public byte[] ToBytes(bool littleEndian = true) + { + var output = this.ToSpan().ToArray(); + + if (!littleEndian) + { + var span = output.AsSpan(); + span.Reverse(); + } + + return output; + } + + //public void ToBytes(Span output, bool lendian = true) + //{ + // if (output.Length < WIDTH_BYTE) + // throw new ArgumentException(message: $"The array should be at least of size {WIDTH_BYTE}", paramName: nameof(output)); + + // output = this.GetWritableBytes(); + + // if (!lendian) + // output.Reverse(); + //} + + public override int GetHashCode() + { + return (int)this.part1; + } + + public override bool Equals(object? obj) + { + return this.Equals(obj as uint256); + } + + public static bool operator !=(uint256? a, uint256? b) + { + return !(a == b); + } + + public static bool operator ==(uint256? a, uint256? b) + { + if (ReferenceEquals(a, b)) + return true; + + if (((object)a == null) || ((object)b == null)) + return false; + + return a.Equals(b); + } + + public bool Equals(uint256? other) + { + if (other is null) + return false; + + return this.part1 == other.part1 + && this.part2 == other.part2 + && this.part3 == other.part3 + && this.part4 == other.part4; + } + + public int CompareTo(uint256 other) + { + return CompareTypes(this, other); + } + + public int CompareTo(object obj) + { + switch (obj) + { + case uint256 target: + return this.CompareTo(target); + + case null: + return this.CompareTo(null as uint256); + + default: + throw new ArgumentException($"Object is not an instance of uint256", nameof(obj)); + } + } + + public static bool operator <(uint256 a, uint256 b) + { + return CompareTypes(a, b) < 0; + } + + public static bool operator >(uint256 a, uint256 b) + { + return CompareTypes(a, b) > 0; + } + + public static bool operator <=(uint256 a, uint256 b) + { + return CompareTypes(a, b) <= 0; + } + + public static bool operator >=(uint256 a, uint256 b) + { + return CompareTypes(a, b) >= 0; + } + + public static int CompareTypes(uint256 a, uint256 b) + { + if (a is null && b is null) + return 0; + + if (a is null && !(b is null)) + return -1; + + if (!(a is null) && b is null) + return 1; + + if (a.part4 < b.part4) return -1; + if (a.part4 > b.part4) return 1; + if (a.part3 < b.part3) return -1; + if (a.part3 > b.part3) return 1; + if (a.part2 < b.part2) return -1; + if (a.part2 > b.part2) return 1; + if (a.part1 < b.part1) return -1; + if (a.part1 > b.part1) return 1; + + return 0; + } + + public static bool operator ==(uint256 a, ulong b) + { + return (a == new uint256(b)); + } + + public static bool operator !=(uint256 a, ulong b) + { + return !(a == new uint256(b)); + } + + public static implicit operator uint256(ulong value) + { + return new uint256(value); + } + + public MutableUint256 AsBitcoinSerializable() + { + return new MutableUint256(this); + } + + public override string ToString() + { + return string.Create(ExpectedSize * 2, this, (dst, src) => + { + ReadOnlySpan rawData = MemoryMarshal.AsBytes(MemoryMarshal.CreateReadOnlySpan(ref src.part1, ExpectedSize / sizeof(ulong))); + + const string HexValues = "0123456789abcdef"; + + int i = rawData.Length - 1; + int j = 0; + + while (i >= 0) + { + byte b = rawData[i--]; + dst[j++] = HexValues[b >> 4]; + dst[j++] = HexValues[b & 0xF]; + } + }); + } + + public ulong GetLow64() + { + return this.part1; + } + + public uint GetLow32() + { + return (uint)(this.part1 & uint.MaxValue); + } + } +} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Uint256/UInt256Old.cs b/src/Blockcore.Benchmark/Uint256/UInt256Old.cs new file mode 100644 index 000000000..e30854cdb --- /dev/null +++ b/src/Blockcore.Benchmark/Uint256/UInt256Old.cs @@ -0,0 +1,522 @@ +using System; +using System.Linq; +using NBitcoin; +using NBitcoin.DataEncoders; + +namespace Blockcore.Benchmark.Uint256.Old +{ + public class uint256 : IEquatable, IComparable, IComparable + { + public class MutableUint256 : IBitcoinSerializable + { + private uint256 _Value; + + public uint256 Value + { + get + { + return this._Value; + } + set + { + this._Value = value; + } + } + + public uint256 MaxValue => uint256.Parse("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"); + + public MutableUint256() + { + this._Value = Zero; + } + + public MutableUint256(uint256 value) + { + this._Value = value; + } + + public void ReadWrite(BitcoinStream stream) + { + if (stream.Serializing) + { + Span b = stackalloc byte[WIDTH_BYTE]; + this.Value.ToBytes(b); + stream.ReadWrite(ref b); + } + else + { + Span b = stackalloc byte[WIDTH_BYTE]; + stream.ReadWrite(ref b); + this._Value = new uint256(b); + } + } + } + + private static readonly uint256 _Zero = new uint256(); + + public static uint256 Zero + { + get { return _Zero; } + } + + private static readonly uint256 _One = new uint256(1); + + public static uint256 One + { + get { return _One; } + } + + public uint256() + { + } + + public uint256(uint256 b) + { + this.pn0 = b.pn0; + this.pn1 = b.pn1; + this.pn2 = b.pn2; + this.pn3 = b.pn3; + this.pn4 = b.pn4; + this.pn5 = b.pn5; + this.pn6 = b.pn6; + this.pn7 = b.pn7; + } + + private const int WIDTH = 256 / 32; + + private uint256(uint[] array) + { + if (array.Length != WIDTH) + throw new ArgumentOutOfRangeException(); + + this.pn0 = array[0]; + this.pn1 = array[1]; + this.pn2 = array[2]; + this.pn3 = array[3]; + this.pn4 = array[4]; + this.pn5 = array[5]; + this.pn6 = array[6]; + this.pn7 = array[7]; + } + + private uint[] ToArray() + { + return new uint[] { this.pn0, this.pn1, this.pn2, this.pn3, this.pn4, this.pn5, this.pn6, this.pn7 }; + } + + public static uint256 operator <<(uint256 a, int shift) + { + uint[] source = a.ToArray(); + var target = new uint[source.Length]; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i + k + 1 < WIDTH && shift != 0) + target[i + k + 1] |= (source[i] >> (32 - shift)); + if (i + k < WIDTH) + target[i + k] |= (target[i] << shift); + } + return new uint256(target); + } + + public static uint256 operator >>(uint256 a, int shift) + { + uint[] source = a.ToArray(); + var target = new uint[source.Length]; + int k = shift / 32; + shift = shift % 32; + for (int i = 0; i < WIDTH; i++) + { + if (i - k - 1 >= 0 && shift != 0) + target[i - k - 1] |= (source[i] << (32 - shift)); + if (i - k >= 0) + target[i - k] |= (source[i] >> shift); + } + return new uint256(target); + } + + public static uint256 Parse(string hex) + { + return new uint256(hex); + } + + public static bool TryParse(string hex, out uint256 result) + { + if (hex == null) + throw new ArgumentNullException("hex"); + if (hex.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) + hex = hex.Substring(2); + result = null; + if (hex.Length != WIDTH_BYTE * 2) + return false; + if (!((HexEncoder)Encoders.Hex).IsValid(hex)) + return false; + result = new uint256(hex); + return true; + } + + private static readonly HexEncoder Encoder = new HexEncoder(); + private const int WIDTH_BYTE = 256 / 8; + internal readonly UInt32 pn0; + internal readonly UInt32 pn1; + internal readonly UInt32 pn2; + internal readonly UInt32 pn3; + internal readonly UInt32 pn4; + internal readonly UInt32 pn5; + internal readonly UInt32 pn6; + internal readonly UInt32 pn7; + + public byte GetByte(int index) + { + int uintIndex = index / sizeof(uint); + int byteIndex = index % sizeof(uint); + UInt32 value; + switch (uintIndex) + { + case 0: + value = this.pn0; + break; + + case 1: + value = this.pn1; + break; + + case 2: + value = this.pn2; + break; + + case 3: + value = this.pn3; + break; + + case 4: + value = this.pn4; + break; + + case 5: + value = this.pn5; + break; + + case 6: + value = this.pn6; + break; + + case 7: + value = this.pn7; + break; + + default: + throw new ArgumentOutOfRangeException("index"); + } + return (byte)(value >> (byteIndex * 8)); + } + + public override string ToString() + { + var bytes = ToBytes(); + Array.Reverse(bytes); + return Encoder.EncodeData(bytes); + } + + public uint256(ulong b) + { + this.pn0 = (uint)b; + this.pn1 = (uint)(b >> 32); + this.pn2 = 0; + this.pn3 = 0; + this.pn4 = 0; + this.pn5 = 0; + this.pn6 = 0; + this.pn7 = 0; + } + + public uint256(byte[] vch, bool lendian = true) + { + if (vch.Length != WIDTH_BYTE) + { + throw new FormatException("the byte array should be 256 byte long"); + } + + if (!lendian) + vch = vch.Reverse().ToArray(); + + this.pn0 = Utils.ToUInt32(vch, 4 * 0, true); + this.pn1 = Utils.ToUInt32(vch, 4 * 1, true); + this.pn2 = Utils.ToUInt32(vch, 4 * 2, true); + this.pn3 = Utils.ToUInt32(vch, 4 * 3, true); + this.pn4 = Utils.ToUInt32(vch, 4 * 4, true); + this.pn5 = Utils.ToUInt32(vch, 4 * 5, true); + this.pn6 = Utils.ToUInt32(vch, 4 * 6, true); + this.pn7 = Utils.ToUInt32(vch, 4 * 7, true); + } + + public uint256(ReadOnlySpan bytes) + { + if (bytes.Length != WIDTH_BYTE) + { + throw new FormatException("the byte array should be 32 bytes long"); + } + + this.pn0 = Utils.ToUInt32(bytes, 4 * 0, true); + this.pn1 = Utils.ToUInt32(bytes, 4 * 1, true); + this.pn2 = Utils.ToUInt32(bytes, 4 * 2, true); + this.pn3 = Utils.ToUInt32(bytes, 4 * 3, true); + this.pn4 = Utils.ToUInt32(bytes, 4 * 4, true); + this.pn5 = Utils.ToUInt32(bytes, 4 * 5, true); + this.pn6 = Utils.ToUInt32(bytes, 4 * 6, true); + this.pn7 = Utils.ToUInt32(bytes, 4 * 7, true); + } + + public uint256(string str) + { + this.pn0 = 0; + this.pn1 = 0; + this.pn2 = 0; + this.pn3 = 0; + this.pn4 = 0; + this.pn5 = 0; + this.pn6 = 0; + this.pn7 = 0; + str = str.Trim(); + + if (str.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) + str = str.Substring(2); + + byte[] bytes = Encoder.DecodeData(str).Reverse().ToArray(); + if (bytes.Length != WIDTH_BYTE) + throw new FormatException("Invalid hex length"); + this.pn0 = Utils.ToUInt32(bytes, 4 * 0, true); + this.pn1 = Utils.ToUInt32(bytes, 4 * 1, true); + this.pn2 = Utils.ToUInt32(bytes, 4 * 2, true); + this.pn3 = Utils.ToUInt32(bytes, 4 * 3, true); + this.pn4 = Utils.ToUInt32(bytes, 4 * 4, true); + this.pn5 = Utils.ToUInt32(bytes, 4 * 5, true); + this.pn6 = Utils.ToUInt32(bytes, 4 * 6, true); + this.pn7 = Utils.ToUInt32(bytes, 4 * 7, true); + } + + public uint256(byte[] vch) + : this(vch, true) + { + } + + public override bool Equals(object obj) + { + var item = obj as uint256; + return this.Equals(item); + } + + public bool Equals(uint256 other) + { + if (other is null) + { + return false; + } + + bool equals = true; + equals &= this.pn0 == other.pn0; + equals &= this.pn1 == other.pn1; + equals &= this.pn2 == other.pn2; + equals &= this.pn3 == other.pn3; + equals &= this.pn4 == other.pn4; + equals &= this.pn5 == other.pn5; + equals &= this.pn6 == other.pn6; + equals &= this.pn7 == other.pn7; + return equals; + } + + public int CompareTo(uint256 other) + { + return Comparison(this, other); + } + + public int CompareTo(object obj) + { + return obj is uint256 v ? CompareTo(v) : + obj is null ? CompareTo(null as uint256) : throw new ArgumentException($"Object is not an instance of uint256", nameof(obj)); + } + + public static bool operator ==(uint256 a, uint256 b) + { + if (ReferenceEquals(a, b)) + return true; + if (((object)a == null) || ((object)b == null)) + return false; + + bool equals = true; + equals &= a.pn0 == b.pn0; + equals &= a.pn1 == b.pn1; + equals &= a.pn2 == b.pn2; + equals &= a.pn3 == b.pn3; + equals &= a.pn4 == b.pn4; + equals &= a.pn5 == b.pn5; + equals &= a.pn6 == b.pn6; + equals &= a.pn7 == b.pn7; + return equals; + } + + public static bool operator <(uint256 a, uint256 b) + { + return Comparison(a, b) < 0; + } + + public static bool operator >(uint256 a, uint256 b) + { + return Comparison(a, b) > 0; + } + + public static bool operator <=(uint256 a, uint256 b) + { + return Comparison(a, b) <= 0; + } + + public static bool operator >=(uint256 a, uint256 b) + { + return Comparison(a, b) >= 0; + } + + public static int Comparison(uint256 a, uint256 b) + { + if (a is null && b is null) + return 0; + if (a is null && !(b is null)) + return -1; + if (!(a is null) && b is null) + return 1; + if (a.pn7 < b.pn7) + return -1; + if (a.pn7 > b.pn7) + return 1; + if (a.pn6 < b.pn6) + return -1; + if (a.pn6 > b.pn6) + return 1; + if (a.pn5 < b.pn5) + return -1; + if (a.pn5 > b.pn5) + return 1; + if (a.pn4 < b.pn4) + return -1; + if (a.pn4 > b.pn4) + return 1; + if (a.pn3 < b.pn3) + return -1; + if (a.pn3 > b.pn3) + return 1; + if (a.pn2 < b.pn2) + return -1; + if (a.pn2 > b.pn2) + return 1; + if (a.pn1 < b.pn1) + return -1; + if (a.pn1 > b.pn1) + return 1; + if (a.pn0 < b.pn0) + return -1; + if (a.pn0 > b.pn0) + return 1; + return 0; + } + + public static bool operator !=(uint256 a, uint256 b) + { + return !(a == b); + } + + public static bool operator ==(uint256 a, ulong b) + { + return (a == new uint256(b)); + } + + public static bool operator !=(uint256 a, ulong b) + { + return !(a == new uint256(b)); + } + + public static implicit operator uint256(ulong value) + { + return new uint256(value); + } + + public byte[] ToBytes(bool lendian = true) + { + var arr = new byte[WIDTH_BYTE]; + this.ToBytes(arr); + if (!lendian) + Array.Reverse(arr); + return arr; + } + + public void ToBytes(byte[] output) + { + Buffer.BlockCopy(Utils.ToBytes(this.pn0, true), 0, output, 4 * 0, 4); + Buffer.BlockCopy(Utils.ToBytes(this.pn1, true), 0, output, 4 * 1, 4); + Buffer.BlockCopy(Utils.ToBytes(this.pn2, true), 0, output, 4 * 2, 4); + Buffer.BlockCopy(Utils.ToBytes(this.pn3, true), 0, output, 4 * 3, 4); + Buffer.BlockCopy(Utils.ToBytes(this.pn4, true), 0, output, 4 * 4, 4); + Buffer.BlockCopy(Utils.ToBytes(this.pn5, true), 0, output, 4 * 5, 4); + Buffer.BlockCopy(Utils.ToBytes(this.pn6, true), 0, output, 4 * 6, 4); + Buffer.BlockCopy(Utils.ToBytes(this.pn7, true), 0, output, 4 * 7, 4); + } + + public void ToBytes(Span output, bool lendian = true) + { + if (output.Length < WIDTH_BYTE) + throw new ArgumentException(message: $"The array should be at least of size {WIDTH_BYTE}", paramName: nameof(output)); + + Span initial = output; + Utils.ToBytes(this.pn0, true, output); + output = output.Slice(4); + Utils.ToBytes(this.pn1, true, output); + output = output.Slice(4); + Utils.ToBytes(this.pn2, true, output); + output = output.Slice(4); + Utils.ToBytes(this.pn3, true, output); + output = output.Slice(4); + Utils.ToBytes(this.pn4, true, output); + output = output.Slice(4); + Utils.ToBytes(this.pn5, true, output); + output = output.Slice(4); + Utils.ToBytes(this.pn6, true, output); + output = output.Slice(4); + Utils.ToBytes(this.pn7, true, output); + + if (!lendian) + initial.Reverse(); + } + + public MutableUint256 AsBitcoinSerializable() + { + return new MutableUint256(this); + } + + public int GetSerializeSize() + { + return WIDTH_BYTE; + } + + public int Size + { + get + { + return WIDTH_BYTE; + } + } + + public ulong GetLow64() + { + return this.pn0 | (ulong)this.pn1 << 32; + } + + public uint GetLow32() + { + return this.pn0; + } + + public override int GetHashCode() + { + return (int)this.pn0; + } + } +} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Uint256/Uint256_CreateNew.cs b/src/Blockcore.Benchmark/Uint256/Uint256_CreateNew.cs new file mode 100644 index 000000000..4a8f8ed7b --- /dev/null +++ b/src/Blockcore.Benchmark/Uint256/Uint256_CreateNew.cs @@ -0,0 +1,42 @@ +using System; +using BenchmarkDotNet.Attributes; +using Blockcore.Benchmark.Uint256; + +namespace Blockcore.Benchmark.Uint256 +{ + [RankColumn, MemoryDiagnoser] + public class Uint256_CreateNew + { + private readonly byte[] data; + + public Uint256_CreateNew() + { + this.data = new byte[32]; + new Random(32).NextBytes(this.data); + } + + [Benchmark] + public New.uint256 Uint256_New() + { + return new New.uint256(this.data); + } + + [Benchmark] + public Old.uint256 Uint256_Old() + { + return new Old.uint256(this.data); + } + + [Benchmark] + public New.uint256 Uint256_New_le() + { + return new New.uint256(this.data, false); + } + + [Benchmark] + public Old.uint256 Uint256_Old_le() + { + return new Old.uint256(this.data, false); + } + } +} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Uint256/Uint256_Serialize.cs b/src/Blockcore.Benchmark/Uint256/Uint256_Serialize.cs new file mode 100644 index 000000000..d3a048070 --- /dev/null +++ b/src/Blockcore.Benchmark/Uint256/Uint256_Serialize.cs @@ -0,0 +1,57 @@ +using System; +using System.IO; +using BenchmarkDotNet.Attributes; +using Blockcore.Benchmark.Uint256; +using NBitcoin; +using uint256 = Blockcore.Benchmark.Uint256.New.uint256; + +namespace Blockcore.Benchmark.Uint256 +{ + [RankColumn, MemoryDiagnoser] + public class Uint256_Serialize + { + private readonly byte[] data; + private readonly New.uint256.MutableUint256 dataNew; + private readonly Old.uint256.MutableUint256 dataOld; + + public Uint256_Serialize() + { + this.data = new byte[32]; + new Random(32).NextBytes(this.data); + this.dataNew = new uint256.MutableUint256(new New.uint256(this.data)); + this.dataOld = new Old.uint256.MutableUint256(new Old.uint256(this.data)); + } + + [Benchmark] + public New.uint256 Uint256_Serialize_New() + { + using (var ms = new MemoryStream(this.data)) + this.dataNew.ReadWrite(new BitcoinStream(ms, true)); + return this.dataNew.Value; + } + + [Benchmark] + public Old.uint256 Uint256_Serialize_Old() + { + using (var ms = new MemoryStream(this.data)) + this.dataOld.ReadWrite(new BitcoinStream(ms, true)); + return this.dataOld.Value; + } + + [Benchmark] + public New.uint256 Uint256_Deserialize_New() + { + using (var ms = new MemoryStream(this.data)) + this.dataNew.ReadWrite(new BitcoinStream(ms, false)); + return this.dataNew.Value; + } + + [Benchmark] + public Old.uint256 Uint256_Deserialize_Old() + { + using (var ms = new MemoryStream(this.data)) + this.dataOld.ReadWrite(new BitcoinStream(ms, false)); + return this.dataOld.Value; + } + } +} \ No newline at end of file diff --git a/src/Blockcore.Benchmark/Uint256/Uint256_ToBytes.cs b/src/Blockcore.Benchmark/Uint256/Uint256_ToBytes.cs new file mode 100644 index 000000000..ccc707cd9 --- /dev/null +++ b/src/Blockcore.Benchmark/Uint256/Uint256_ToBytes.cs @@ -0,0 +1,47 @@ +using System; +using BenchmarkDotNet.Attributes; +using Blockcore.Benchmark.Uint256; +using Blockcore.Benchmark.Uint256.New; + +namespace Blockcore.Benchmark.Uint256 +{ + [RankColumn, MemoryDiagnoser] + public class Uint256_ToBytes + { + private readonly New.uint256 dataNew; + private readonly Old.uint256 dataOld; + + public Uint256_ToBytes() + { + var data = new byte[32]; + new Random(32).NextBytes(data); + + this.dataNew = new New.uint256(data); + this.dataOld = new Old.uint256(data); + } + + [Benchmark] + public byte[] Uint256_New() + { + return this.dataNew.ToBytes(); + } + + [Benchmark] + public byte[] Uint256_Old() + { + return this.dataOld.ToBytes(); + } + + [Benchmark] + public byte[] Uint256_New_le() + { + return this.dataNew.ToBytes(false); + } + + [Benchmark] + public byte[] Uint256_Old_le() + { + return this.dataOld.ToBytes(false); + } + } +} \ No newline at end of file diff --git a/src/Blockcore.sln b/src/Blockcore.sln index 9db1ba33e..8893f1342 100644 --- a/src/Blockcore.sln +++ b/src/Blockcore.sln @@ -96,6 +96,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stratis", "Stratis", "{0F6F EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bitcoin.Cli", "Bitcoin.Cli\Bitcoin.Cli.csproj", "{F163E8AD-41F2-42ED-BF37-BE5DD5F53C03}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blockcore.Benchmark", "Blockcore.Benchmark\Blockcore.Benchmark.csproj", "{03BE8A1E-81C4-4022-86C6-2FA8938C6208}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -246,6 +248,10 @@ Global {F163E8AD-41F2-42ED-BF37-BE5DD5F53C03}.Debug|Any CPU.Build.0 = Debug|Any CPU {F163E8AD-41F2-42ED-BF37-BE5DD5F53C03}.Release|Any CPU.ActiveCfg = Release|Any CPU {F163E8AD-41F2-42ED-BF37-BE5DD5F53C03}.Release|Any CPU.Build.0 = Release|Any CPU + {03BE8A1E-81C4-4022-86C6-2FA8938C6208}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03BE8A1E-81C4-4022-86C6-2FA8938C6208}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03BE8A1E-81C4-4022-86C6-2FA8938C6208}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03BE8A1E-81C4-4022-86C6-2FA8938C6208}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -288,6 +294,7 @@ Global {78DF115B-2471-41C3-8D9C-360720855CF5} = {3B56C02B-4468-4268-B797-851562789FCC} {0F6F0DC5-EC62-465B-8FC7-F24449CD7359} = {3B56C02B-4468-4268-B797-851562789FCC} {F163E8AD-41F2-42ED-BF37-BE5DD5F53C03} = {78DF115B-2471-41C3-8D9C-360720855CF5} + {03BE8A1E-81C4-4022-86C6-2FA8938C6208} = {1B9A916F-DDAC-4675-B424-EDEDC1A58231} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {6C780ABA-5872-4B83-AD3F-A5BD423AD907}