Browse Source

RandomHash2: final development and partial integration

Herman Schoenfeld 6 years ago
parent
commit
8cf07008ed

+ 8 - 6
src/core/UAccounts.pas

@@ -835,12 +835,14 @@ begin
     ms.Write(operationBlock.timestamp,4);
     ms.Write(operationBlock.timestamp,4);
     ms.Write(operationBlock.nonce,4);
     ms.Write(operationBlock.nonce,4);
     if CT_ACTIVATE_RANDOMHASH_V4 AND (operationBlock.protocol_version >= CT_PROTOCOL_4) then begin
     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
     end else
       TCrypto.DoDoubleSha256(ms.Memory,ms.Size,PoW);
       TCrypto.DoDoubleSha256(ms.Memory,ms.Size,PoW);
   finally
   finally

+ 6 - 4
src/core/UBlockChain.pas

@@ -1285,10 +1285,12 @@ begin
   FStreamPoW.WriteBuffer(FDigest_Part3[Low(FDigest_Part3)],Length(FDigest_Part3));
   FStreamPoW.WriteBuffer(FDigest_Part3[Low(FDigest_Part3)],Length(FDigest_Part3));
   FStreamPoW.Write(FOperationBlock.timestamp,4);
   FStreamPoW.Write(FOperationBlock.timestamp,4);
   FStreamPoW.Write(FOperationBlock.nonce,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;
 end;
 
 
 procedure TPCOperationsComp.Calc_Digest_Parts;
 procedure TPCOperationsComp.Calc_Digest_Parts;

+ 40 - 2
src/core/UCrypto.pas

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

+ 29 - 9
src/core/UPoolMinerThreads.pas

@@ -25,7 +25,7 @@ interface
 {$I config.inc}
 {$I config.inc}
 
 
 uses
 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};
   {$IFNDEF FPC}System.Generics.Collections{$ELSE}Generics.Collections{$ENDIF};
 
 
 type
 type
@@ -498,7 +498,10 @@ begin
   FLastDigest := digest;
   FLastDigest := digest;
   LUseRandomHash := TPoolMinerThread.UseRandomHash(usedMinerValuesForWork.version);
   LUseRandomHash := TPoolMinerThread.UseRandomHash(usedMinerValuesForWork.version);
   if LUseRandomHash then
   if LUseRandomHash then
-    LHash := TCrypto.DoRandomHash(digest)
+    if (usedMinerValuesForWork.version >= CT_PROTOCOL_5) then
+      LHash := TCrypto.DoRandomHash2(digest)
+    else
+      LHash := TCrypto.DoRandomHash(digest)
   else
   else
     LHash := TCrypto.DoSha256(TCrypto.DoSha256(digest));
     LHash := TCrypto.DoSha256(TCrypto.DoSha256(digest));
   if (TBaseType.BinStrComp(LHash,usedMinerValuesForWork.target_pow)<=0) then begin
   if (TBaseType.BinStrComp(LHash,usedMinerValuesForWork.target_pow)<=0) then begin
@@ -792,6 +795,7 @@ Var
   dstep : Integer;
   dstep : Integer;
   LUseRandomHash : boolean;
   LUseRandomHash : boolean;
   LRandomHasher : TRandomHashFast;
   LRandomHasher : TRandomHashFast;
+  LRandomHasher2 : TRandomHash2;
   LDisposables : TDisposables;
   LDisposables : TDisposables;
 begin
 begin
   DebugStep := '----------';
   DebugStep := '----------';
@@ -799,6 +803,7 @@ begin
   nonce := 0;
   nonce := 0;
   dstep := 0;
   dstep := 0;
   LRandomHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
   LRandomHasher := LDisposables.AddObject( TRandomHashFast.Create ) as TRandomHashFast;
+  LRandomHasher2 := LDisposables.AddObject( TRandomHash2.Create ) as TRandomHash2;
   Try
   Try
     while (Not Terminated) And (Not FCPUDeviceThread.Terminated) do begin
     while (Not Terminated) And (Not FCPUDeviceThread.Terminated) do begin
       Try
       Try
@@ -812,7 +817,10 @@ begin
         try
         try
           LUseRandomHash := TPoolMinerThread.UseRandomHash(FCurrentMinerValuesForWork.version);
           LUseRandomHash := TPoolMinerThread.UseRandomHash(FCurrentMinerValuesForWork.version);
           if (LUseRandomHash) then begin
           if (LUseRandomHash) then begin
-            roundsToDo := 20;
+            if FCurrentMinerValuesForWork.version < CT_PROTOCOL_5 then
+              roundsToDo := 20
+            else
+              roundsToDo := 100;
           end else begin
           end else begin
             roundsToDo := 10000;
             roundsToDo := 10000;
           end;
           end;
@@ -837,10 +845,16 @@ begin
                 FDigestStreamMsg.Position := FDigestStreamMsg.Size - 4;
                 FDigestStreamMsg.Position := FDigestStreamMsg.Size - 4;
                 FDigestStreamMsg.Write(nonce,4);
                 FDigestStreamMsg.Write(nonce,4);
                 if LUseRandomHash then begin
                 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);
+                  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)
+                  end
+                end else begin
                   TCrypto.DoDoubleSha256(FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
                   TCrypto.DoDoubleSha256(FDigestStreamMsg.Memory,FDigestStreamMsg.Size,resultPoW);
+                end;
                 if (TBaseType.BinStrComp(resultPoW,FCurrentMinerValuesForWork.target_pow)<0) then begin
                 if (TBaseType.BinStrComp(resultPoW,FCurrentMinerValuesForWork.target_pow)<0) then begin
                   if (Terminated) Or (FCPUDeviceThread.Terminated) then exit;
                   if (Terminated) Or (FCPUDeviceThread.Terminated) then exit;
                   dstep := 5;
                   dstep := 5;
@@ -854,9 +868,15 @@ begin
                   end;
                   end;
                   dstep := 8;
                   dstep := 8;
                 end;
                 end;
-                if LUseRandomHash then
-                  nonce := LRandomHasher.NextNonce
-                else if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
+                if LUseRandomHash then begin
+                  if (FCurrentMinerValuesForWork.version < CT_PROTOCOL_5) then begin
+                    nonce := LRandomHasher.NextNonce;
+                  end else begin
+                    if LRandomHasher2.HasCachedHash then
+                      nonce := LRandomHasher2.PeekCachedHash.Nonce
+                    else inc(nonce);
+                  end;
+                end else if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
               end;
               end;
               finalHashingTC:=TPlatform.GetTickCount;
               finalHashingTC:=TPlatform.GetTickCount;
             end else begin
             end else begin

+ 102 - 56
src/core/URandomHash2.pas

@@ -112,13 +112,25 @@ type
   TRandomHash2 = class sealed(TObject)
   TRandomHash2 = class sealed(TObject)
     const
     const
       N = 5;      // Max-number of hashing rounds required to compute a nonce, total rounds = 2^N
       N = 5;      // Max-number of hashing rounds required to compute a nonce, total rounds = 2^N
-      J = 3;       // Max-number of dependent neighbouring nonces required to evaluate a nonce round
+      J = 5;       // Max-number of dependent neighbouring nonces required to evaluate a nonce round
       M = 1024;    // The memory expansion unit (in bytes), total bytes per nonce = M * (2^N (N-2) + 2)
       M = 1024;    // The memory expansion unit (in bytes), total bytes per nonce = M * (2^N (N-2) + 2)
       NUM_HASH_ALGO = 18;
       NUM_HASH_ALGO = 18;
 
 
-    {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
+      public type
+
+        TCachedHash = record
+          Nonce : UInt32;
+          Header : TBytes;
+          Hash : TBytes;
+        end;
+
+      {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
       FMurmurHash3_x86_32 : IHash;
       FMurmurHash3_x86_32 : IHash;
       FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
       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 ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes; inline;
       function MemTransform1(const AChunk: TBytes): TBytes; inline;
       function MemTransform1(const AChunk: TBytes): TBytes; inline;
       function MemTransform2(const AChunk: TBytes): TBytes; inline;
       function MemTransform2(const AChunk: TBytes): TBytes; inline;
@@ -130,13 +142,19 @@ type
       function MemTransform8(const AChunk: TBytes): TBytes; inline;
       function MemTransform8(const AChunk: TBytes): TBytes; inline;
       function Expand(const AInput: TBytes; AExpansionFactor: Int32) : TBytes;
       function Expand(const AInput: TBytes; AExpansionFactor: Int32) : TBytes;
       function Compress(const AInputs: TArray<TBytes>): TBytes; inline;
       function Compress(const AInputs: TArray<TBytes>): TBytes; inline;
-      function ChangeNonce(const ABlockHeader: TBytes; ANonce: UInt32): TBytes; inline;
+      function SetLastDWordLE(const ABytes: TBytes; AValue: UInt32): TBytes; inline;
+      function GetLastDWordLE(const ABytes: TBytes) : UInt32; inline;
       function Checksum(const AInput: TBytes): UInt32; overload; inline;
       function Checksum(const AInput: TBytes): UInt32; overload; inline;
       function Checksum(const AInput: TArray<TBytes>): UInt32; overload; inline;
       function Checksum(const AInput: TArray<TBytes>): UInt32; overload; inline;
+      function ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes; inline;
       function Hash(const ABlockHeader: TBytes; ARound: Int32; out AFoundLastRound : Int32) : TArray<TBytes>; overload;
       function Hash(const ABlockHeader: TBytes; ARound: Int32; out AFoundLastRound : Int32) : TArray<TBytes>; overload;
     public
     public
       constructor Create;
       constructor Create;
       destructor Destroy; override;
       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;
       function Hash(const ABlockHeader: TBytes): TBytes; overload; inline;
       class function Compute(const ABlockHeader: TBytes): TBytes; overload; static; inline;
       class function Compute(const ABlockHeader: TBytes): TBytes; overload; static; inline;
   end;
   end;
@@ -156,12 +174,13 @@ implementation
 
 
 uses UCommon, UMemory, URandomHash;
 uses UCommon, UMemory, URandomHash;
 
 
-
 { TRandomHash2 }
 { TRandomHash2 }
 
 
 constructor TRandomHash2.Create;
 constructor TRandomHash2.Create;
 begin
 begin
   FMurmurHash3_x86_32 := THashFactory.THash32.CreateMurmurHash3_x86_32();
   FMurmurHash3_x86_32 := THashFactory.THash32.CreateMurmurHash3_x86_32();
+  SetLength(Self.FCachedHeaderTemplate, 0);
+  FCachedHashes := TList<TCachedHash>.Create;
   FHashAlg[0] := THashFactory.TCrypto.CreateSHA2_256();
   FHashAlg[0] := THashFactory.TCrypto.CreateSHA2_256();
   FHashAlg[1] := THashFactory.TCrypto.CreateSHA2_384();
   FHashAlg[1] := THashFactory.TCrypto.CreateSHA2_384();
   FHashAlg[2] := THashFactory.TCrypto.CreateSHA2_512();
   FHashAlg[2] := THashFactory.TCrypto.CreateSHA2_512();
@@ -185,6 +204,8 @@ end;
 destructor TRandomHash2.Destroy;
 destructor TRandomHash2.Destroy;
 var i : integer;
 var i : integer;
 begin
 begin
+ FCachedHashes.Clear;
+ FreeAndNil(FCachedHashes);
  FMurmurHash3_x86_32 := nil;
  FMurmurHash3_x86_32 := nil;
  for i := Low(FHashAlg) to High(FHashAlg) do
  for i := Low(FHashAlg) to High(FHashAlg) do
    FHashAlg[i] := nil;
    FHashAlg[i] := nil;
@@ -210,7 +231,13 @@ begin
   LAllOutputs := Hash(ABlockHeader, N, LLastRound);
   LAllOutputs := Hash(ABlockHeader, N, LLastRound);
   if LLastRound <= 0 then
   if LLastRound <= 0 then
     raise ERandomHash2.Create('Internal Error: 984F52997131417E8D63C43BD686F5B2'); // Should have found final round!
     raise ERandomHash2.Create('Internal Error: 984F52997131417E8D63C43BD686F5B2'); // Should have found final round!
-  Result := FHashAlg[0].ComputeBytes(Compress(LAllOutputs)).GetBytes;
+  Result := ComputeVeneerRound(LAllOutputs);
+end;
+
+function TRandomHash2.ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes;
+begin
+  // Final "veneer" round of RandomHash is a SHA2-256 of compresion of prior round outputs
+  Result := FHashAlg[0].ComputeBytes(Compress(ARoundOutputs)).GetBytes;
 end;
 end;
 
 
 function TRandomHash2.Hash(const ABlockHeader: TBytes; ARound: Int32; out AFoundLastRound : Int32) : TArray<TBytes>;
 function TRandomHash2.Hash(const ABlockHeader: TBytes; ARound: Int32; out AFoundLastRound : Int32) : TArray<TBytes>;
@@ -219,7 +246,8 @@ var
   LNeighbourLastRound : Int32;
   LNeighbourLastRound : Int32;
   LSeed, LNumNeighbours: UInt32;
   LSeed, LNumNeighbours: UInt32;
   LGen: TMersenne32;
   LGen: TMersenne32;
-  LRoundInput, LNeighbourNonceHeader, LOutput, LBytes: TBytes;
+  LRoundInput, LNeighbourNonceHeader, LOutput : TBytes;
+  LCachedHash : TCachedHash;
   LParentOutputs, LNeighborOutputs, LToArray: TArray<TBytes>;
   LParentOutputs, LNeighborOutputs, LToArray: TArray<TBytes>;
   LHashFunc: IHash;
   LHashFunc: IHash;
   i: Int32;
   i: Int32;
@@ -246,11 +274,25 @@ begin
     LRoundOutputs.AddRange( LParentOutputs );
     LRoundOutputs.AddRange( LParentOutputs );
 
 
     // Add neighbouring nonce outputs to this round outputs
     // Add neighbouring nonce outputs to this round outputs
-    LNumNeighbours := (LGen.NextUInt32 MOD J)+1;
+    LNumNeighbours := LGen.NextUInt32 MOD J;
     for i := 0 to LNumNeighbours do begin
     for i := 0 to LNumNeighbours do begin
-      LNeighbourNonceHeader := ChangeNonce(ABlockHeader, LGen.NextUInt32);
+      LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
       LNeighborOutputs := Hash(LNeighbourNonceHeader, ARound - 1, LNeighbourLastRound);
       LNeighborOutputs := Hash(LNeighbourNonceHeader, ARound - 1, LNeighbourLastRound);
       LRoundOutputs.AddRange(LNeighborOutputs);
       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 := TArrayTool<Byte>.Copy(LCachedHash.Header);
+          SetLastDWordLE(FCachedHeaderTemplate, 0);
+        end;
+        FCachedHashes.Add(LCachedHash);
+      end;
     end;
     end;
 
 
     // Compress the parent/neighbouring outputs to form this rounds input
     // Compress the parent/neighbouring outputs to form this rounds input
@@ -262,77 +304,60 @@ begin
   LHashFunc := FHashAlg[LGen.NextUInt32 mod NUM_HASH_ALGO];
   LHashFunc := FHashAlg[LGen.NextUInt32 mod NUM_HASH_ALGO];
   LOutput := LHashFunc.ComputeBytes(LRoundInput).GetBytes;
   LOutput := LHashFunc.ComputeBytes(LRoundInput).GetBytes;
 
 
-  // Determine if final round (last byte of hash mod N = 0)
-  if (ARound = N) OR (LOutput[High(LOutput)] MOD N = 0) then
-    AFoundLastRound := ARound;
-
   // Memory-expand the hash, add to output list and return
   // Memory-expand the hash, add to output list and return
   LOutput := Expand(LOutput, N - ARound);
   LOutput := Expand(LOutput, N - ARound);
   LRoundOutputs.Add(LOutput);
   LRoundOutputs.Add(LOutput);
   Result := LRoundOutputs.ToArray;
   Result := LRoundOutputs.ToArray;
+
+  // Determine if final round
+  if (ARound = N) OR (GetLastDWordLE(LOutput) MOD N = 0) then
+    AFoundLastRound := ARound;
 end;
 end;
 
 
-function TRandomHash2.ChangeNonce(const ABlockHeader: TBytes;  ANonce: UInt32): TBytes;
+function TRandomHash2.SetLastDWordLE(const ABytes: TBytes;  AValue: UInt32): TBytes;
 var
 var
   LHeaderLength : Integer;
   LHeaderLength : Integer;
 begin
 begin
   // NOTE: NONCE is last 4 bytes of header!
   // NOTE: NONCE is last 4 bytes of header!
 
 
   // Clone the original header
   // Clone the original header
-  Result := Copy(ABlockHeader);
+  Result := Copy(ABytes);
 
 
   // If digest not big enough to contain a nonce, just return the clone
   // If digest not big enough to contain a nonce, just return the clone
-  LHeaderLength := Length(ABlockHeader);
+  LHeaderLength := Length(ABytes);
   if LHeaderLength < 4 then
   if LHeaderLength < 4 then
     exit;
     exit;
 
 
   // Overwrite the nonce in little-endian
   // Overwrite the nonce in little-endian
-  Result[LHeaderLength - 4] := Byte(ANonce);
-  Result[LHeaderLength - 3] := (ANonce SHR 8) AND 255;
-  Result[LHeaderLength - 2] := (ANonce SHR 16) AND 255;
-  Result[LHeaderLength - 1] := (ANonce SHR 24) AND 255;
+  Result[LHeaderLength - 4] := Byte(AValue);
+  Result[LHeaderLength - 3] := (AValue SHR 8) AND 255;
+  Result[LHeaderLength - 2] := (AValue SHR 16) AND 255;
+  Result[LHeaderLength - 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;
 end;
 
 
 function TRandomHash2.Checksum(const AInput: TBytes): UInt32;
 function TRandomHash2.Checksum(const AInput: TBytes): UInt32;
-var
-  LSeed : UInt32;
-  LLen, i, j : UInt32;
 begin
 begin
-  if Length(AInput) < 4 then
-    raise ERandomHash2.Create('Can''t checksum arrays smaller than 4 bytes');
-
-  // Seed the XorShift32 RNG with last little-endian DWORD of input
-  LLen := Length(AInput);
-  LSeed := AInput[LLen - 4] OR
-          (AInput[LLen - 3] SHL 8) OR
-          (AInput[LLen - 2] SHL 16) OR
-          (AInput[LLen - 1] SHL 24);
-  if LSeed = 0 then LSeed := 1;
-
-  // The "checksum" is determined by picking random values from the input and
-  // re-seeding using those values (and bit-rotating with other entropy sources).
-  for i := 0 to 24 do begin
-    // Build a new 32bit seed by selecting 4 random bytes selected using
-    // the existing 32bit seed. The bytes are aggregated in LE sequence.
-    // Note: XorShift alters LSeed each call
-    LSeed := AInput[TXorShift32.Next(LSeed) MOD LLen] OR
-            (AInput[TXorShift32.Next(LSeed) MOD LLen] SHL 8) OR
-            (AInput[TXorShift32.Next(LSeed) MOD LLen] SHL 16) OR
-            (AInput[TXorShift32.Next(LSeed) MOD LLen] SHL 24);
-    if LSeed = 0 then LSeed := 1;
-
-    // Bit-Rotate the new 32-byte seed using i and input length
-    LSeed := TBits.RotateLeft32(LSeed, (LLen + i) MOD 32);
-  end;
-  Result := LSeed;
+  Result := Checksum( TArray<TBytes>.Create( AInput ) );
 end;
 end;
 
 
 function TRandomHash2.Checksum(const AInput : TArray<TBytes>): UInt32;
 function TRandomHash2.Checksum(const AInput : TArray<TBytes>): UInt32;
-var
-  i: Int32;
 begin
 begin
-  // shortcut just checksum last item
-  Result := Checksum(AInput[High(AInput)]);
+  // Checksum is the MurMur3 of the compression
+  Result := FMurmurHash3_x86_32.ComputeBytes(Compress(AInput)).GetUInt32;
 end;
 end;
 
 
 function TRandomHash2.Compress(const AInputs : TArray<TBytes>): TBytes;
 function TRandomHash2.Compress(const AInputs : TArray<TBytes>): TBytes;
@@ -344,7 +369,7 @@ var
   LDisposables : TDisposables;
   LDisposables : TDisposables;
 begin
 begin
   SetLength(Result, 100);
   SetLength(Result, 100);
-  LSeed := Checksum(AInputs);
+  LSeed :=  GetLastDWordLE(AInputs [High(AInputs)]);  // Seed using last 4 bytes
   LGen := LDisposables.AddObject( TMersenne32.Create( LSeed ) ) as TMersenne32;
   LGen := LDisposables.AddObject( TMersenne32.Create( LSeed ) ) as TMersenne32;
   for i := 0 to 99 do
   for i := 0 to 99 do
   begin
   begin
@@ -353,6 +378,27 @@ begin
   end;
   end;
 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;
 function TRandomHash2.ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes;
 begin
 begin
   SetLength(Result, Length(AChunk1) + Length(AChunk2));
   SetLength(Result, Length(AChunk1) + Length(AChunk2));
@@ -365,8 +411,8 @@ var
   i, LChunkLength : UInt32;
   i, LChunkLength : UInt32;
   LState : UInt32;
   LState : UInt32;
 begin
 begin
-  // Seed XorShift32 with non-zero seed (checksum of input or 1)
-  LState := Checksum(AChunk);
+  // Seed XorShift32 with last byte
+  LState := GetLastDWordLE(AChunk);
   if LState = 0 then
   if LState = 0 then
     LState := 1;
     LState := 1;
 
 

+ 10 - 1
src/gui-classic/UFRMDiagnosticTool.dfm

@@ -27,7 +27,7 @@ object FRMDiagnosticTool: TFRMDiagnosticTool
     OnClick = btnRHClick
     OnClick = btnRHClick
   end
   end
   object btnRH2: TButton
   object btnRH2: TButton
-    Left = 151
+    Left = 144
     Top = 8
     Top = 8
     Width = 130
     Width = 130
     Height = 25
     Height = 25
@@ -43,4 +43,13 @@ object FRMDiagnosticTool: TFRMDiagnosticTool
     Anchors = [akLeft, akTop, akRight, akBottom]
     Anchors = [akLeft, akTop, akRight, akBottom]
     TabOrder = 2
     TabOrder = 2
   end
   end
+  object btnRHC: TButton
+    Left = 280
+    Top = 8
+    Width = 186
+    Height = 25
+    Caption = 'Start Random Hash 2 (Cached)'
+    TabOrder = 3
+    OnClick = btnRHCClick
+  end
 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

+ 66 - 7
src/gui-classic/UFRMDiagnosticTool.pas

@@ -3,25 +3,32 @@ unit UFRMDiagnosticTool;
 interface
 interface
 
 
 uses
 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;
+  Graphics, Controls, Forms, Dialogs, ExtCtrls, StdCtrls,
+  SysUtils, Variants, Classes, UThread, UMemory, URandomHash, URandomHash2,
+  {$IFNDEF FPC}
+  System.TimeSpan,
+  {$ENDIF}
+  UCommon;
 
 
 type
 type
   TFRMDiagnosticTool = class(TForm)
   TFRMDiagnosticTool = class(TForm)
     btnRH: TButton;
     btnRH: TButton;
     btnRH2: TButton;
     btnRH2: TButton;
     txtLog: TMemo;
     txtLog: TMemo;
+    btnRHC: TButton;
     procedure btnRH2Click(Sender: TObject);
     procedure btnRH2Click(Sender: TObject);
     procedure btnRHClick(Sender: TObject);
     procedure btnRHClick(Sender: TObject);
     procedure FormCreate(Sender: TObject);
     procedure FormCreate(Sender: TObject);
+    procedure btnRHCClick(Sender: TObject);
   private
   private
     { Private declarations }
     { Private declarations }
     FDisposables : TDisposables;
     FDisposables : TDisposables;
     FRHThread : TPCThread;
     FRHThread : TPCThread;
     FRH2Thread : TPCThread;
     FRH2Thread : TPCThread;
+    FRH2CachedThread : TPCThread;
     procedure OnRandomHashReport(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
     procedure OnRandomHashReport(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
     procedure OnRandomHash2Report(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
     procedure OnRandomHash2Report(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
+    procedure OnRandomHash2CachedReport(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
   public
   public
     { Public declarations }
     { Public declarations }
   end;
   end;
@@ -52,7 +59,7 @@ type
 
 
   TRandomHashThread = class(TAlgorithmThread)
   TRandomHashThread = class(TAlgorithmThread)
     private
     private
-      FHasher : TRandomHash;
+      FHasher : TRandomHashFast;
     protected
     protected
       function NextRound : TBytes; override;
       function NextRound : TBytes; override;
     public
     public
@@ -70,6 +77,17 @@ type
       constructor Create; override;
       constructor Create; override;
   end;
   end;
 
 
+  { TRandomHash2CachedThread }
+
+  TRandomHash2CachedThread = class(TAlgorithmThread)
+    private
+      FHasher : TRandomHash2;
+    protected
+      function NextRound : TBytes; override;
+    public
+      constructor Create; override;
+  end;
+
 implementation
 implementation
 
 
 uses UBaseTypes;
 uses UBaseTypes;
@@ -118,7 +136,7 @@ end;
 constructor TRandomHashThread.Create;
 constructor TRandomHashThread.Create;
 begin
 begin
   Inherited Create;
   Inherited Create;
-  FHasher := TRandomHash.Create;
+  FHasher := TRandomHashFast.Create;
   FDisposables.AddObject(FHasher);
   FDisposables.AddObject(FHasher);
 end;
 end;
 
 
@@ -141,17 +159,39 @@ begin
   Result := FHasher.Hash(FLastHash);
   Result := FHasher.Hash(FLastHash);
 end;
 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 }
 { TFRMDiagnosicTool }
 
 
 procedure TFRMDiagnosticTool.FormCreate(Sender: TObject);
 procedure TFRMDiagnosticTool.FormCreate(Sender: TObject);
 begin
 begin
+  FRH2CachedThread := TRandomHash2CachedThread.Create;
   FRH2Thread := TRandomHash2Thread.Create;
   FRH2Thread := TRandomHash2Thread.Create;
   FRHThread := TRandomHashThread.Create;
   FRHThread := TRandomHashThread.Create;
   FDisposables.AddObject(FRHThread);
   FDisposables.AddObject(FRHThread);
   FDisposables.AddObject(FRH2Thread);
   FDisposables.AddObject(FRH2Thread);
+  FDisposables.AddObject(FRH2CachedThread);
 
 
   TRandomHashThread(FRHThread).Notify := OnRandomHashReport;
   TRandomHashThread(FRHThread).Notify := OnRandomHashReport;
   TRandomHash2Thread(FRH2Thread).Notify := OnRandomHash2Report;
   TRandomHash2Thread(FRH2Thread).Notify := OnRandomHash2Report;
+  TRandomHash2CachedThread(FRH2CachedThread).Notify := OnRandomHash2CachedReport;
 end;
 end;
 
 
 procedure TFRMDiagnosticTool.btnRHClick(Sender: TObject);
 procedure TFRMDiagnosticTool.btnRHClick(Sender: TObject);
@@ -176,11 +216,22 @@ begin
   end;
   end;
 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);
 procedure TFRMDiagnosticTool.OnRandomHashReport(ATotalHashes : UInt32; const ATimeSpan : TTimeSpan);
 var
 var
  LHPS : Double;
  LHPS : Double;
 begin
 begin
-  LHPS := Double(ATotalHashes) / Double(ATimeSpan.TotalSeconds);
+  LHPS := Trunc(ATotalHashes) / ATimeSpan.TotalSeconds;
   txtLog.Text := txtLog.Text + Format('Random Hash: %n H/S%s', [LHPS, sLineBreak]);
   txtLog.Text := txtLog.Text + Format('Random Hash: %n H/S%s', [LHPS, sLineBreak]);
 end;
 end;
 
 
@@ -188,8 +239,16 @@ procedure TFRMDiagnosticTool.OnRandomHash2Report(ATotalHashes : UInt32; const AT
 var
 var
  LHPS : Double;
  LHPS : Double;
 begin
 begin
-  LHPS := Double(ATotalHashes) / Double(ATimeSpan.TotalSeconds);
+  LHPS := Trunc(ATotalHashes) / ATimeSpan.TotalSeconds;
   txtLog.Text := txtLog.Text + Format('Random Hash 2: %n H/S%s', [LHPS, sLineBreak]);
   txtLog.Text := txtLog.Text + Format('Random Hash 2: %n H/S%s', [LHPS, sLineBreak]);
 end;
 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.
 end.

+ 36 - 4
src/gui-classic/UFRMWallet.dfm

@@ -421,6 +421,10 @@ object FRMWallet: TFRMWallet
     OnChange = PageControlChange
     OnChange = PageControlChange
     object tsMyAccounts: TTabSheet
     object tsMyAccounts: TTabSheet
       Caption = 'Account Explorer'
       Caption = 'Account Explorer'
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Splitter1: TSplitter
       object Splitter1: TSplitter
         Left = 400
         Left = 400
         Top = 66
         Top = 66
@@ -658,6 +662,10 @@ object FRMWallet: TFRMWallet
         TabOrder = 2
         TabOrder = 2
         object tsAccountOperations: TTabSheet
         object tsAccountOperations: TTabSheet
           Caption = 'Account Operations'
           Caption = 'Account Operations'
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgAccountOperations: TDrawGrid
           object dgAccountOperations: TDrawGrid
             Left = 0
             Left = 0
             Top = 0
             Top = 0
@@ -671,6 +679,10 @@ object FRMWallet: TFRMWallet
         object tsMultiSelectAccounts: TTabSheet
         object tsMultiSelectAccounts: TTabSheet
           Caption = 'Selected Accounts For Batch Operation'
           Caption = 'Selected Accounts For Batch Operation'
           ImageIndex = 1
           ImageIndex = 1
+          ExplicitLeft = 0
+          ExplicitTop = 0
+          ExplicitWidth = 0
+          ExplicitHeight = 0
           object dgSelectedAccounts: TDrawGrid
           object dgSelectedAccounts: TDrawGrid
             Left = 41
             Left = 41
             Top = 31
             Top = 31
@@ -862,6 +874,10 @@ object FRMWallet: TFRMWallet
     object tsPendingOperations: TTabSheet
     object tsPendingOperations: TTabSheet
       Caption = 'Pending Operations'
       Caption = 'Pending Operations'
       ImageIndex = 5
       ImageIndex = 5
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object dgPendingOperations: TDrawGrid
       object dgPendingOperations: TDrawGrid
         Left = 0
         Left = 0
         Top = 86
         Top = 86
@@ -908,6 +924,10 @@ object FRMWallet: TFRMWallet
     object tsBlockChain: TTabSheet
     object tsBlockChain: TTabSheet
       Caption = 'Block Explorer'
       Caption = 'Block Explorer'
       ImageIndex = 1
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel2: TPanel
       object Panel2: TPanel
         Left = 0
         Left = 0
         Top = 0
         Top = 0
@@ -1000,6 +1020,10 @@ object FRMWallet: TFRMWallet
     object tsOperations: TTabSheet
     object tsOperations: TTabSheet
       Caption = 'Operations Explorer'
       Caption = 'Operations Explorer'
       ImageIndex = 1
       ImageIndex = 1
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object Panel1: TPanel
       object Panel1: TPanel
         Left = 0
         Left = 0
         Top = 0
         Top = 0
@@ -1048,6 +1072,10 @@ object FRMWallet: TFRMWallet
     object tsLogs: TTabSheet
     object tsLogs: TTabSheet
       Caption = 'Logs'
       Caption = 'Logs'
       ImageIndex = 2
       ImageIndex = 2
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       object pnlTopLogs: TPanel
       object pnlTopLogs: TPanel
         Left = 0
         Left = 0
         Top = 0
         Top = 0
@@ -1079,6 +1107,10 @@ object FRMWallet: TFRMWallet
     object tsNodeStats: TTabSheet
     object tsNodeStats: TTabSheet
       Caption = 'Node Stats'
       Caption = 'Node Stats'
       ImageIndex = 3
       ImageIndex = 3
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
       DesignSize = (
         857
         857
         438)
         438)
@@ -1148,6 +1180,10 @@ object FRMWallet: TFRMWallet
     object tsMessages: TTabSheet
     object tsMessages: TTabSheet
       Caption = 'Messages'
       Caption = 'Messages'
       ImageIndex = 6
       ImageIndex = 6
+      ExplicitLeft = 0
+      ExplicitTop = 0
+      ExplicitWidth = 0
+      ExplicitHeight = 0
       DesignSize = (
       DesignSize = (
         857
         857
         438)
         438)
@@ -1353,10 +1389,6 @@ object FRMWallet: TFRMWallet
         Caption = 'About Pascal Coin...'
         Caption = 'About Pascal Coin...'
         OnClick = miAboutPascalCoinClick
         OnClick = miAboutPascalCoinClick
       end
       end
-      object miDiagnosticTool: TMenuItem
-        Caption = 'Diagnostic Tool'
-        OnClick = miDiagnosticToolClick
-      end
     end
     end
   end
   end
 end
 end

+ 7 - 3
src/gui-classic/UFRMWallet.pas

@@ -177,7 +177,6 @@ type
     MiFindOperationbyOpHash: TMenuItem;
     MiFindOperationbyOpHash: TMenuItem;
     MiAccountInformation: TMenuItem;
     MiAccountInformation: TMenuItem;
     MiOperationsExplorer: TMenuItem;
     MiOperationsExplorer: TMenuItem;
-    miDiagnosticTool: TMenuItem;
     procedure cbHashRateUnitsClick(Sender: TObject);
     procedure cbHashRateUnitsClick(Sender: TObject);
     procedure ebHashRateBackBlocksExit(Sender: TObject);
     procedure ebHashRateBackBlocksExit(Sender: TObject);
     procedure ebHashRateBackBlocksKeyPress(Sender: TObject; var Key: char);
     procedure ebHashRateBackBlocksKeyPress(Sender: TObject; var Key: char);
@@ -229,7 +228,7 @@ type
     procedure MiFindOperationbyOpHashClick(Sender: TObject);
     procedure MiFindOperationbyOpHashClick(Sender: TObject);
     procedure MiAccountInformationClick(Sender: TObject);
     procedure MiAccountInformationClick(Sender: TObject);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
     procedure FormClose(Sender: TObject; var Action: TCloseAction);
-    procedure miDiagnosticToolClick(Sender: TObject);
+    procedure Test_ShowDiagnosticTool(Sender: TObject);
   private
   private
     FLastNodesCacheUpdatedTS : TDateTime;
     FLastNodesCacheUpdatedTS : TDateTime;
     FBackgroundPanel : TPanel;
     FBackgroundPanel : TPanel;
@@ -1046,6 +1045,11 @@ begin
   mi.Caption:='Ask for Free Account';
   mi.Caption:='Ask for Free Account';
   mi.OnClick:=Test_AskForFreeAccount;
   mi.OnClick:=Test_AskForFreeAccount;
   miAbout.Add(mi);
   miAbout.Add(mi);
+  mi := TMenuItem.Create(MainMenu);
+  mi.Caption:='Diagnostic Tool';
+  mi.OnClick:=Test_ShowDiagnosticTool;
+  miAbout.Add(mi);
+
 end;
 end;
 
 
 {$IFDEF TESTING_NO_POW_CHECK}
 {$IFDEF TESTING_NO_POW_CHECK}
@@ -1615,7 +1619,7 @@ begin
   end;
   end;
 end;
 end;
 
 
-procedure TFRMWallet.miDiagnosticToolClick(Sender: TObject);
+procedure TFRMWallet.Test_ShowDiagnosticTool(Sender: TObject);
 var
 var
  LDialog : TFRMDiagnosticTool;
  LDialog : TFRMDiagnosticTool;
 begin
 begin

+ 15 - 1
src/libraries/sphere10/UCommon.pas

@@ -51,7 +51,8 @@ function Bytes2Hex(const ABytes: TBytes; AUsePrefix : boolean = false) : String;
 {$ENDIF}
 {$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 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 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: Cardinal): Cardinal; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Integer): Integer; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64; overload;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Int64): Int64; overload;
@@ -582,6 +583,7 @@ end;
 function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean;
 function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean;
 var ABytes1Len, ABytes2Len : Integer;
 var ABytes1Len, ABytes2Len : Integer;
 begin
 begin
+  Result := BytesEqual(ABytes1, ABytes2, 0, Length(ABytes1));
   ABytes1Len := Length(ABytes1);
   ABytes1Len := Length(ABytes1);
   ABytes2Len := Length(ABytes2);
   ABytes2Len := Length(ABytes2);
   if (ABytes1Len <> ABytes2Len) OR (ABytes1Len = 0) then
   if (ABytes1Len <> ABytes2Len) OR (ABytes1Len = 0) then
@@ -590,6 +592,18 @@ begin
     Result := CompareMem(@ABytes1[0], @ABytes2[0], ABytes1Len);
     Result := CompareMem(@ABytes1[0], @ABytes2[0], ABytes1Len);
 end;
 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 < ALength) OR (ABytes2Len < ALength) then
+    Exit(False);
+  Result := CompareMem(@ABytes1[AFrom], @ABytes2[AFrom], ALength);
+end;
+
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal;
 function IIF(const ACondition: Boolean; const ATrueResult, AFalseResult: Cardinal): Cardinal;
 begin
 begin
   if ACondition then
   if ACondition then

+ 3 - 2
src/pascalcoin_miner.lpi

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

+ 16 - 1
src/pascalcoin_wallet_classic.lpi

@@ -42,7 +42,7 @@
         <PackageName Value="LCL"/>
         <PackageName Value="LCL"/>
       </Item1>
       </Item1>
     </RequiredPackages>
     </RequiredPackages>
-    <Units Count="34">
+    <Units Count="37">
       <Unit0>
       <Unit0>
         <Filename Value="pascalcoin_wallet_classic.dpr"/>
         <Filename Value="pascalcoin_wallet_classic.dpr"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
@@ -207,6 +207,21 @@
         <Filename Value="gui-classic\UGUIUtils.pas"/>
         <Filename Value="gui-classic\UGUIUtils.pas"/>
         <IsPartOfProject Value="True"/>
         <IsPartOfProject Value="True"/>
       </Unit33>
       </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>
     </Units>
   </ProjectOptions>
   </ProjectOptions>
   <CompilerOptions>
   <CompilerOptions>

BIN
src/pascalcoin_wallet_classic.res