Browse Source

RandomHash2 - improved PoW algorithm

RandomHash2 with optimizations. Mostly working but perhaps issue with integration.
RandomHash2 achieves 4x CPU bias over GPU offering a far higher CPU verification rate.
Cryptographic hardness verified.
Herman Schoenfeld 6 years ago
parent
commit
7d889c9e13

+ 5 - 0
.gitignore

@@ -48,3 +48,8 @@ __history/
 __recovery/
 dunit.ini
 *.res
+/*.DS_Store
+/PIP/*.DS_Store
+/PIP/resources/*.DS_Store
+/PIP/resources/PIP-0034A/*.DS_Store
+/src/*.tvsconfig

+ 8 - 6
src/core/UAccounts.pas

@@ -838,12 +838,14 @@ begin
     ms.Write(operationBlock.timestamp,4);
     ms.Write(operationBlock.nonce,4);
     if CT_ACTIVATE_RANDOMHASH_V4 AND (operationBlock.protocol_version >= CT_PROTOCOL_4) then begin
-      if Assigned(_INTERNAL_PascalCoinProtocol) then begin
-        SetLength(LDigest,ms.Size);
-        Move(ms.Memory^,LDigest[0],ms.Size);
-        if _INTERNAL_PascalCoinProtocol.FPCHardcodedRandomHashTable.FindRandomHashByDigest(LDigest,PoW) then Exit;
-      end;
-      TCrypto.DoRandomHash(ms.Memory,ms.Size,PoW);
+      if (operationBlock.protocol_version < CT_PROTOCOL_5) then begin
+        if Assigned(_INTERNAL_PascalCoinProtocol) then begin
+          SetLength(LDigest,ms.Size);
+          Move(ms.Memory^,LDigest[0],ms.Size);
+          if _INTERNAL_PascalCoinProtocol.FPCHardcodedRandomHashTable.FindRandomHashByDigest(LDigest,PoW) then Exit;
+        end;
+        TCrypto.DoRandomHash(ms.Memory,ms.Size,PoW);
+      end else TCrypto.DoRandomHash2(ms.Memory,ms.Size,PoW);
     end else
       TCrypto.DoDoubleSha256(ms.Memory,ms.Size,PoW);
   finally

+ 6 - 4
src/core/UBlockChain.pas

@@ -1286,10 +1286,12 @@ begin
   FStreamPoW.WriteBuffer(FDigest_Part3[Low(FDigest_Part3)],Length(FDigest_Part3));
   FStreamPoW.Write(FOperationBlock.timestamp,4);
   FStreamPoW.Write(FOperationBlock.nonce,4);
-  if CT_ACTIVATE_RANDOMHASH_V4 AND (FOperationBlock.protocol_version >= CT_PROTOCOL_4) then
-    TCrypto.DoRandomHash(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW)
-  else
-    TCrypto.DoDoubleSha256(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW);
+  if CT_ACTIVATE_RANDOMHASH_V4 AND (FOperationBlock.protocol_version >= CT_PROTOCOL_4) then begin
+    if (FOperationBlock.protocol_version < CT_PROTOCOL_5) then
+      TCrypto.DoRandomHash(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW)
+    else
+      TCrypto.DoRandomHash2(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW);
+  end else TCrypto.DoDoubleSha256(FStreamPoW.Memory,length(FDigest_Part1)+length(FDigest_Part2_Payload)+length(FDigest_Part3)+8,PoW);
 end;
 
 procedure TPCOperationsComp.Calc_Digest_Parts;

+ 35 - 2
src/core/UCrypto.pas

@@ -39,7 +39,7 @@ uses
   ClpBigInteger,
   ClpCryptoLibTypes,
   {$ENDIF}
-  URandomHash, UBaseTypes, UPCDataTypes;
+  URandomHash, URandomHash2, UBaseTypes, UPCDataTypes;
 
 Type
   ECryptoException = Class(Exception);
@@ -87,8 +87,12 @@ Type
     class function DoDoubleSha256(const TheMessage : TRawBytes) : TRawBytes; overload;
     class procedure DoDoubleSha256(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
     class function DoRandomHash(const TheMessage : TRawBytes) : TRawBytes; overload;
+    class function DoRandomHash2(const TheMessage : TRawBytes) : TRawBytes; overload;
     class procedure DoRandomHash(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
+    class procedure DoRandomHash2(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
     class procedure DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
+    class procedure DoRandomHash2(AHasher : TRandomHash2; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
+   // class procedure DoRandomHash2(AHasher : TRandomHash2; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes); overload;
     class function DoRipeMD160_HEXASTRING(const TheMessage : TRawBytes) : TRawBytes; overload;
     class function DoRipeMD160AsRaw(p : PAnsiChar; plength : Cardinal) : TRawBytes; overload;
     class function DoRipeMD160AsRaw(const TheMessage : TRawBytes) : TRawBytes; overload;
@@ -152,7 +156,7 @@ Const
 implementation
 
 uses
-  ULog, UConst, UAccounts;
+  ULog, UConst, UAccounts, UCommon;
 
 Var _initialized : Boolean = false;
 
@@ -769,6 +773,11 @@ begin
   Result := TRandomHashFast.Compute(TheMessage);
 end;
 
+class function TCrypto.DoRandomHash2(const TheMessage: TRawBytes): TRawBytes;
+begin
+  Result := TRandomHash2.Compute(TheMessage);
+end;
+
 class procedure TCrypto.DoRandomHash(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
 var
   LInput : TBytes;
@@ -781,6 +790,18 @@ begin
   Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
 end;
 
+class procedure TCrypto.DoRandomHash2(p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
+var
+  LInput : TBytes;
+  LResult : TBytes;
+begin
+  if Length(ResultSha256) <> 32 then SetLength(ResultSha256, 32);
+  SetLength(LInput, plength);
+  Move(p^, LInput[0], plength);
+  LResult := TRandomHash2.Compute(LInput);
+  Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
+end;
+
 class procedure TCrypto.DoRandomHash(AFastHasher : TRandomHashFast; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
 var
   LInput : TBytes;
@@ -793,6 +814,18 @@ begin
   Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
 end;
 
+class procedure TCrypto.DoRandomHash2(AHasher : TRandomHash2; p : PAnsiChar; plength : Cardinal; out ResultSha256 : TRawBytes);
+var
+  LInput : TBytes;
+  LResult : TBytes;
+begin
+  if Length(ResultSha256) <> 32 then SetLength(ResultSha256, 32);
+  SetLength(LInput, plength);
+  Move(p^, LInput[0], plength);
+  LResult := AHasher.Hash(LInput);
+  Move(LResult[0], ResultSha256[Low(ResultSha256)], 32);
+end;
+
 { TBigNum }
 
 function TBigNum.Add(BN: TBigNum): TBigNum;

+ 93 - 24
src/core/UPoolMinerThreads.pas

@@ -25,7 +25,7 @@ interface
 {$I config.inc}
 
 uses
-  Classes, SysUtils, syncobjs, UThread, UPoolMining, UAccounts, UCrypto, ULog, UBlockChain, USha256, URandomHash, UBaseTypes, UCommon,
+  Classes, SysUtils, syncobjs, UThread, UPoolMining, UAccounts, UCrypto, ULog, UBlockChain, USha256, URandomHash, URandomHash2, UBaseTypes, UCommon,
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 type
@@ -157,6 +157,7 @@ Type
   private
     FCPUDeviceThread : TCPUDeviceThread;
     FLock : TCriticalSection;
+    FJobNum : Integer;
   protected
     FCurrentMinerValuesForWork : TMinerValuesForWork;
     FInternalSha256 : TSHA256HASH;
@@ -498,7 +499,10 @@ begin
   FLastDigest := digest;
   LUseRandomHash := TPoolMinerThread.UseRandomHash(usedMinerValuesForWork.version);
   if LUseRandomHash then
-    LHash := TCrypto.DoRandomHash(digest)
+    if (usedMinerValuesForWork.version >= CT_PROTOCOL_5) then
+      LHash := TCrypto.DoRandomHash2(digest)
+    else
+      LHash := TCrypto.DoRandomHash(digest)
   else
     LHash := TCrypto.DoSha256(TCrypto.DoSha256(digest));
   if (TBaseType.BinStrComp(LHash,usedMinerValuesForWork.target_pow)<=0) then begin
@@ -745,6 +749,7 @@ begin
       cpu := TCPUOpenSSLMinerThread(l[i]);
       cpu.FLock.Acquire;
       try
+        Inc(cpu.FJobNum);
         cpu.FCurrentMinerValuesForWork := FMinerValuesForWork;
         cpu.FInternalSha256 := sflc;
         cpu.FInternalChunk := lc;
@@ -781,17 +786,28 @@ end;
 { TCPUOpenSSLMinerThread }
 
 procedure TCPUOpenSSLMinerThread.BCExecute;
+
+type
+  TNonceResult = record
+    Nonce : UInt32;
+    PoW : TBytes;
+  end;
+
 Var
   ts : Cardinal;
-  i,roundsToDo : Integer;
+  i, j, roundsToDo, LRoundsPerformed : Integer;
   nonce : Cardinal;
   baseRealTC,baseHashingTC,finalHashingTC : TTickCount;
   resultPoW : TRawBytes;
-  //
+  LRoundJobNum : Integer;
   AuxStats : TMinerStats;
   dstep : Integer;
   LUseRandomHash : boolean;
   LRandomHasher : TRandomHashFast;
+  LRandomHasher2 : TRandomHash2;
+  LCachedItem : TRandomHash2.TCachedHash;
+  LNonceResult : TNonceResult;
+  LResultsToCheck : TList<TNonceResult>;
   LDisposables : TDisposables;
 begin
   DebugStep := '----------';
@@ -799,6 +815,8 @@ begin
   nonce := 0;
   dstep := 0;
   LRandomHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  LRandomHasher2 := LDisposables.AddObject( TRandomHash2.Create ) as TRandomHash2;
+  LResultsToCheck := LDisposables.AddObject( TList<TNonceResult>.Create ) as TList<TNonceResult>;
   Try
     while (Not Terminated) And (Not FCPUDeviceThread.Terminated) do begin
       Try
@@ -812,7 +830,10 @@ begin
         try
           LUseRandomHash := TPoolMinerThread.UseRandomHash(FCurrentMinerValuesForWork.version);
           if (LUseRandomHash) then begin
-            roundsToDo := 20;
+            if FCurrentMinerValuesForWork.version < CT_PROTOCOL_5 then
+              roundsToDo := 20
+            else
+              roundsToDo := 200;
           end else begin
             roundsToDo := 10000;
           end;
@@ -829,34 +850,81 @@ begin
 
           If FDigestStreamMsg.Size>8 then begin
             if FCPUDeviceThread.FUseOpenSSLFunctions OR LUseRandomHash then begin
+
+              // update timestamp
               FDigestStreamMsg.Position:=FDigestStreamMsg.Size - 8;
               FDigestStreamMsg.Write(ts,4);
+
               baseHashingTC:=TPlatform.GetTickCount;
               dstep := 4;
+              LRoundJobNum := FJobNum;
+              LRoundsPerformed := 0;
               for i := 1 to roundsToDo do begin
+                LResultsToCheck.Clear;
+                if LRoundJobNum <> FJobNum then
+                  break;
+
+                // write nonce
                 FDigestStreamMsg.Position := FDigestStreamMsg.Size - 4;
                 FDigestStreamMsg.Write(nonce,4);
+
+                // perform the hash
                 if LUseRandomHash then begin
-                  // Note if i > 1 then FDigestStreamMsg.Memory == LHasher.NextHeader (needs to be for CPU optimization to work)
-                  TCrypto.DoRandomHash(LRandomHasher,FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
-                end else
+                  if (FCurrentMinerValuesForWork.version < CT_PROTOCOL_5) then begin
+                    // Note if i > 1 then FDigestStreamMsg.Memory == LHasher.NextHeader (needs to be for CPU optimization to work)
+                    TCrypto.DoRandomHash(LRandomHasher,FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
+                    LNonceResult.Nonce := nonce;
+                    LNonceResult.PoW := resultPoW;
+                    LResultsToCheck.Add(LNonceResult);
+                    Inc(LRoundsPerformed);
+                  end else begin
+                    // Note if > 1 then FDigestStreamMsg.Memory == LRandomHash2.CachedHashes[0].Header (needs to be for CPU optimization to work)
+                    TCrypto.DoRandomHash2(LRandomHasher2, FDigestStreamMsg.Memory, FDigestStreamMsg.Size, resultPoW);
+                    LNonceResult.Nonce := nonce;
+                    LNonceResult.PoW := resultPoW;
+                    LResultsToCheck.Add(LNonceResult);
+                    Inc(LRoundsPerformed);
+                    while LRandomHasher2.HasCachedHash do begin
+                      LCachedItem := LRandomHasher2.PopCachedHash;
+                      LNonceResult.Nonce := LCachedItem.Nonce;
+                      LNonceResult.PoW := LCachedItem.Hash;
+                      LResultsToCheck.Add(LNonceResult);
+                      Inc(LRoundsPerformed);
+                    end;
+                  end
+                end else begin
                   TCrypto.DoDoubleSha256(FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
-                if (TBaseType.BinStrComp(resultPoW,FCurrentMinerValuesForWork.target_pow)<0) then begin
-                  if (Terminated) Or (FCPUDeviceThread.Terminated) then exit;
-                  dstep := 5;
-                  FLock.Release;
-                  try
-                    dstep := 6;
-                    FCPUDeviceThread.FoundNOnce(FCurrentMinerValuesForWork, ts, nonce);
-                    dstep := 7;
-                  finally
-                    FLock.Acquire;
+                  LNonceResult.Nonce := nonce;
+                  LNonceResult.PoW := resultPoW;
+                  LResultsToCheck.Add(LNonceResult);
+                  Inc(LRoundsPerformed);
+                end;
+
+                // check results
+                for j:= 0 to LResultsToCheck.Count - 1 do begin
+                  if (TBaseType.BinStrComp(LResultsToCheck[j].PoW,FCurrentMinerValuesForWork.target_pow)<0) then begin
+                    if (Terminated) Or (FCPUDeviceThread.Terminated) then exit;
+                    dstep := 5;
+                    FLock.Release;
+                    try
+                      dstep := 6;
+                      FCPUDeviceThread.FoundNOnce(FCurrentMinerValuesForWork, ts, LResultsToCheck[j].Nonce);
+                      dstep := 7;
+                    finally
+                      FLock.Acquire;
+                    end;
+                    dstep := 8;
                   end;
-                  dstep := 8;
                 end;
-                if LUseRandomHash then
-                  nonce := LRandomHasher.NextNonce
-                else if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
+
+                // select next nonce
+                if LUseRandomHash then begin
+                  if (FCurrentMinerValuesForWork.version < CT_PROTOCOL_5) then begin
+                    nonce := LRandomHasher.NextNonce;
+                  end else begin
+                    nonce := Random(FMaxNOnce - FMinNOnce) + FMinNOnce;
+                  end;
+                end else if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
               end;
               finalHashingTC:=TPlatform.GetTickCount;
             end else begin
@@ -872,12 +940,12 @@ begin
                     FLock.Acquire;
                   end;
                 end;
-                if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
+                if(nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
               end;
               finalHashingTC:=TPlatform.GetTickCount;
             end;
             AuxStats.Miners:=FCPUDeviceThread.FCPUs;
-            AuxStats.RoundsCount:=roundsToDo;
+            AuxStats.RoundsCount:=LRoundsPerformed;
             AuxStats.WorkingMillisecondsTotal:=TPlatform.GetTickCount - baseRealTC;
             AuxStats.WorkingMillisecondsHashing:= finalHashingTC - baseHashingTC;
             dstep := 9;
@@ -905,6 +973,7 @@ begin
   FDigestStreamMsg := TMemoryStream.Create;
   FMinNOnce := 0; FMaxNOnce:=$FFFFFFFF;
   FResetNOnce:=True;
+  FJobNum := 0;
   inherited Create(false);
 end;
 

+ 3 - 1
src/core/UPoolMining.pas

@@ -837,7 +837,9 @@ begin
             TLog.NewLog(ltInfo,ClassName,sJobInfo+' - Found a solution for block '+IntToStr(nbOperations.OperationBlock.block));
           end else TLog.NewLog(lterror,ClassName,sJobInfo+Format(' Calculated Pow > Min PoW ->  %s > %s',
             [TCrypto.ToHexaString(P^.OperationsComp.OperationBlock.proof_of_work),TCrypto.ToHexaString(_targetPoW)]));
-        end else TLog.NewLog(lterror,ClassName,sJobInfo+Format(' Timestamp %d < MinTimestamp %d',[_timestamp,P^.SentMinTimestamp]));
+        end else begin
+          TLog.NewLog(lterror,ClassName,sJobInfo+Format(' Timestamp %d < MinTimestamp %d',[_timestamp,P^.SentMinTimestamp]));
+        end;
         dec(i);
       end;
     Finally

+ 542 - 0
src/core/URandomHash2.pas

@@ -0,0 +1,542 @@
+unit URandomHash2;
+
+{ Copyright (c) 2018 by Herman Schoenfeld
+
+  RandomHash2 Reference Implementation
+
+  Distributed under the MIT software license, see the accompanying file LICENSE
+  or visit http://www.opensource.org/licenses/mit-license.php.
+
+  This unit is a part of the PascalCoin Project, an infinitely scalable
+  cryptocurrency. Find us here:
+  Web: https://www.pascalcoin.org
+  Source: https://github.com/PascalCoin/PascalCoin
+
+  THIS LICENSE HEADER MUST NOT BE REMOVED.
+}
+
+{%region Compiler Directives}
+{$IFDEF FPC}
+  {$UNDEF DELPHI}
+  {$MODE delphi}
+  {$DEFINE USE_UNROLLED_VARIANT}
+  {$OVERFLOWCHECKS OFF}
+  {$RANGECHECKS OFF}
+  {$POINTERMATH ON}
+  {$WARNINGS OFF}
+  {$HINTS OFF}
+  {$NOTES OFF}
+  {$OPTIMIZATION LEVEL3}
+  {$OPTIMIZATION PEEPHOLE}
+  {$OPTIMIZATION REGVAR}
+  {$OPTIMIZATION LOOPUNROLL}
+  {$OPTIMIZATION STRENGTH}
+  {$OPTIMIZATION CSE}
+  {$OPTIMIZATION DFA}
+  {$IFDEF CPUI386}
+    {$OPTIMIZATION USEEBP}
+  {$ENDIF}
+  {$IFDEF CPUX86_64}
+    {$OPTIMIZATION USERBP}
+  {$ENDIF}
+{$ELSE}
+  {$DEFINE USE_UNROLLED_VARIANT}
+  {$DEFINITIONINFO ON}
+  {$HINTS OFF}
+  {$OVERFLOWCHECKS OFF}
+  {$RANGECHECKS OFF}
+  {$POINTERMATH ON}
+  {$STRINGCHECKS OFF}
+  {$WARN DUPLICATE_CTOR_DTOR OFF}
+  // 2010 only
+  {$IF CompilerVersion = 21.0}
+  {$DEFINE DELPHI2010}
+  {$IFEND}
+  // 2010 and Above
+  {$IF CompilerVersion >= 21.0}
+  {$DEFINE DELPHI2010_UP}
+  {$IFEND}
+  // XE and Above
+  {$IF CompilerVersion >= 22.0}
+  {$DEFINE DELPHIXE_UP}
+  {$IFEND}
+  // XE2 and Above
+  {$IF CompilerVersion >= 23.0}
+  {$DEFINE DELPHIXE2_UP}
+  {$DEFINE HAS_UNITSCOPE}
+  {$IFEND}
+  // XE3 and Below
+  {$IF CompilerVersion <= 24.0}
+  {$DEFINE DELPHIXE3_DOWN}
+  {$IFEND}
+  // XE3 and Above
+  {$IF CompilerVersion >= 24.0}
+  {$DEFINE DELPHIXE3_UP}
+  {$LEGACYIFEND ON}
+  {$ZEROBASEDSTRINGS OFF}
+  {$IFEND}
+  // XE7 and Above
+  {$IF CompilerVersion >= 28.0}
+  {$DEFINE DELPHIXE7_UP}
+  {$IFEND}
+  // 10.2 Tokyo and Above
+  {$IF CompilerVersion >= 32.0}
+  {$DEFINE DELPHI10.2_TOKYO_UP}
+  {$IFEND}
+  // 2010 and Above
+  {$IFNDEF DELPHI2010_UP}
+  {$MESSAGE ERROR 'This Library requires Delphi 2010 or higher.'}
+  {$ENDIF}
+  // 10.2 Tokyo and Above
+  {$IFDEF DELPHI10.2_TOKYO_UP}
+  {$WARN COMBINING_SIGNED_UNSIGNED OFF}
+  {$WARN COMBINING_SIGNED_UNSIGNED64 OFF}
+  {$ENDIF}
+{$ENDIF}
+{%endregion}
+
+interface
+
+uses {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF}, SysUtils, HlpIHash, HlpBits, HlpHashFactory;
+
+type
+
+  { TRandomHash2 }
+
+  TRandomHash2 = class sealed(TObject)
+    const
+      MIN_N = 2; // Min-number of hashing rounds required to compute a nonce, min total rounds = J^MIN_N
+      MAX_N = 4; // Max-number of hashing rounds required to compute a nonce, max total rounds = J^MAX_N
+      MIN_J = 0; // Min-number of dependent neighbouring nonces required to evaluate a nonce round
+      MAX_J = 8; // Max-number of dependent neighbouring nonces required to evaluate a nonce round
+      M = 256;    // The memory expansion unit (in bytes), max total bytes per nonce = M * ((MAX_J+1)^MAX_N (MAX_N-2) + 2)
+      NUM_HASH_ALGO = 18;
+
+      public type
+
+        TCachedHash = record
+          Nonce : UInt32;
+          Header : TBytes;
+          Hash : TBytes;
+        end;
+
+      {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
+      FMurmurHash3_x86_32 : IHash;
+      FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
+      FCachedHeaderTemplate : TBytes;
+      FCachedHashes : TList<TCachedHash>;
+
+      function GetCachedHashes : TArray<TCachedHash>; inline;
+      function ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes; inline;
+      function MemTransform1(const AChunk: TBytes): TBytes; inline;
+      function MemTransform2(const AChunk: TBytes): TBytes; inline;
+      function MemTransform3(const AChunk: TBytes): TBytes; inline;
+      function MemTransform4(const AChunk: TBytes): TBytes; inline;
+      function MemTransform5(const AChunk: TBytes): TBytes; inline;
+      function MemTransform6(const AChunk: TBytes): TBytes; inline;
+      function MemTransform7(const AChunk: TBytes): TBytes; inline;
+      function MemTransform8(const AChunk: TBytes): TBytes; inline;
+      function Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32) : TBytes;
+      function Compress(const AInputs: TArray<TBytes>; ASeed : UInt32): TBytes; inline;
+      function SetLastDWordLE(const ABytes: TBytes; AValue: UInt32): TBytes; inline;
+      function GetLastDWordLE(const ABytes: TBytes) : UInt32; inline;
+      function ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes; inline;
+      function Hash(const ABlockHeader: TBytes; ARound: Int32; out AFoundLastRound : Int32) : TArray<TBytes>; overload;
+    public
+      constructor Create;
+      destructor Destroy; override;
+      property CachedHashes : TArray<TCachedHash> read GetCachedHashes;
+      function HasCachedHash : Boolean; inline;
+      function PopCachedHash : TCachedHash; inline;
+      function PeekCachedHash : TCachedHash; inline;
+      function Hash(const ABlockHeader: TBytes): TBytes; overload; inline;
+      class function Compute(const ABlockHeader: TBytes): TBytes; overload; static; inline;
+  end;
+
+ { ERandomHash2 }
+
+  ERandomHash2 = class(Exception);
+
+resourcestring
+  SUnSupportedHash = 'Unsupported Hash Selected';
+  SInvalidRound = 'Round must be between 0 and N inclusive';
+  SOverlappingArgs = 'Overlapping read/write regions';
+  SBufferTooSmall = 'Buffer too small to apply memory transform';
+  SBlockHeaderTooSmallForNonce = 'Buffer too small to contain nonce';
+
+implementation
+
+uses UCommon, UMemory, URandomHash;
+
+{ TRandomHash2 }
+
+constructor TRandomHash2.Create;
+begin
+  FMurmurHash3_x86_32 := THashFactory.THash32.CreateMurmurHash3_x86_32();
+  SetLength(Self.FCachedHeaderTemplate, 0);
+  FCachedHashes := TList<TCachedHash>.Create;
+  FHashAlg[0] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[1] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[2] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[4] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[5] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[6] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[7] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[9] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[10] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[11] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[12] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[13] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[15] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[16] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[17] := THashFactory.TCrypto.CreateWhirlPool();
+end;
+
+destructor TRandomHash2.Destroy;
+var i : integer;
+begin
+ FCachedHashes.Clear;
+ FreeAndNil(FCachedHashes);
+ FMurmurHash3_x86_32 := nil;
+ for i := Low(FHashAlg) to High(FHashAlg) do
+   FHashAlg[i] := nil;
+ inherited Destroy;
+end;
+
+class function TRandomHash2.Compute(const ABlockHeader: TBytes): TBytes;
+var
+  LHasher : TRandomHash2;
+  LDisposables : TDisposables;
+begin
+ LHasher := LDisposables.AddObject( TRandomHash2.Create ) as TRandomHash2;
+ Result := LHasher.Hash(ABlockHeader);
+end;
+
+function TRandomHash2.Hash(const ABlockHeader: TBytes): TBytes;
+var
+  LAllOutputs: TArray<TBytes>;
+  LSeed: UInt32;
+  LLastRound : Int32;
+begin
+  LLastRound := 0;
+  LAllOutputs := Hash(ABlockHeader, MAX_N, LLastRound);
+  if LLastRound <= 0 then
+    raise ERandomHash2.Create('Internal Error: 984F52997131417E8D63C43BD686F5B2'); // Should have found final round!
+  Result := ComputeVeneerRound(LAllOutputs);
+end;
+
+function TRandomHash2.ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes;
+var
+  LSeed : UInt32;
+begin
+  LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
+  // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
+  Result := FHashAlg[0].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
+end;
+
+function TRandomHash2.Hash(const ABlockHeader: TBytes; ARound: Int32; out AFoundLastRound : Int32) : TArray<TBytes>;
+var
+  LRoundOutputs: TList<TBytes>;
+  LNeighbourLastRound : Int32;
+  LSeed, LNumNeighbours: UInt32;
+  LGen: TMersenne32;
+  LRoundInput, LNeighbourNonceHeader, LOutput : TBytes;
+  LCachedHash : TCachedHash;
+  LParentOutputs, LNeighborOutputs, LToArray, LBuffs2: TArray<TBytes>;
+  LHashFunc: IHash;
+  i: Int32;
+  LDisposables : TDisposables;
+  LBuff : TBytes;
+begin
+  if (ARound < 1) or (ARound > MAX_N) then
+    raise EArgumentOutOfRangeException.CreateRes(@SInvalidRound);
+
+  LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
+  LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
+  if ARound = 1 then begin
+    LRoundInput := FHashAlg[0].ComputeBytes(ABlockHeader).GetBytes;
+    LSeed := GetLastDWordLE( LRoundInput );
+    LGen.Initialize(LSeed);
+  end else begin
+    LParentOutputs := Hash(ABlockHeader, ARound - 1, AFoundLastRound);
+    if AFoundLastRound > 0 then
+      // Previous round was the final round, so just return it's value
+      Exit(LParentOutputs);
+
+    // Add parent round outputs to this round outputs
+    LSeed := GetLastDWordLE( LParentOutputs[High(LParentOutputs)] );
+    LGen.Initialize(LSeed);
+    LRoundOutputs.AddRange( LParentOutputs );
+
+    // Add neighbouring nonce outputs to this round outputs
+    LNumNeighbours := (LGen.NextUInt32 MOD (MAX_J - MIN_J)) + MIN_J;
+    for i := 1 to LNumNeighbours do begin
+      LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
+      LNeighborOutputs := Hash(LNeighbourNonceHeader, ARound - 1, LNeighbourLastRound);
+      LRoundOutputs.AddRange(LNeighborOutputs);
+
+      // If neighbour was a fully evaluated nonce, cache it for re-use
+      if LNeighbourLastRound > 0 then begin
+        LCachedHash.Nonce := GetLastDWordLE(LNeighbourNonceHeader);
+        LCachedHash.Header := LNeighbourNonceHeader;
+        LCachedHash.Hash := ComputeVeneerRound(LNeighborOutputs);
+        // if header is different (other than nonce), clear cache
+        if NOT BytesEqual(FCachedHeaderTemplate, LCachedHash.Header, 0, 32 - 4) then begin
+          FCachedHashes.Clear;
+          FCachedHeaderTemplate := SetLastDWordLE(LCachedHash.Header, 0);
+        end;
+        FCachedHashes.Add(LCachedHash);
+      end;
+    end;
+    // Compress the parent/neighbouring outputs to form this rounds input
+    LRoundInput := Compress( LRoundOutputs.ToArray, LGen.NextUInt32 );
+  end;
+
+  // Select a random hash function and hash the input to find the output
+  LHashFunc := FHashAlg[LGen.NextUInt32 mod NUM_HASH_ALGO];
+  LOutput := LHashFunc.ComputeBytes(LRoundInput).GetBytes;
+
+  // Memory-expand the output, add to output list and return output list
+  LOutput := Expand(LOutput, MAX_N - ARound, LGen.NextUInt32);
+  LRoundOutputs.Add(LOutput);
+  Result := LRoundOutputs.ToArray;
+
+  // Determine if final round
+  if (ARound = MAX_N) OR ((ARound >= MIN_N) AND (GetLastDWordLE(LOutput) MOD MAX_N = 0)) then
+    AFoundLastRound := ARound
+  else AFoundLastRound := 0;
+end;
+
+function TRandomHash2.SetLastDWordLE(const ABytes: TBytes; AValue: UInt32): TBytes;
+var
+  ABytesLength : Integer;
+begin
+  // Clone the original header
+  Result := Copy(ABytes);
+
+  // If digest not big enough to contain a nonce, just return the clone
+  ABytesLength := Length(ABytes);
+  if ABytesLength < 4 then
+    exit;
+
+  // Overwrite the nonce in little-endian
+  Result[ABytesLength - 4] := Byte(AValue);
+  Result[ABytesLength - 3] := (AValue SHR 8) AND 255;
+  Result[ABytesLength - 2] := (AValue SHR 16) AND 255;
+  Result[ABytesLength - 1] := (AValue SHR 24) AND 255;
+end;
+
+function TRandomHash2.GetLastDWordLE(const ABytes: TBytes) : UInt32;
+var LLen : Integer;
+begin
+  LLen := Length(ABytes);
+  if LLen < 4 then
+   raise EArgumentOutOfRangeException.CreateRes(@SBlockHeaderTooSmallForNonce);
+
+  // Last 4 bytes are nonce (LE)
+  Result := ABytes[LLen - 4] OR
+           (ABytes[LLen - 3] SHL 8) OR
+           (ABytes[LLen - 2] SHL 16) OR
+           (ABytes[LLen - 1] SHL 24);
+end;
+
+
+function TRandomHash2.Compress(const AInputs : TArray<TBytes>; ASeed : UInt32): TBytes;
+var
+  i: Int32;
+  LSource: TBytes;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  SetLength(Result, 100);
+  LGen := LDisposables.AddObject( TMersenne32.Create( ASeed ) ) as TMersenne32;
+  for i := 0 to 99 do
+  begin
+    LSource := AInputs[LGen.NextUInt32 mod Length(AInputs)];
+    Result[i] := LSource[LGen.NextUInt32 mod Length(LSource)];
+  end;
+end;
+
+function TRandomHash2.GetCachedHashes : TArray<TCachedHash>;
+begin
+  Result := FCachedHashes.ToArray;
+end;
+
+function TRandomHash2.HasCachedHash : Boolean;
+begin
+  Result := FCachedHashes.Count > 0;
+end;
+
+function TRandomHash2.PopCachedHash : TCachedHash;
+begin
+  Result := FCachedHashes.Last;
+  FCachedHashes.Delete(FCachedHashes.Count - 1);
+end;
+
+function TRandomHash2.PeekCachedHash : TCachedHash;
+begin
+  Result := FCachedHashes.Last;
+end;
+
+function TRandomHash2.ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes;
+begin
+  SetLength(Result, Length(AChunk1) + Length(AChunk2));
+  Move(AChunk1[0], Result[0], Length(AChunk1));
+  Move(AChunk2[0], Result[Length(AChunk1)], Length(AChunk2));
+end;
+
+function TRandomHash2.MemTransform1(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength : UInt32;
+  LState : UInt32;
+begin
+  // Seed XorShift32 with last byte
+  LState := GetLastDWordLE(AChunk);
+  if LState = 0 then
+    LState := 1;
+
+  // Select random bytes from input using XorShift32 RNG
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := AChunk[TXorShift32.Next(LState) MOD LChunkLength];
+end;
+
+function TRandomHash2.MemTransform2(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  Move(AChunk[LPivot + LOdd], Result[0], LPivot);
+  Move(AChunk[0], Result[LPivot + LOdd], LPivot);
+  // Set middle-byte for odd-length arrays
+  if LOdd = 1 then
+    Result[LPivot] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform3(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := AChunk[LChunkLength - i - 1];
+end;
+
+function TRandomHash2.MemTransform4(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := 0 to Pred(LPivot) do
+  begin
+    Result[(i * 2)] := AChunk[i];
+    Result[(i * 2) + 1] := AChunk[i + LPivot + LOdd];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[High(Result)] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform5(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := Low(AChunk) to Pred(LPivot) do
+  begin
+    Result[(i * 2)] := AChunk[i + LPivot + LOdd];
+    Result[(i * 2) + 1] := AChunk[i];
+  end;
+  // Set final byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[High(Result)] := AChunk[LPivot];
+end;
+
+function TRandomHash2.MemTransform6(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength, LPivot, LOdd: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  LPivot := LChunkLength SHR 1;
+  LOdd := LChunkLength MOD 2;
+  SetLength(Result, LChunkLength);
+  for i := 0 to Pred(LPivot) do
+  begin
+    Result[i] := AChunk[(i * 2)] xor AChunk[(i * 2) + 1];
+    Result[i + LPivot + LOdd] := AChunk[i] xor AChunk[LChunkLength - i - 1];
+  end;
+  // Set middle-byte for odd-lengths
+  if LOdd = 1 THEN
+    Result[LPivot] := AChunk[High(AChunk)];
+end;
+
+function TRandomHash2.MemTransform7(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := TBits.RotateLeft8(AChunk[i], LChunkLength - i);
+end;
+
+function TRandomHash2.MemTransform8(const AChunk: TBytes): TBytes;
+var
+  i, LChunkLength: Int32;
+begin
+  LChunkLength := Length(AChunk);
+  SetLength(Result, LChunkLength);
+  for i := 0 to High(AChunk) do
+    Result[i] := TBits.RotateRight8(AChunk[i], LChunkLength - i);
+end;
+
+function TRandomHash2.Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32): TBytes;
+var
+  LSize, LBytesToAdd: Int32;
+  LOutput, LNextChunk: TBytes;
+  LRandom: UInt32;
+  LGen: TMersenne32;
+  LDisposables : TDisposables;
+begin
+  LGen := LDisposables.AddObject( TMersenne32.Create (ASeed) ) as TMersenne32;
+  LSize := Length(AInput) + (AExpansionFactor * M);
+  LOutput := Copy(AInput);
+  LBytesToAdd := LSize - Length(AInput);
+
+  while LBytesToAdd > 0 do
+  begin
+    LNextChunk := Copy(LOutput);
+    if Length(LNextChunk) > LBytesToAdd then
+      SetLength(LNextChunk, LBytesToAdd);
+
+    LRandom := LGen.NextUInt32;
+    case LRandom mod 8 of
+      0: LOutput := ContencateByteArrays(LOutput, MemTransform1(LNextChunk));
+      1: LOutput := ContencateByteArrays(LOutput, MemTransform2(LNextChunk));
+      2: LOutput := ContencateByteArrays(LOutput, MemTransform3(LNextChunk));
+      3: LOutput := ContencateByteArrays(LOutput, MemTransform4(LNextChunk));
+      4: LOutput := ContencateByteArrays(LOutput, MemTransform5(LNextChunk));
+      5: LOutput := ContencateByteArrays(LOutput, MemTransform6(LNextChunk));
+      6: LOutput := ContencateByteArrays(LOutput, MemTransform7(LNextChunk));
+      7: LOutput := ContencateByteArrays(LOutput, MemTransform8(LNextChunk));
+    end;
+    LBytesToAdd := LBytesToAdd - Length(LNextChunk);
+  end;
+  Result := LOutput;
+end;
+
+end.

+ 65 - 0
src/gui-classic/UFRMDiagnosticTool.dfm

@@ -0,0 +1,65 @@
+object FRMDiagnosticTool: TFRMDiagnosticTool
+  Left = 0
+  Top = 0
+  Caption = 'FRMDiagnosticTool'
+  ClientHeight = 324
+  ClientWidth = 735
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = False
+  OnCreate = FormCreate
+  DesignSize = (
+    735
+    324)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object btnRH: TButton
+    Left = 8
+    Top = 8
+    Width = 130
+    Height = 25
+    Caption = 'Start Random Hash'
+    TabOrder = 0
+    OnClick = btnRHClick
+  end
+  object btnRH2: TButton
+    Left = 144
+    Top = 8
+    Width = 130
+    Height = 25
+    Caption = 'Start Random Hash 2'
+    TabOrder = 1
+    OnClick = btnRH2Click
+  end
+  object txtLog: TMemo
+    Left = 8
+    Top = 39
+    Width = 719
+    Height = 277
+    Anchors = [akLeft, akTop, akRight, akBottom]
+    TabOrder = 2
+    ExplicitWidth = 554
+  end
+  object btnRHC: TButton
+    Left = 280
+    Top = 8
+    Width = 186
+    Height = 25
+    Caption = 'Start Random Hash 2 (Cached)'
+    TabOrder = 3
+    OnClick = btnRHCClick
+  end
+  object btnEntropy: TButton
+    Left = 472
+    Top = 8
+    Width = 186
+    Height = 25
+    Caption = 'Entropy Tests'
+    TabOrder = 4
+    OnClick = btnEntropyClick
+  end
+end

+ 67 - 0
src/gui-classic/UFRMDiagnosticTool.lfm

@@ -0,0 +1,67 @@
+object FRMDiagnosticTool: TFRMDiagnosticTool
+  Left = 0
+  Height = 486
+  Top = 0
+  Width = 855
+  Caption = 'FRMDiagnosticTool'
+  ClientHeight = 522
+  ClientWidth = 765
+  Color = clBtnFace
+  DesignTimePPI = 144
+  Font.Color = clWindowText
+  Font.Height = -17
+  Font.Name = 'Tahoma'
+  OnCreate = FormCreate
+  LCLVersion = '2.1.0.0'
+  object btnRH: TButton
+    Left = 12
+    Height = 38
+    Top = 12
+    Width = 195
+    Caption = 'Start Random Hash'
+    Font.Color = clWindowText
+    Font.Height = -17
+    Font.Name = 'Tahoma'
+    OnClick = btnRHClick
+    ParentFont = False
+    TabOrder = 0
+  end
+  object btnRH2: TButton
+    Left = 216
+    Height = 38
+    Top = 12
+    Width = 195
+    Caption = 'Start Random Hash 2'
+    Font.Color = clWindowText
+    Font.Height = -17
+    Font.Name = 'Tahoma'
+    OnClick = btnRH2Click
+    ParentFont = False
+    TabOrder = 1
+  end
+  object txtLog: TMemo
+    Left = 12
+    Height = 236
+    Top = 58
+    Width = 591
+    Anchors = [akTop, akLeft, akRight, akBottom]
+    Font.Color = clWindowText
+    Font.Height = -17
+    Font.Name = 'Tahoma'
+    ParentFont = False
+    TabOrder = 2
+  end
+  object btnRHC: TButton
+    Left = 420
+    Height = 38
+    Top = 12
+    Width = 279
+    Caption = 'Start Random Hash 2 (Cached)'
+    Font.Color = clWindowText
+    Font.Height = -17
+    Font.Name = 'Tahoma'
+    OnClick = btnRHCClick
+    ParentFont = False
+    TabOrder = 3
+  end
+end

+ 293 - 0
src/gui-classic/UFRMDiagnosticTool.pas

@@ -0,0 +1,293 @@
+unit UFRMDiagnosticTool;
+
+interface
+
+uses
+  Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls,
+  SysUtils, Variants, Classes, UThread, UMemory, URandomHash, URandomHash2,
+  {$IFNDEF FPC}
+  System.TimeSpan,
+  {$ENDIF}
+  UCommon;
+
+type
+  TFRMDiagnosticTool = class(TForm)
+    btnRH: TButton;
+    btnRH2: TButton;
+    txtLog: TMemo;
+    btnRHC: TButton;
+    btnEntropy: TButton;
+    procedure btnRH2Click(Sender: TObject);
+    procedure btnRHClick(Sender: TObject);
+    procedure FormCreate(Sender: TObject);
+    procedure btnRHCClick(Sender: TObject);
+    procedure btnEntropyClick(Sender: TObject);
+  private
+    { Private declarations }
+    FDisposables : TDisposables;
+    FRHThread : TPCThread;
+    FRH2Thread : TPCThread;
+    FRH2CachedThread : TPCThread;
+    procedure OnRandomHashReport(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+    procedure OnRandomHash2Report(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+    procedure OnRandomHash2CachedReport(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+  public
+    { Public declarations }
+  end;
+
+  { TAlgorithmNotify }
+
+  TAlgorithmNotify  = procedure (ATotalHashes : UInt32; const ATimeSpan : TTimeSpan) of object;
+
+  { TAlgorithmThread}
+
+  TAlgorithmThread = class(TPCThread)
+    private
+      FDisposables : TDisposables;
+      FNotifyHashCount : UInt32;
+      FNotifyDuration : TTimeSpan;
+      FNotify : TAlgorithmNotify;
+      procedure ThreadSafeNotify;
+    protected
+      FLastHash : TBytes;
+      procedure BCExecute; override;
+      function NextRound : TBytes; virtual; abstract;
+    public
+      property Notify : TAlgorithmNotify read FNotify write FNotify;
+      constructor Create; virtual;
+  end;
+
+  { TRandomHashThread }
+
+  TRandomHashThread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHashFast;
+    protected
+      function NextRound : TBytes; override;
+    public
+      constructor Create; override;
+  end;
+
+  { TRandomHash2Thread }
+
+  TRandomHash2Thread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash2;
+    protected
+      function NextRound : TBytes; override;
+    public
+      constructor Create; override;
+  end;
+
+  { TRandomHash2CachedThread }
+
+  TRandomHash2CachedThread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash2;
+    protected
+      function NextRound : TBytes; override;
+    public
+      constructor Create; override;
+  end;
+
+implementation
+
+uses UBaseTypes, UCrypto;
+
+{$R *.dfm}
+
+{ TAlgorithmThread }
+
+constructor TAlgorithmThread.Create;
+begin
+  Inherited Create(True);
+  SetLength(FLastHash, 32);
+end;
+
+procedure TAlgorithmThread.BCExecute;
+var
+ LTC : TTickCount;
+ LStartTime : TDateTime;
+ LTotalHashes : UInt32;
+begin
+  LTotalHashes := 0;
+  LStartTime := Now;
+  LTC := TPlatform.GetTickCount;
+  while True do begin
+   FLastHash := NextRound;
+   inc(LTotalHashes);
+   if TPlatform.GetElapsedMilliseconds(LTC)>2500 then begin
+     FNotifyDuration := TTimeSpan.Subtract(Now, LStartTime);
+     FNotifyHashCount := LTotalHashes;
+     Queue( ThreadSafeNotify );
+     LTotalHashes := 0;
+     LStartTime := Now;
+     LTC := TPlatform.GetTickCount;
+   end;
+  end;
+end;
+
+procedure TAlgorithmThread.ThreadSafeNotify;
+begin
+  if Assigned(FNotify) then
+    FNotify(FNotifyHashCount, FNotifyDuration);
+end;
+
+{ TRandomHashThread }
+
+constructor TRandomHashThread.Create;
+begin
+  Inherited Create;
+  FHasher := TRandomHashFast.Create;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHashThread.NextRound : TBytes;
+begin
+   Result := FHasher.Hash(FLastHash);
+end;
+
+{ TRandomHash2Thread }
+
+constructor TRandomHash2Thread.Create;
+begin
+  Inherited Create;
+  FHasher := TRandomHash2.Create;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHash2Thread.NextRound : TBytes;
+begin
+  Result := FHasher.Hash(FLastHash);
+end;
+
+{ TRandomHash2CachedThread }
+
+constructor TRandomHash2CachedThread.Create;
+begin
+  Inherited Create;
+  FHasher := TRandomHash2.Create;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHash2CachedThread.NextRound : TBytes;
+begin
+  if FHasher.HasCachedHash then
+    Result := FHasher.PopCachedHash.Hash
+  else
+    Result := FHasher.Hash(FLastHash);
+end;
+
+
+
+{ TFRMDiagnosicTool }
+
+procedure TFRMDiagnosticTool.FormCreate(Sender: TObject);
+begin
+  FRH2CachedThread := TRandomHash2CachedThread.Create;
+  FRH2Thread := TRandomHash2Thread.Create;
+  FRHThread := TRandomHashThread.Create;
+  FDisposables.AddObject(FRHThread);
+  FDisposables.AddObject(FRH2Thread);
+  FDisposables.AddObject(FRH2CachedThread);
+
+  TRandomHashThread(FRHThread).Notify := OnRandomHashReport;
+  TRandomHash2Thread(FRH2Thread).Notify := OnRandomHash2Report;
+  TRandomHash2CachedThread(FRH2CachedThread).Notify := OnRandomHash2CachedReport;
+end;
+
+procedure TFRMDiagnosticTool.btnRHClick(Sender: TObject);
+begin
+  if FRHThread.Suspended then begin
+    FRHThread.Suspended := False;
+    btnRH.Caption := 'Stop Random Hash';
+  end else begin
+    FRHThread.Suspended := True;
+    btnRH.Caption := 'Start Random Hash';
+  end;
+end;
+
+procedure TFRMDiagnosticTool.btnEntropyClick(Sender: TObject);
+
+  procedure SetLastDWordLE(var ABytes: TBytes;  AValue: UInt32);
+  var
+    LHeaderLength : Integer;
+  begin
+    // NOTE: NONCE is last 4 bytes of header!
+
+    // If digest not big enough to contain a nonce, just return the clone
+    LHeaderLength := Length(ABytes);
+    if LHeaderLength < 4 then
+      exit;
+
+    // Overwrite the nonce in little-endian
+    ABytes[LHeaderLength - 4] := Byte(AValue);
+    ABytes[LHeaderLength - 3] := (AValue SHR 8) AND 255;
+    ABytes[LHeaderLength - 2] := (AValue SHR 16) AND 255;
+    ABytes[LHeaderLength - 1] := (AValue SHR 24) AND 255;
+  end;
+
+
+var LIn, LOut : TBytes; i : Integer; TXT : String;
+begin
+  SetLength(LIn, 200);
+  FillChar(LIn, 200, 1);
+
+  TXT := '';
+  for I := 1 to 10 do begin
+    LOut := TRandomHash2.Compute(LIn);
+    TXT := TXT + Format('RH2( %s ) = %s %s', [ TCrypto.ToHexaString(LIn), TCrypto.ToHexaString(LOut), sLineBreak]);
+    SetLastDWordLE(LIn, I);
+  end;
+
+  txtLog.Text := TXT;
+
+end;
+
+procedure TFRMDiagnosticTool.btnRH2Click(Sender: TObject);
+begin
+  if FRH2Thread.Suspended then begin
+    FRH2Thread.Suspended := False;
+    btnRH2.Caption := 'Stop Random Hash 2';
+  end else begin
+    FRH2Thread.Suspended := True;
+    btnRH2.Caption := 'Start Random Hash 2';
+  end;
+end;
+
+procedure TFRMDiagnosticTool.btnRHCClick(Sender: TObject);
+begin
+  if FRH2CachedThread.Suspended then begin
+    FRH2CachedThread.Suspended := False;
+    btnRHC.Caption := 'Stop Random Hash 2 (Cached)';
+  end else begin
+    FRH2CachedThread.Suspended := True;
+    btnRHC.Caption := 'Start Random Hash 2 (Cached)';
+  end;
+end;
+
+procedure TFRMDiagnosticTool.OnRandomHashReport(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+var
+ LHPS : Double;
+begin
+  LHPS := Trunc(ATotalHashes) / ATimeSpan.TotalSeconds;
+  txtLog.Text := txtLog.Text + Format('Random Hash: %n H/S%s', [LHPS, sLineBreak]);
+end;
+
+procedure TFRMDiagnosticTool.OnRandomHash2Report(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+var
+ LHPS : Double;
+begin
+  LHPS := Trunc(ATotalHashes) / ATimeSpan.TotalSeconds;
+  txtLog.Text := txtLog.Text + Format('Random Hash 2: %n H/S%s', [LHPS, sLineBreak]);
+end;
+
+procedure TFRMDiagnosticTool.OnRandomHash2CachedReport(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+var
+ LHPS : Double;
+begin
+  LHPS := Trunc(ATotalHashes) / ATimeSpan.TotalSeconds;
+  txtLog.Text := txtLog.Text + Format('Random Hash 2 (Cached): %n H/S%s', [LHPS, sLineBreak]);
+end;
+
+end.

+ 3 - 0
src/gui-classic/UFRMDiagnosticTool.vlb

@@ -0,0 +1,3 @@
+[]
+Coordinates=0,0,91,36
+

+ 44 - 0
src/gui-classic/UFRMRandomHashUtil.dfm

@@ -0,0 +1,44 @@
+object FRMDiagnosticTool: TFRMDiagnosticTool
+  Left = 0
+  Top = 0
+  Caption = 'FRMDiagnosticTool'
+  ClientHeight = 324
+  ClientWidth = 570
+  Color = clBtnFace
+  Font.Charset = DEFAULT_CHARSET
+  Font.Color = clWindowText
+  Font.Height = -11
+  Font.Name = 'Tahoma'
+  Font.Style = []
+  OldCreateOrder = False
+  DesignSize = (
+    570
+    324)
+  PixelsPerInch = 96
+  TextHeight = 13
+  object txtLog: TEdit
+    Left = 8
+    Top = 39
+    Width = 554
+    Height = 21
+    Anchors = [akLeft, akTop, akRight, akBottom]
+    TabOrder = 0
+    Text = 'txtLog'
+  end
+  object btnRH: TButton
+    Left = 8
+    Top = 8
+    Width = 105
+    Height = 25
+    Caption = 'Random Hash'
+    TabOrder = 1
+  end
+  object btnRH2: TButton
+    Left = 119
+    Top = 8
+    Width = 98
+    Height = 25
+    Caption = 'Random Hash 2'
+    TabOrder = 2
+  end
+end

+ 127 - 0
src/gui-classic/UFRMRandomHashUtil.pas

@@ -0,0 +1,127 @@
+unit UFRMRandomHashUtil;
+
+interface
+
+uses
+  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
+  System.TimeSpan, UThread, UMemory, URandomHash, URandomHash2,
+  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
+
+type
+  TFRMDiagnosticTool = class(TForm)
+    txtLog: TEdit;
+    btnRH: TButton;
+    btnRH2: TButton;
+  private
+    { Private declarations }
+    TRHThread : TPCThread;
+    TRH2Thread : TPCThread;
+  public
+    { Public declarations }
+  end;
+
+  TRandomHashNotify  = procedure (ATotalHashes : UInt32; const ATimeSpan : TTimeSpan) of object;
+
+  TAlgorithmThread = class(TPCThread)
+    private
+      FDisposables : TDisposables;
+      FNotifyHashCount : UInt32;
+      FNotifyDuration : TTimeSpan;
+      FNotify : TRandomHashNotify;
+      procedure ThreadSafeNotify;
+    protected
+      FLastHash : TBytes;
+      procedure BCExecute; override;
+      function NextRound : TBytes; virtual; abstract;
+    public
+      constructor Create; virtual;
+  end;
+
+  TRandomHashThread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash;
+    protected
+      function NextRound : TBytes; override;
+    public
+      constructor Create; override;
+  end;
+
+  TRandomHash2Thread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash2;
+    protected
+      function NextRound : TBytes; override;
+    public
+      constructor Create; override;
+  end;
+
+implementation
+
+uses UBaseTypes;
+
+{$R *.dfm}
+
+{ TAlgorithmThread }
+
+constructor TAlgorithmThread.Create;
+begin
+  Inherited Create(True);
+end;
+
+procedure TAlgorithmThread.BCExecute;
+var
+ LTC : TTickCount;
+ LStartTime : TDateTime;
+ LTotalHashes : UInt32;
+begin
+  LTotalHashes := 0;
+  LStartTime := Now;
+  LTC := TPlatform.GetTickCount;
+  while True do begin
+   FLastHash := NextRound;
+   inc(LTotalHashes);
+   if TPlatform.GetElapsedMilliseconds(LTC)>1000 then begin
+     FNotifyDuration := TTimeSpan.Subtract(Now, LStartTime);
+     FNotifyHashCount := LTotalHashes;
+     Queue( ThreadSafeNotify );
+     LTotalHashes := 0;
+     LStartTime := Now;
+     LTC := TPlatform.GetTickCount;
+   end;
+  end;
+end;
+
+procedure TAlgorithmThread.ThreadSafeNotify;
+begin
+  FNotify(FNotifyHashCount, FNotifyDuration);
+end;
+
+{ TRandomHashThread }
+
+constructor TRandomHashThread.Create;
+begin
+  FHasher := TRandomHash.Create;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHashThread.NextRound : TBytes;
+begin
+   Result := FHasher.Hash(FLastHash);
+end;
+
+{ TRandomHash2Thread }
+
+constructor TRandomHash2Thread.Create;
+begin
+  FHasher := TRandomHash2.Create;
+  FDisposables.AddObject(FHasher);
+end;
+
+function TRandomHash2Thread.NextRound : TBytes;
+begin
+   Result := FHasher.Hash(FLastHash);
+end;
+
+{ TFRMDiagnosicTool }
+
+end.

+ 8 - 0
src/gui-classic/UFRMWallet.dfm

@@ -421,6 +421,10 @@ object FRMWallet: TFRMWallet
     OnChange = PageControlChange
     object tsMyAccounts: TTabSheet
       Caption = 'Account Explorer'
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Splitter1: TSplitter
         Left = 400
         Top = 66
@@ -658,6 +662,10 @@ object FRMWallet: TFRMWallet
         TabOrder = 2
         object tsAccountOperations: TTabSheet
           Caption = 'Account Operations'
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgAccountOperations: TDrawGrid
             Left = 0
             Top = 0

+ 19 - 1
src/gui-classic/UFRMWallet.pas

@@ -228,6 +228,7 @@ type
     procedure MiFindOperationbyOpHashClick(Sender: TObject);
     procedure MiAccountInformationClick(Sender: TObject);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
+    procedure Test_ShowDiagnosticTool(Sender: TObject);
   private
     FLastNodesCacheUpdatedTS : TDateTime;
     FBackgroundPanel : TPanel;
@@ -329,7 +330,7 @@ Uses UFolderHelper,
 {$ENDIF}
   UTime, UFileStorage,
   UThread, UOpTransaction, UFRMPascalCoinWalletConfig,
-  UFRMOperationsExplorer,
+  UFRMOperationsExplorer, UFRMDiagnosticTool,
   {$IFDEF TESTNET}
   UFRMRandomOperations,
   UPCTNetDataExtraMessages,
@@ -1077,6 +1078,11 @@ begin
   mi.Caption:='Ask for Free Account';
   mi.OnClick:=Test_AskForFreeAccount;
   miAbout.Add(mi);
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Diagnostic Tool';
+  mi.OnClick:=Test_ShowDiagnosticTool;
+  miAbout.Add(mi);
+
 end;
 
 {$IFDEF TESTING_NO_POW_CHECK}
@@ -1646,6 +1652,18 @@ begin
   end;
 end;
 
+procedure TFRMWallet.Test_ShowDiagnosticTool(Sender: TObject);
+var
+ LDialog : TFRMDiagnosticTool;
+begin
+  LDialog := TFRMDiagnosticTool.Create(Nil);
+  try
+    LDialog.ShowModal;
+  finally
+    LDialog.Free;
+  end;
+end;
+
 procedure TFRMWallet.MiFindaccountClick(Sender: TObject);
 begin
   PageControl.ActivePage := tsMyAccounts;

+ 18 - 9
src/libraries/sphere10/UCommon.pas

@@ -44,14 +44,13 @@ const
 
 
 function String2Hex(const Buffer: String): String;
-{$IFDEF UNITTESTS}
 function Hex2Bytes(const AHexString: String): TBytes; overload;
 function TryHex2Bytes(const AHexString: String; out ABytes : TBytes): boolean; overload;
 function Bytes2Hex(const ABytes: TBytes; AUsePrefix : boolean = false) : String;
-{$ENDIF}
 function BinStrComp(const Str1, Str2 : String): Integer; // Binary-safe StrComp replacement. StrComp will return 0 for when str1 and str2 both start with NUL character.
 function BytesCompare(const ABytes1, ABytes2: TBytes): integer;
-function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean; inline;
+function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean; overload; inline;
+function BytesEqual(const ABytes1, ABytes2 : TBytes; AFrom, ALength : UInt32) : boolean; overload; inline;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64; overload;
@@ -462,7 +461,6 @@ begin
     Result := AnsiLowerCase(Result + IntToHex(Ord(Buffer[n]), 2));
 end;
 
-{$IFDEF UNITTESTS}
 function Hex2Bytes(const AHexString: String): TBytes;
 begin
   if NOT TryHex2Bytes(AHexString, Result) then
@@ -496,10 +494,14 @@ begin
   SetLength(ABytes, LHexLength DIV 2);
   P := @ABytes[Low(ABytes)];
   LHexString := LowerCase(AHexString);
+  {$IFDEF FPC}
   LHexIndex := HexToBin(PAnsiChar(@LHexString[LHexStart]), P, System.Length(ABytes));
+  {$ELSE}
+  LHexIndex := HexToBin(@LHexString[LHexStart],0,ABytes,0,Length(ABytes));
+  {$ENDIF}
   Result := (LHexIndex = (LHexLength DIV 2));
 end;
-{$ENDIF}
+
 
 function Bytes2Hex(const ABytes: TBytes; AUsePrefix : boolean = false) : String;
 var
@@ -580,14 +582,20 @@ begin
 end;
 
 function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean;
+begin
+  Result := BytesEqual(ABytes1, ABytes2, 0, Length(ABytes1));
+end;
+
+function BytesEqual(const ABytes1, ABytes2 : TBytes; AFrom, ALength : UInt32) : boolean;
 var ABytes1Len, ABytes2Len : Integer;
 begin
+  if ALength = 0 then
+    Exit(False);
   ABytes1Len := Length(ABytes1);
   ABytes2Len := Length(ABytes2);
-  if (ABytes1Len <> ABytes2Len) OR (ABytes1Len = 0) then
-    Result := False
-  else
-    Result := CompareMem(@ABytes1[0], @ABytes2[0], ABytes1Len);
+  if ((ABytes1Len - AFrom) < ALength) OR ((ABytes2Len - AFrom) < ALength ) then
+    Exit(False);
+  Result := CompareMem(@ABytes1[AFrom], @ABytes2[AFrom], ALength);
 end;
 
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal;
@@ -1912,6 +1920,7 @@ begin
 end;
 
 { TFileStreamHelper }
+
 {$IFNDEF FPC}
 procedure TFileStreamHelper.WriteString(const AString : String);
 begin

+ 3 - 1
src/pascalcoin_miner.lpi

@@ -62,12 +62,14 @@
     </SearchPaths>
     <CodeGeneration>
       <Optimizations>
-        <OptimizationLevel Value="3"/>
+        <OptimizationLevel Value="4"/>
       </Optimizations>
     </CodeGeneration>
     <Linking>
       <Debugging>
         <GenerateDebugInfo Value="False"/>
+        <DebugInfoType Value="dsDwarf2Set"/>
+        <UseValgrind Value="True"/>
       </Debugging>
     </Linking>
   </CompilerOptions>

+ 3 - 3
src/pascalcoin_miner.pp

@@ -58,7 +58,7 @@ type
   end;
 
 Const
-  CT_MINER_VERSION = {$IFDEF PRODUCTION}'4.0'{$ELSE}{$IFDEF TESTNET}'4.0 TESTNET'{$ELSE}ERROR{$ENDIF}{$ENDIF};
+  CT_MINER_VERSION = {$IFDEF PRODUCTION}'5.0'{$ELSE}{$IFDEF TESTNET}'5.0 TESTNET'{$ELSE}ERROR{$ENDIF}{$ENDIF};
   CT_Line_DeviceStatus = 3;
   CT_Line_ConnectionStatus = 4;
   CT_Line_MinerValues = 7;
@@ -218,8 +218,8 @@ var
     end;
     if HasOption('c','cpu') then begin
       c := StrToIntDef(GetOptionValue('c','cpu'),-1);
-      if (c<=0) or (c>TLogicalCPUCount.GetLogicalCPUCount()) then begin
-        WriteLn('Invalid cpu value ',c,'. Valid values: 1..',TLogicalCPUCount.GetLogicalCPUCount());
+      if (c<=0) or (c>TCPUTool.GetLogicalCPUCount()) then begin
+        WriteLn('Invalid cpu value ',c,'. Valid values: 1..',TCPUTool.GetLogicalCPUCount());
         Terminate;
         exit;
       end;

+ 5 - 1
src/pascalcoin_wallet_classic.dpr

@@ -59,7 +59,11 @@ uses
   UPCOrderedLists in 'core\UPCOrderedLists.pas',
   UPCOperationsSignatureValidator in 'core\UPCOperationsSignatureValidator.pas',
   UPCTNetDataExtraMessages in 'core\UPCTNetDataExtraMessages.pas',
-  UFRMHashLock in 'gui-classic\UFRMHashLock.pas' {FRMHashLock};
+  UFRMHashLock in 'gui-classic\UFRMHashLock.pas' {FRMHashLock},
+  URandomHash2 in 'core\URandomHash2.pas',
+  UFRMDiagnosticTool in 'gui-classic\UFRMDiagnosticTool.pas' {FRMDiagnosticTool},
+  UCommon in 'libraries\sphere10\UCommon.pas',
+  UMemory in 'libraries\sphere10\UMemory.pas';
 
 {$R *.res}
 

+ 7 - 0
src/pascalcoin_wallet_classic.dproj

@@ -219,6 +219,13 @@
             <Form>FRMHashLock</Form>
             <FormType>dfm</FormType>
         </DCCReference>
+        <DCCReference Include="core\URandomHash2.pas"/>
+        <DCCReference Include="gui-classic\UFRMDiagnosticTool.pas">
+            <Form>FRMDiagnosticTool</Form>
+            <FormType>dfm</FormType>
+        </DCCReference>
+        <DCCReference Include="libraries\sphere10\UCommon.pas"/>
+        <DCCReference Include="libraries\sphere10\UMemory.pas"/>
         <BuildConfiguration Include="Debug">
             <Key>Cfg_2</Key>
             <CfgParent>Base</CfgParent>

+ 16 - 1
src/pascalcoin_wallet_classic.lpi

@@ -42,7 +42,7 @@
         <PackageName Value="LCL"/>
       </Item1>
     </RequiredPackages>
-    <Units Count="34">
+    <Units Count="37">
       <Unit0>
         <Filename Value="pascalcoin_wallet_classic.dpr"/>
         <IsPartOfProject Value="True"/>
@@ -207,6 +207,21 @@
         <Filename Value="gui-classic\UGUIUtils.pas"/>
         <IsPartOfProject Value="True"/>
       </Unit33>
+      <Unit34>
+        <Filename Value="core\URandomHash2.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit34>
+      <Unit35>
+        <Filename Value="core\URandomHash.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit35>
+      <Unit36>
+        <Filename Value="gui-classic\UFRMDiagnosticTool.pas"/>
+        <IsPartOfProject Value="True"/>
+        <ComponentName Value="FRMDiagnosticTool"/>
+        <HasResources Value="True"/>
+        <ResourceBaseClass Value="Form"/>
+      </Unit36>
     </Units>
   </ProjectOptions>
   <CompilerOptions>

BIN
src/pascalcoin_wallet_classic.res


+ 5 - 1
src/pascalcoin_wallet_experimental.lpi

@@ -77,7 +77,7 @@
         <PackageName Value="LCL"/>
       </Item1>
     </RequiredPackages>
-    <Units Count="103">
+    <Units Count="104">
       <Unit0>
         <Filename Value="pascalcoin_wallet_experimental.dpr"/>
         <IsPartOfProject Value="True"/>
@@ -638,6 +638,10 @@
         <HasResources Value="True"/>
         <ResourceBaseClass Value="Form"/>
       </Unit102>
+      <Unit103>
+        <Filename Value="core\URandomHash2.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit103>
     </Units>
   </ProjectOptions>
   <CompilerOptions>

+ 4 - 2
src/tests/PascalCoinUnitTests.dpr

@@ -10,9 +10,11 @@ uses
   TestFramework,
   GUITestRunner,
   TextTestRunner,
-  URandomHash in '..\core\URandomHash.pas',
   UPCSafeBoxRootHashTests in 'UPCSafeBoxRootHashTests.pas',
-  URandomHash.Tests.Delphi in 'URandomHash.Tests.Delphi.pas';
+  URandomHash.Tests.Delphi in 'URandomHash.Tests.Delphi.pas',
+  URandomHash2.Tests.Delphi in 'URandomHash2.Tests.Delphi.pas',
+  URandomHash in '..\core\URandomHash.pas',
+  URandomHash2 in '..\core\URandomHash2.pas';
 
 begin
   Application.Initialize;

+ 3 - 1
src/tests/PascalCoinUnitTests.dproj

@@ -101,9 +101,11 @@
         <DelphiCompile Include="$(MainSource)">
             <MainSource>MainSource</MainSource>
         </DelphiCompile>
-        <DCCReference Include="..\core\URandomHash.pas"/>
         <DCCReference Include="UPCSafeBoxRootHashTests.pas"/>
         <DCCReference Include="URandomHash.Tests.Delphi.pas"/>
+        <DCCReference Include="URandomHash2.Tests.Delphi.pas"/>
+        <DCCReference Include="..\core\URandomHash.pas"/>
+        <DCCReference Include="..\core\URandomHash2.pas"/>
         <None Include="PascalCoinUnitTests.dpr"/>
         <BuildConfiguration Include="Debug">
             <Key>Cfg_2</Key>

+ 30 - 3
src/tests/PascalCoinUnitTests.lpi

@@ -13,7 +13,7 @@
       <UseXPManifest Value="True"/>
     </General>
     <BuildModes Count="1">
-      <Item1 Name="Default" Default="True"/>
+      <Item1 Name="Debug" Default="True"/>
     </BuildModes>
     <PublishOptions>
       <Version Value="2"/>
@@ -35,7 +35,7 @@
         <PackageName Value="FCL"/>
       </Item3>
     </RequiredPackages>
-    <Units Count="17">
+    <Units Count="19">
       <Unit0>
         <Filename Value="PascalCoinUnitTests.lpr"/>
         <IsPartOfProject Value="True"/>
@@ -106,8 +106,15 @@
       <Unit16>
         <Filename Value="URandomHash.Tests.pas"/>
         <IsPartOfProject Value="True"/>
-        <UnitName Value="URandomHashTests"/>
       </Unit16>
+      <Unit17>
+        <Filename Value="URandomHash2.Tests.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit17>
+      <Unit18>
+        <Filename Value="..\core\URandomHash2.pas"/>
+        <IsPartOfProject Value="True"/>
+      </Unit18>
     </Units>
   </ProjectOptions>
   <CompilerOptions>
@@ -121,7 +128,27 @@
       <OtherUnitFiles Value="..\..\src;..\core;..\libraries\generics.collections;..\libraries\hashlib4pascal;..\libraries\sphere10;..\libraries\pascalcoin"/>
       <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
     </SearchPaths>
+    <Parsing>
+      <SyntaxOptions>
+        <IncludeAssertionCode Value="True"/>
+      </SyntaxOptions>
+    </Parsing>
+    <CodeGeneration>
+      <Checks>
+        <IOChecks Value="True"/>
+        <RangeChecks Value="True"/>
+        <OverflowChecks Value="True"/>
+        <StackChecks Value="True"/>
+      </Checks>
+      <VerifyObjMethodCallValidity Value="True"/>
+    </CodeGeneration>
     <Linking>
+      <Debugging>
+        <DebugInfoType Value="dsDwarf2Set"/>
+        <UseHeaptrc Value="True"/>
+        <TrashVariables Value="True"/>
+        <UseExternalDbgSyms Value="True"/>
+      </Debugging>
       <Options>
         <PassLinkerOptions Value="True"/>
         <LinkerOptions Value=" -dUseCThreads"/>

+ 3 - 2
src/tests/PascalCoinUnitTests.lpr

@@ -3,8 +3,9 @@ program UPascalCoinUnitTests;
 {$mode objfpc}{$H+}
 
 uses
-  Interfaces, Forms, GuiTestRunner, UCommon.Collections,
-  UCommon.Tests, UCommon.Collections.Tests, UMemory.Tests, UThread.Tests, URandomHash.Tests;
+  Interfaces, Forms, GuiTestRunner, UCommon.Collections, UCommon.Tests,
+  UCommon.Collections.Tests, UMemory.Tests, UThread.Tests, URandomHash.Tests,
+  URandomHash2.Tests, URandomHash;
 
 {$R *.res}
 

BIN
src/tests/PascalCoinUnitTests.res


+ 56 - 0
src/tests/URandomHash2.Tests.Delphi.pas

@@ -0,0 +1,56 @@
+unit URandomHash2.Tests.Delphi;
+
+interface
+
+uses
+  Classes, SysUtils, {$IFDEF FPC}fpcunit,testregistry {$ELSE}TestFramework {$ENDIF FPC},
+  UUnitTests, HlpIHash;
+
+type
+
+  { TRandomHash2Test }
+
+  TRandomHash2Test = class(TPascalCoinUnitTest)
+  published
+    procedure TestRandomHash2_Standard;
+  end;
+
+implementation
+
+uses variants, UCommon, UMemory, URandomHash2, HlpHashFactory, HlpBitConverter, strutils;
+
+const
+
+  { RandomHash Official Values }
+
+  DATA_RANDOMHASH2_STANDARD_INPUT : array[1..3] of String = (
+    '0x0',
+    'The quick brown fox jumps over the lazy dog',
+    '0x000102030405060708090a0b0c0d0e0f'
+  );
+
+  DATA_RANDOMHASH2_STANDARD_EXPECTED : array[1..3] of String = (
+    '0x78b7b1a58fd073a47b02279080ff5b0d1fb673f0477c18a652801ba8fd0cbac8',
+    '0xf6ad93cb45b8749a1d64cd74cf446975cf782990587f9ddd08dfeee2087e7487',
+    '0xa37a261ff74ccb03bbb64bd1c3da5928f580052adcf7ff48596f4297110f20c2'
+  );
+
+{ TRandomHash2Test }
+
+procedure TRandomHash2Test.TestRandomHash2_Standard;
+var
+  i : integer;
+  LStr, LStr2 : String;
+  LB, LB2 : TBytes;
+begin
+  for i := Low(DATA_RANDOMHASH2_STANDARD_INPUT) to High(DATA_RANDOMHASH2_STANDARD_INPUT) do
+    AssertEquals(ParseBytes(DATA_RANDOMHASH2_STANDARD_EXPECTED[i]), TRandomHash2.Compute(ParseBytes(DATA_RANDOMHASH2_STANDARD_INPUT[i])));
+   //WriteLn(Format('%s', [Bytes2Hex(TRandomHash.Compute(ParseBytes(LCase.Input)), True)]));
+end;
+
+
+initialization
+
+  RegisterTest(TRandomHash2Test.Suite);
+
+end.

+ 168 - 0
src/tests/URandomHash2.Tests.pas

@@ -0,0 +1,168 @@
+unit URandomHash2.Tests;
+
+{$IFDEF FPC}
+  {$MODE DELPHI}
+  {$MODESWITCH NESTEDPROCVARS}
+{$ENDIF}
+
+
+interface
+
+uses
+  Classes, SysUtils, {$IFDEF FPC}fpcunit,testregistry {$ELSE}DUnitX.TestFramework, DUnitX.DUnitCompatibility{$ENDIF FPC},
+  UUnitTests, HlpIHash;
+
+type
+
+  { TRandomHash2Test }
+
+  TRandomHash2Test = class(TPascalCoinUnitTest)
+  published
+    procedure TestRandomHash2_Standard;
+    procedure TestRandomHash2;
+    procedure CacheConsistency;
+    procedure GetSetLastDWordConsistency;
+  end;
+
+
+  { TRandomHash2StressTest }
+
+  TRandomHash2StressTest = class(TPascalCoinUnitTest)
+  published
+    procedure Reference1000;
+  end;
+
+implementation
+
+uses
+  variants, UCommon, UCommon.Collections, UMemory, URandomHash2, HlpHashFactory,
+  HlpBitConverter, strutils, Generics.Collections;
+
+const
+
+  { General purpose byte array for testing }
+
+  DATA_BYTES : String = '0x4f550200ca022000bb718b4b00d6f74478c332f5fb310507e55a9ef9b38551f63858e3f7c86dbd00200006f69afae8a6b0735b6acfcc58b7865fc8418897c530211f19140c9f95f24532102700000000000003000300a297fd17506f6c796d696e65722e506f6c796d696e65722e506f6c796d6939303030303030302184d63666eb166619e925cef2a306549bbc4d6f4da3bdf28b4393d5c1856f0ee3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855000000006d68295b00000000';
+
+  { RandomHash Official Values }
+
+  DATA_RANDOMHASH_STANDARD : array[1..3] of TTestItem<String, String> = (
+    (Input: '0x0';                                         Expected: '0x78b7b1a58fd073a47b02279080ff5b0d1fb673f0477c18a652801ba8fd0cbac8'),
+    (Input: 'The quick brown fox jumps over the lazy dog'; Expected: '0xf6ad93cb45b8749a1d64cd74cf446975cf782990587f9ddd08dfeee2087e7487'),
+    (Input: '0x000102030405060708090a0b0c0d0e0f';          Expected: '0xa37a261ff74ccb03bbb64bd1c3da5928f580052adcf7ff48596f4297110f20c2')
+  );
+
+  {  Hash Test Data }
+
+  DATA_RANDOMHASH : array[1..16] of TTestItem<Integer, String> = (
+    { NOTE: Input denotes the number of bytes to take from DATA_BYTES when executing test }
+    (Input: 17;  Expected: '0x28e348e6a865e5333e4528e0743b4965248cac41904e11d9011a50cb19513fb6'),
+    (Input: 31;  Expected: '0x656a42062355319bd643a9eff87cc04d14ea442384e7dd0932113911f2823024'),
+    (Input: 32;  Expected: '0xd666b74b91c2ddfb54d663bafb369c53055a875ababbdf1f510db39dd73d86fa'),
+    (Input: 33;  Expected: '0x163b3758fb5dfb896f469f9914df1f43d966a8ff3d1e710ed51fdcfc7e425308'),
+    (Input: 34;  Expected: '0x97a3618f57f9477cd4ba91397cd7856ecd05f8d8206b077828cd52e8ecdcd4c5'),
+    (Input: 63;  Expected: '0x93f998f385413219f4ca9b764e3f69f90100fdf43d4434d0f8fe4bcf4ed98ae6'),
+    (Input: 64;  Expected: '0x709a93634fe1927f4b94570049ebf33ac4c1bd750392df070e44d59261d763e9'),
+    (Input: 65;  Expected: '0x2ef12aab3156b3e25551ed7c1dfbf51c6dfc84510cb6cc46b405bae02578f1b0'),
+    (Input: 100; Expected: '0x6cf7f41cb8ec80ae512e68322f67bf381821599d39a2007cb6d1857b3458947a'),
+    (Input: 117; Expected: '0xa3ce45f763b30dc74181b4ad4a02631318f93cdf3cc4f4054b01561df29c53fd'),
+    (Input: 127; Expected: '0x1c042113abb5c41265916be7092acf2000a148668b0bca9671e9cdbdf13dc99a'),
+    (Input: 128; Expected: '0xda552e27ca43bcfc8c636a4c58041027f9f0a06b0d40031c889fe3a673b89701'),
+    (Input: 129; Expected: '0x4b1b1d161ff80435ea4a6839530258d33d3105b5812def2b1b65bf543df06e62'),
+    (Input: 178; Expected: '0x66cbb61b4b927ca60c4ca04d999b0d8398b9fa9cf1db2866956b246f11cbc692'),
+    (Input: 199; Expected: '0x3705f63d6242acd77fbc262010b34101165c7cc6c0c9bbd607bbc118d1626a5f'),
+    (Input: 200; Expected: '0xcde9da66925e5b363aaca02dac5c473e81ab6a0f1b7a6b6dbac9d0ebec383e22')
+  );
+
+{ TRandomHash2Test }
+
+procedure TRandomHash2Test.TestRandomHash2_Standard;
+var
+  LCase : TTestItem<String, String>;
+begin
+  for LCase in DATA_RANDOMHASH_STANDARD do
+    AssertEquals(ParseBytes(LCase.Expected), TRandomHash2.Compute(ParseBytes(LCase.Input)));
+    //WriteLn(Format('%s', [Bytes2Hex(TRandomHash2.Compute(ParseBytes(LCase.Input)), True)]));
+end;
+
+procedure TRandomHash2Test.TestRandomHash2;
+var
+  LInput : TBytes;
+  LCase : TTestItem<Integer, String>;
+begin
+  for LCase in DATA_RANDOMHASH do begin
+    LInput := TArrayTool<byte>.Copy(ParseBytes(DATA_BYTES), 0, LCase.Input);
+    AssertEquals(ParseBytes(LCase.Expected), TRandomHash2.Compute(LInput));
+    //WriteLn(Format('%s', [Bytes2Hex(TRandomHash2.Compute(LInput), True)]));
+  end;
+end;
+
+procedure TRandomHash2Test.CacheConsistency;
+var
+  LBuff : TBytes;
+  LHasher : TRandomHash2;
+  LCachedHash : TRandomHash2.TCachedHash;
+  i, j : Integer;
+  LDisposables : TDisposables;
+begin
+  LBuff := ParseBytes(DATA_BYTES);
+  LHasher := LDisposables.AddObject( TRandomHash2.Create ) as TRandomHash2;
+
+  for i := 1 to 100 do begin
+    LBuff := LHasher.Hash(LBuff);
+    while LHasher.HasCachedHash do begin
+      LCachedHash := LHasher.PopCachedHash;
+      AssertEquals(TRandomHash2.Compute(LCachedHash.Header), LCachedHash.Hash);
+    end;
+  end;
+end;
+
+procedure TRandomHash2Test.GetSetLastDWordConsistency;
+var
+  LBuff, LBuff2 : TBytes;
+  LHasher : TRandomHash2;
+  LCachedHash : TRandomHash2.TCachedHash;
+  i  : UInt32;
+  LDisposables : TDisposables;
+begin
+  LBuff := ParseBytes(DATA_BYTES);
+  LHasher := LDisposables.AddObject( TRandomHash2.Create ) as TRandomHash2;
+  for i := 1 to 100 do begin
+    LBuff := LHasher.Hash(LBuff);
+    AssertEquals(32768 + i, LHasher.GetLastDWordLE(LHasher.SetLastDWordLE(LBuff, 32768 + i)));
+  end;
+end;
+
+{ TRandomHash2StressTest }
+
+procedure TRandomHash2StressTest.Reference1000;
+const
+  NUM_ITER = 1000;
+var
+  i : Integer;
+  LBuff : TBytes;
+  LHasher : TRandomHash2;
+  LDisposables : TDisposables;
+begin
+  LBuff := ParseBytes(DATA_BYTES);
+  LHasher := LDisposables.AddObject( TRandomHash2.Create ) as TRandomHash2;
+  for i := 1 to NUM_ITER do
+    LBuff := LHasher.Hash(LBuff);
+  // no exceptions should occur
+end;
+
+
+
+initialization
+
+{$IFDEF FPC}
+  RegisterTest(TRandomHash2Test);
+  RegisterTest(TRandomHash2StressTest);
+{$ELSE}
+  //TDUnitX.RegisterTextFixture(TRandomHashTest);
+  //TDUnitX.RegisterTextFixture(TRandomHashFastTest);
+  //TDUnitX.RegisterTextFixture(TRandomHashFast_TChecksummedByteCollectionTest);
+  //TDUnitX.RegisterTextFixture(TRandomHashStressTest);
+{$ENDIF FPC}
+
+end.