Browse Source

PIP-0036: Final RandomHash2 algorithm revision.
- Added 77 internal hash algorithms
- Updated unit tests
- Updated pasminer
- Updated PIP
- Final change.

Herman Schoenfeld 6 years ago
parent
commit
47b3bfcfef

+ 254 - 211
PIP/PIP-0036.md

@@ -23,15 +23,15 @@ In short, the purpose of RandomHash2 is to address the the need for faster block
 
 
 Whilst RandomHash appears to have empirically achieved it's GPU and ASIC resistivity goals, it's computationally-heavy nature has resulted in an unforeseen consequence of slow blockchain validation.
 Whilst RandomHash appears to have empirically achieved it's GPU and ASIC resistivity goals, it's computationally-heavy nature has resulted in an unforeseen consequence of slow blockchain validation.
 
 
-First of all, RandomHash introduces the concept of nonce-dependency between nonces. This means to evaluate a nonce, the partial evaluation of other random neighboring nonces is required. This allows RandomHash to operate in two modes, validation mode and mining mode. 
+First of all, RandomHash introduces the concept of nonce-dependencies between themselves. This means in order to evaluate the hash for a block-header with some nonce, the partial evaluation of the same header with random "neighbouring nonces" is required. This allows RandomHash to operate in two modes, validation mode and mining mode. 
 
 
-In validation mode, RandomHash is simply used to hash a block-header in order to verify it's the correct block in the blockchain -- just the same as how SHA2-256D does in Bitcoin.
+In validation mode, RandomHash is simply used to hash a block-header in order to verify it's position in a blockchain -- just the same as how SHA2-256D does in Bitcoin.
 
 
-In mining mode, RandomHash is used to mine the next block by enumerating many nonces to to find an acceptable Proof-of-Work for the next block. In this mode, RandomHash exploits the partial calculations from previous rounds by resuming them in subsequent hashing rounds. In RandomHash, this mode operates at twice the hashrate as validation mode, and is the basis for the "CPU Bias" and important to achieve GPU resistivity. It is biased towards CPU's since re-using the partially calculated nonces is a serial optimization that cannot be efficiently exploited in a parallelized, batch-computation context (such as GPU mining). In other words, the optimal nonce-set is enumerated on-the-fly and cannot be pre-determined into a range for parallel mining as GPU mining requires.
+In mining mode, RandomHash is used to mine the next block-header by enumerating many nonces to find an acceptable Proof-of-Work solution for the next block. In this mode, RandomHash exploits the partial calculations from previous rounds by resuming them in subsequent hashing rounds. In RandomHash 1, this mode operates at twice the hashrate as validation mode (200%), and is the basis for the "CPU Bias" and important to achieve GPU resistivity. It is biased towards CPU's since re-using the partially calculated nonces is a serial optimization that cannot be efficiently exploited in a parallelized, batch-computation context (such as GPU mining). In other words, the optimal nonce-set is enumerated on-the-fly and cannot be pre-determined into a range for parallel mining as GPU mining requires.
 
 
 However, on a typical 2019 desktop computer, the validation hashrate of RandomHash is approximately 20 hashes per second. At 288 blocks per day, that's 14 seconds to validate a days worth of blocks and over 1 hour to validate 1 year of blocks. Whilst multi-threading, performance tuning and other optimizations have significantly optimized this performance oversight, it remains a fundamental a long-term issue that needs to be resolved.
 However, on a typical 2019 desktop computer, the validation hashrate of RandomHash is approximately 20 hashes per second. At 288 blocks per day, that's 14 seconds to validate a days worth of blocks and over 1 hour to validate 1 year of blocks. Whilst multi-threading, performance tuning and other optimizations have significantly optimized this performance oversight, it remains a fundamental a long-term issue that needs to be resolved.
 
 
-RandomHash2 offers an order of magnitude improvement in both validation and mining hashrate. In RandomHash2, the same machine validates at ~300 hashes per second yet mines at ~1,000 hashes per second with far less memory-hardness. Whilst not empirically tested against GPU performance, these numbers suggest a 333% CPU bias.
+RandomHash2 offers an order of magnitude improvement in both validation and mining hashrate. In RandomHash2, the same machine validates at ~400 hashes per second yet mines at ~2,000 hashes per second with far less memory-hardness. Whilst not empirically tested against GPU performance, these numbers suggest a 500% CPU bias.
 
 
 #### RandomHash vs RandomHash2 Measured Performance
 #### RandomHash vs RandomHash2 Measured Performance
 
 
@@ -39,8 +39,8 @@ RandomHash2 offers an order of magnitude improvement in both validation and mini
 | :------------------------- | :------------------------ | :-------------------- | :------------------- | :------------------- | :------------------ |
 | :------------------------- | :------------------------ | :-------------------- | :------------------- | :------------------- | :------------------ |
 | RandomHash (validation)    | 23                        | 5,018,876             | 5,018,628            | 5,019,116            | 83.86               |
 | RandomHash (validation)    | 23                        | 5,018,876             | 5,018,628            | 5,019,116            | 83.86               |
 | RandomHash (mining)        | 44                        | 7,528,288             | 7,527,872            | 7,528,732            | 136                 |
 | RandomHash (mining)        | 44                        | 7,528,288             | 7,527,872            | 7,528,732            | 136                 |
-| RandomHash2 (validation)   | 309                       | 16,719                | 1,380                | 221,536              | 29,420              |
-| **RandomHash2 (mining)**   | 1,051                     | 16,693                | 1,312                | 251,104              | 29,374              |
+| RandomHash2 (validation)   | 401                       | 16,361                | 560                  | 73,872               | 14,934              |
+| **RandomHash2 (mining)**   | 2,109                     | 74,642                | 1,048                | 157,944              | 15,460              |
 
 
 _**Machine**: AMD FX-8150 8 Core 3.60 Ghz utilizing 1 thread_
 _**Machine**: AMD FX-8150 8 Core 3.60 Ghz utilizing 1 thread_
 
 
@@ -58,7 +58,7 @@ RandomHash2 is similarly structured to RandomHash, but has some key differences.
 2. ```N``` varies per nonce in a non-deterministic manner between ```MIN_N=2``` and ```MAX_N=4```, inclusive;
 2. ```N``` varies per nonce in a non-deterministic manner between ```MIN_N=2``` and ```MAX_N=4```, inclusive;
 3. Each level in (1) also depends on ```J``` neighboring nonces, determined randomly and non-deterministically;
 3. Each level in (1) also depends on ```J``` neighboring nonces, determined randomly and non-deterministically;
 4. The value ```J``` is restricted to ```MIN_J=0``` and ```MAX_J=4```;
 4. The value ```J``` is restricted to ```MIN_J=0``` and ```MAX_J=4```;
-5. Each level selects a random hash function from a set of 18 well-known hash algorithms;
+5. Each level selects a random hash function from a set of 77 well-known cryptographic hash algorithms;
 6. The input digest hashed at a level is the compression of the transitive closure of the hash outputs of all it's prior-levels (1) and it's neighbouring nonce's prior-levels (3);
 6. The input digest hashed at a level is the compression of the transitive closure of the hash outputs of all it's prior-levels (1) and it's neighbouring nonce's prior-levels (3);
 7. The output of every level is expanded for (low) memory-hardness;
 7. The output of every level is expanded for (low) memory-hardness;
 8. Randomness is generated using ```Mersenne Twister``` algorithm;
 8. Randomness is generated using ```Mersenne Twister``` algorithm;
@@ -74,7 +74,7 @@ The primary differences to predecessor are:
 ```pascal
 ```pascal
     MIN_N = 2;
     MIN_N = 2;
     MAX_N = 4;
     MAX_N = 4;
-    MIN_J = 0;
+    MIN_J = 1;
     MAX_J = 8;
     MAX_J = 8;
     M = 256;
     M = 256;
 ```
 ```
@@ -128,13 +128,105 @@ if LNeighbourWasLastRound then begin
 end; 
 end; 
 ```
 ```
 
 
+**There are now 77 internally used hash algorithms, not 18**
+
+In order to strengthen ASIC and GPU resistance, RandomHash2 now employs 77 internal hash algorithms to transform the data. This moves the economic costs of an ASIC far beyond viability for any rational economic actor, and since the sequence in which these 77 hash algorithms are used is in-determinable and random on a nonce-by-nonce basis. This number is also such that it makes GPU-programs (kernels) similarly non-viable, although not to the same degree as an ASIC.
+
+The below are the algorithms in their indices as they appear within RandomHash2 algorithm.
+
+Blake2B_160, Blake2B_256, Blake2B_512, Blake2B_384, Blake2S_128, Blake2S_160, Blake2S_224, Blake2S_256, Gost, GOST3411_2012_256, GOST3411_2012_512, Grindahl256, Grindahl512, HAS160, Haval_3_128, Haval_3_160, Haval_3_192, Haval_3_224, Haval_3_256, Haval_4_128, Haval_4_160, Haval_4_192, Haval_4_224, Haval_4_256, Haval_5_128, Haval_5_160, Haval_5_192, Haval_5_224, Haval_5_256, Keccak_224, Keccak_256, Keccak_288, Keccak_384, Keccak_512, MD2, MD5, MD4, Panama, RadioGatun32, RIPEMD, RIPEMD128, RIPEMD160, RIPEMD256, RIPEMD320, SHA0, SHA1, SHA2_224, SHA2_256, SHA2_384, SHA2_512, SHA2_512_224, SHA2_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512, Snefru_8_128, Snefru_8_256, Tiger_3_128, Tiger_3_160, Tiger_3_192, Tiger_4_128, Tiger_4_160, Tiger_4_192, Tiger_5_128, Tiger_5_160, Tiger_5_192, Tiger2_3_128, Tiger2_3_160, Tiger2_3_192, Tiger2_4_128, Tiger2_4_160, Tiger2_4_192, Tiger2_5_128, Tiger2_5_160, Tiger2_5_192, WhirlPool 
+
+```pascal
+
+  FHashAlg[0] := THashFactory.TCrypto.CreateBlake2B_160();
+  FHashAlg[1] := THashFactory.TCrypto.CreateBlake2B_256();
+  FHashAlg[2] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateBlake2B_384();
+  FHashAlg[4] := THashFactory.TCrypto.CreateBlake2S_128();
+  FHashAlg[5] := THashFactory.TCrypto.CreateBlake2S_160();
+  FHashAlg[6] := THashFactory.TCrypto.CreateBlake2S_224();
+  FHashAlg[7] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateGost();
+  FHashAlg[9] := THashFactory.TCrypto.CreateGOST3411_2012_256();
+  FHashAlg[10] := THashFactory.TCrypto.CreateGOST3411_2012_512();
+  FHashAlg[11] := THashFactory.TCrypto.CreateGrindahl256();
+  FHashAlg[12] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[13] := THashFactory.TCrypto.CreateHAS160();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_3_128();
+  FHashAlg[15] := THashFactory.TCrypto.CreateHaval_3_160();
+  FHashAlg[16] := THashFactory.TCrypto.CreateHaval_3_192();
+  FHashAlg[17] := THashFactory.TCrypto.CreateHaval_3_224();
+  FHashAlg[18] := THashFactory.TCrypto.CreateHaval_3_256();
+  FHashAlg[19] := THashFactory.TCrypto.CreateHaval_4_128();
+  FHashAlg[20] := THashFactory.TCrypto.CreateHaval_4_160();
+  FHashAlg[21] := THashFactory.TCrypto.CreateHaval_4_192();
+  FHashAlg[22] := THashFactory.TCrypto.CreateHaval_4_224();
+  FHashAlg[23] := THashFactory.TCrypto.CreateHaval_4_256();
+  FHashAlg[24] := THashFactory.TCrypto.CreateHaval_5_128();
+  FHashAlg[25] := THashFactory.TCrypto.CreateHaval_5_160();
+  FHashAlg[26] := THashFactory.TCrypto.CreateHaval_5_192();
+  FHashAlg[27] := THashFactory.TCrypto.CreateHaval_5_224();
+  FHashAlg[28] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[29] := THashFactory.TCrypto.CreateKeccak_224();
+  FHashAlg[30] := THashFactory.TCrypto.CreateKeccak_256();
+  FHashAlg[31] := THashFactory.TCrypto.CreateKeccak_288();
+  FHashAlg[32] := THashFactory.TCrypto.CreateKeccak_384();
+  FHashAlg[33] := THashFactory.TCrypto.CreateKeccak_512();
+  FHashAlg[34] := THashFactory.TCrypto.CreateMD2();
+  FHashAlg[35] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[36] := THashFactory.TCrypto.CreateMD4();
+  FHashAlg[37] := THashFactory.TCrypto.CreatePanama();
+  FHashAlg[38] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[39] := THashFactory.TCrypto.CreateRIPEMD();
+  FHashAlg[40] := THashFactory.TCrypto.CreateRIPEMD128();
+  FHashAlg[41] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[42] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[43] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[44] := THashFactory.TCrypto.CreateSHA0();
+  FHashAlg[45] := THashFactory.TCrypto.CreateSHA1();
+  FHashAlg[46] := THashFactory.TCrypto.CreateSHA2_224();
+  FHashAlg[47] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[48] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[49] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[50] := THashFactory.TCrypto.CreateSHA2_512_224();
+  FHashAlg[51] := THashFactory.TCrypto.CreateSHA2_512_256();
+  FHashAlg[52] := THashFactory.TCrypto.CreateSHA3_224();
+  FHashAlg[53] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[54] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[55] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[56] := THashFactory.TCrypto.CreateSnefru_8_128();
+  FHashAlg[57] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[58] := THashFactory.TCrypto.CreateTiger_3_128();
+  FHashAlg[59] := THashFactory.TCrypto.CreateTiger_3_160();
+  FHashAlg[60] := THashFactory.TCrypto.CreateTiger_3_192();
+  FHashAlg[61] := THashFactory.TCrypto.CreateTiger_4_128();
+  FHashAlg[62] := THashFactory.TCrypto.CreateTiger_4_160();
+  FHashAlg[63] := THashFactory.TCrypto.CreateTiger_4_192();
+  FHashAlg[64] := THashFactory.TCrypto.CreateTiger_5_128();
+  FHashAlg[65] := THashFactory.TCrypto.CreateTiger_5_160();
+  FHashAlg[66] := THashFactory.TCrypto.CreateTiger_5_192();
+  FHashAlg[67] := THashFactory.TCrypto.CreateTiger2_3_128();
+  FHashAlg[68] := THashFactory.TCrypto.CreateTiger2_3_160();
+  FHashAlg[69] := THashFactory.TCrypto.CreateTiger2_3_192();
+  FHashAlg[70] := THashFactory.TCrypto.CreateTiger2_4_128();
+  FHashAlg[71] := THashFactory.TCrypto.CreateTiger2_4_160();
+  FHashAlg[72] := THashFactory.TCrypto.CreateTiger2_4_192();
+  FHashAlg[73] := THashFactory.TCrypto.CreateTiger2_5_128();
+  FHashAlg[74] := THashFactory.TCrypto.CreateTiger2_5_160();
+  FHashAlg[75] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[76] := THashFactory.TCrypto.CreateWhirlPool(); 
+
+
+```
+
+
 ### Analysis
 ### Analysis
  
  
 #### Cryptographic Strength
 #### Cryptographic Strength
 
 
 Since hashing starts and ends with a ```SHA2-256``` the cryptographic strength of RandomHash2 is **at least** that of ```SHA2-256D``` as used in Bitcoin. Even if one were to assume the data transformations in between the start/end were cryptographically insecure, it wouldn't change this minimum security guarantee.
 Since hashing starts and ends with a ```SHA2-256``` the cryptographic strength of RandomHash2 is **at least** that of ```SHA2-256D``` as used in Bitcoin. Even if one were to assume the data transformations in between the start/end were cryptographically insecure, it wouldn't change this minimum security guarantee.
 
 
-However, the transformations in between are not weak and involve the use of 18 other cryptographically strong hash algorithms. As a result, RandomHash2 is orders of magnitude more stronger than standard cryptographic hash algorithms since they are combined in random, non-determinstic ways. However this achievement is paid for by significant performance overhead (which is intentional).
+However, the transformations in between are not weak and involve the use of 77 other cryptographically strong hash algorithms. As a result, RandomHash2 is orders of magnitude more stronger than standard cryptographic hash algorithms since they are combined in random, non-determinstic ways. However this achievement is paid for by significant performance overhead (which is intentional).
 
 
 However, within the 18 hash algorithms used, some are considered "cryptographically weak" such as MD5. The use of some weak algorithms is inconsequential to overall security since their purpose is not to add to security but to computational complexity to prevent ASIC manufacturing.
 However, within the 18 hash algorithms used, some are considered "cryptographically weak" such as MD5. The use of some weak algorithms is inconsequential to overall security since their purpose is not to add to security but to computational complexity to prevent ASIC manufacturing.
 
 
@@ -171,13 +263,14 @@ In order to determine if this balance is achieved, an empirical nonce-scanning a
 
 
 | N       | Mean Hashrate (H/s)       | Mean Mem Per Hash (b) | Min Mem Per Hash (b) | Max Mem Per Hash (b) | Sample Std Dev. (b) |
 | N       | Mean Hashrate (H/s)       | Mean Mem Per Hash (b) | Min Mem Per Hash (b) | Max Mem Per Hash (b) | Sample Std Dev. (b) |
 | :------ | :------------------------ | :-------------------- | :------------------- | :------------------- | :------------------ |
 | :------ | :------------------------ | :-------------------- | :------------------- | :------------------- | :------------------ |
-| 2 (min) | 240                       | 4,175                 | 1,312                | 7,184                | 1,854               |
-| 3       | 651                       | 5,984                 | 1,312                | 49,436               | 6,293               |
-| 4 (max) | 1,051                     | 16,693                | 1,312                | 251,104              | 29,374              |
+| 2 (min) | 964                       | 1,462                 | 760                  | 2,292                | 443                 |
+| 3       | 1,708                     | 1,978                 | 760                  | 14,844               | 6,293               |
+| 4 (max) | 2,109                     | 74,642                | 1,048                | 157,944              | 15,460              |
+
 
 
 _**Machine**: AMD FX-8150 8 Core 3.60 Ghz utilizing 1 thread_
 _**Machine**: AMD FX-8150 8 Core 3.60 Ghz utilizing 1 thread_
 
 
-As the above table shows, this balance is achieved. Nonce-scanning (via CPU) yields no benefit whatsoever and in fact incurs a hashrate penalty. Also, it is the opinion of the author that any future optimization would change this balance since it would benefit all levels proportionally. However, a line of inquiry is to investigate if whether or not the reduced memory-hardness of nonce-scanning may yield a benefit for GPU-based nonce-scanning attack. In any event, the result of this attack is only to gain higher hashrate and **does not compromise the cryptographic security** of the blockchain whatsoever.
+As the above table shows, this balance is achieved. Nonce-scanning (via CPU) yields no observable benefit and in fact incurs a hashrate penalty. Also, it is the opinion of the author that any future optimization would **not** change this balance since an optimization would benefit all nonce scanning levels proportionally. However, a worthy line of inquiry is whether or not the reduced memory-hardness of nonce-scanning at lower levels may yield some optimization benefit for GPU-based miner. In any event, the result of this attack is only to gain higher hashrate and **does not compromise the cryptographic security** of the blockchain whatsoever.
  
  
 #### CPU Bias
 #### CPU Bias
 
 
@@ -217,108 +310,132 @@ A reference implementation of RandomHash can be found [here][2]. A full implemen
 
 
 ```pascal
 ```pascal
 
 
-  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;
-
-    private
-      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 CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean; 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 TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
-      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';
+TRandomHash2 = class sealed(TObject)
+const
+    MIN_N = 2; // Min-number of hashing rounds required to compute a nonce
+    MAX_N = 4; // Max-number of hashing rounds required to compute a nonce
+    MIN_J = 1; // 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 = 64;    // The memory expansion unit (in bytes)
+    NUM_HASH_ALGO = 77; // Number of internal cryptographic hash algorithms used
+    SHA2_256_IX = 47; // index of SHA2-256 within the 77 algorithms used
+
+private
+    FHashAlg : array[0..NUM_HASH_ALGO-1] of IHash;  // declared here to avoid race-condition during mining
+
+    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 ComputeVeneerRound(const ARoundOutputs : TArray<TBytes>) : TBytes; inline;
+    function CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean; overload;
+public
+    constructor Create;
+    destructor Destroy; override;
+    function TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
+    function Hash(const ABlockHeader: TBytes): TBytes; overload; inline;
+    class function Compute(const ABlockHeader: TBytes): TBytes; overload; static; inline;
+end; 
 
 
-implementation
+{ ERandomHash2 }
 
 
-uses UMemory, URandomHash;
+ERandomHash2 = class(Exception);
 
 
-{ TRandomHash2 }
+{ TRandomHash2 implementation }
 
 
 constructor TRandomHash2.Create;
 constructor TRandomHash2.Create;
 begin
 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();
+  FHashAlg[0] := THashFactory.TCrypto.CreateBlake2B_160();
+  FHashAlg[1] := THashFactory.TCrypto.CreateBlake2B_256();
+  FHashAlg[2] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateBlake2B_384();
+  FHashAlg[4] := THashFactory.TCrypto.CreateBlake2S_128();
+  FHashAlg[5] := THashFactory.TCrypto.CreateBlake2S_160();
+  FHashAlg[6] := THashFactory.TCrypto.CreateBlake2S_224();
+  FHashAlg[7] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateGost();
+  FHashAlg[9] := THashFactory.TCrypto.CreateGOST3411_2012_256();
+  FHashAlg[10] := THashFactory.TCrypto.CreateGOST3411_2012_512();
+  FHashAlg[11] := THashFactory.TCrypto.CreateGrindahl256();
+  FHashAlg[12] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[13] := THashFactory.TCrypto.CreateHAS160();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_3_128();
+  FHashAlg[15] := THashFactory.TCrypto.CreateHaval_3_160();
+  FHashAlg[16] := THashFactory.TCrypto.CreateHaval_3_192();
+  FHashAlg[17] := THashFactory.TCrypto.CreateHaval_3_224();
+  FHashAlg[18] := THashFactory.TCrypto.CreateHaval_3_256();
+  FHashAlg[19] := THashFactory.TCrypto.CreateHaval_4_128();
+  FHashAlg[20] := THashFactory.TCrypto.CreateHaval_4_160();
+  FHashAlg[21] := THashFactory.TCrypto.CreateHaval_4_192();
+  FHashAlg[22] := THashFactory.TCrypto.CreateHaval_4_224();
+  FHashAlg[23] := THashFactory.TCrypto.CreateHaval_4_256();
+  FHashAlg[24] := THashFactory.TCrypto.CreateHaval_5_128();
+  FHashAlg[25] := THashFactory.TCrypto.CreateHaval_5_160();
+  FHashAlg[26] := THashFactory.TCrypto.CreateHaval_5_192();
+  FHashAlg[27] := THashFactory.TCrypto.CreateHaval_5_224();
+  FHashAlg[28] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[29] := THashFactory.TCrypto.CreateKeccak_224();
+  FHashAlg[30] := THashFactory.TCrypto.CreateKeccak_256();
+  FHashAlg[31] := THashFactory.TCrypto.CreateKeccak_288();
+  FHashAlg[32] := THashFactory.TCrypto.CreateKeccak_384();
+  FHashAlg[33] := THashFactory.TCrypto.CreateKeccak_512();
+  FHashAlg[34] := THashFactory.TCrypto.CreateMD2();
+  FHashAlg[35] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[36] := THashFactory.TCrypto.CreateMD4();
+  FHashAlg[37] := THashFactory.TCrypto.CreatePanama();
+  FHashAlg[38] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[39] := THashFactory.TCrypto.CreateRIPEMD();
+  FHashAlg[40] := THashFactory.TCrypto.CreateRIPEMD128();
+  FHashAlg[41] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[42] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[43] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[44] := THashFactory.TCrypto.CreateSHA0();
+  FHashAlg[45] := THashFactory.TCrypto.CreateSHA1();
+  FHashAlg[46] := THashFactory.TCrypto.CreateSHA2_224();
+  FHashAlg[47] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[48] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[49] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[50] := THashFactory.TCrypto.CreateSHA2_512_224();
+  FHashAlg[51] := THashFactory.TCrypto.CreateSHA2_512_256();
+  FHashAlg[52] := THashFactory.TCrypto.CreateSHA3_224();
+  FHashAlg[53] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[54] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[55] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[56] := THashFactory.TCrypto.CreateSnefru_8_128();
+  FHashAlg[57] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[58] := THashFactory.TCrypto.CreateTiger_3_128();
+  FHashAlg[59] := THashFactory.TCrypto.CreateTiger_3_160();
+  FHashAlg[60] := THashFactory.TCrypto.CreateTiger_3_192();
+  FHashAlg[61] := THashFactory.TCrypto.CreateTiger_4_128();
+  FHashAlg[62] := THashFactory.TCrypto.CreateTiger_4_160();
+  FHashAlg[63] := THashFactory.TCrypto.CreateTiger_4_192();
+  FHashAlg[64] := THashFactory.TCrypto.CreateTiger_5_128();
+  FHashAlg[65] := THashFactory.TCrypto.CreateTiger_5_160();
+  FHashAlg[66] := THashFactory.TCrypto.CreateTiger_5_192();
+  FHashAlg[67] := THashFactory.TCrypto.CreateTiger2_3_128();
+  FHashAlg[68] := THashFactory.TCrypto.CreateTiger2_3_160();
+  FHashAlg[69] := THashFactory.TCrypto.CreateTiger2_3_192();
+  FHashAlg[70] := THashFactory.TCrypto.CreateTiger2_4_128();
+  FHashAlg[71] := THashFactory.TCrypto.CreateTiger2_4_160();
+  FHashAlg[72] := THashFactory.TCrypto.CreateTiger2_4_192();
+  FHashAlg[73] := THashFactory.TCrypto.CreateTiger2_5_128();
+  FHashAlg[74] := THashFactory.TCrypto.CreateTiger2_5_160();
+  FHashAlg[75] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[76] := THashFactory.TCrypto.CreateWhirlPool();
 end;
 end;
 
 
 destructor TRandomHash2.Destroy;
 destructor TRandomHash2.Destroy;
 var i : integer;
 var i : integer;
 begin
 begin
- FCachedHashes.Clear;
- FreeAndNil(FCachedHashes);
- FMurmurHash3_x86_32 := nil;
- for i := Low(FHashAlg) to High(FHashAlg) do
-   FHashAlg[i] := nil;
- inherited Destroy;
+  for i := Low(FHashAlg) to High(FHashAlg) do
+    FHashAlg[i] := nil;
+  inherited Destroy;
 end;
 end;
 
 
 class function TRandomHash2.Compute(const ABlockHeader: TBytes): TBytes;
 class function TRandomHash2.Compute(const ABlockHeader: TBytes): TBytes;
@@ -353,7 +470,7 @@ var
 begin
 begin
   LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
   LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
   // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
   // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
-  Result := FHashAlg[0].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
+  Result := FHashAlg[SHA2_256_IX].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
 end;
 end;
 
 
 function TRandomHash2.CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean;
 function TRandomHash2.CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean;
@@ -363,7 +480,6 @@ var
   LSeed, LNumNeighbours: UInt32;
   LSeed, LNumNeighbours: UInt32;
   LGen: TMersenne32;
   LGen: TMersenne32;
   LRoundInput, LNeighbourNonceHeader, LOutput : TBytes;
   LRoundInput, LNeighbourNonceHeader, LOutput : TBytes;
-  LCachedHash : TCachedHash;
   LParentOutputs, LNeighborOutputs, LToArray, LBuffs2: TArray<TBytes>;
   LParentOutputs, LNeighborOutputs, LToArray, LBuffs2: TArray<TBytes>;
   LHashFunc: IHash;
   LHashFunc: IHash;
   i: Int32;
   i: Int32;
@@ -376,7 +492,7 @@ begin
   LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
   LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
   LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
   LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
   if ARound = 1 then begin
   if ARound = 1 then begin
-    LRoundInput := FHashAlg[0].ComputeBytes(ABlockHeader).GetBytes;
+    LRoundInput := FHashAlg[SHA2_256_IX].ComputeBytes(ABlockHeader).GetBytes;
     LSeed := GetLastDWordLE( LRoundInput );
     LSeed := GetLastDWordLE( LRoundInput );
     LGen.Initialize(LSeed);
     LGen.Initialize(LSeed);
   end else begin
   end else begin
@@ -397,19 +513,6 @@ begin
       LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
       LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
       LNeighbourWasLastRound := CalculateRoundOutputs(LNeighbourNonceHeader, ARound - 1, LNeighborOutputs);
       LNeighbourWasLastRound := CalculateRoundOutputs(LNeighbourNonceHeader, ARound - 1, LNeighborOutputs);
       LRoundOutputs.AddRange(LNeighborOutputs);
       LRoundOutputs.AddRange(LNeighborOutputs);
-
-      // If neighbour was a fully evaluated nonce, cache it for re-use
-      if LNeighbourWasLastRound 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;
     end;
     // Compress the parent/neighbouring outputs to form this rounds input
     // Compress the parent/neighbouring outputs to form this rounds input
     LRoundInput := Compress( LRoundOutputs.ToArray, LGen.NextUInt32 );
     LRoundInput := Compress( LRoundOutputs.ToArray, LGen.NextUInt32 );
@@ -428,39 +531,6 @@ begin
   Result := (ARound = MAX_N) OR ((ARound >= MIN_N) AND (GetLastDWordLE(LOutput) MOD MAX_N = 0));
   Result := (ARound = MAX_N) OR ((ARound >= MIN_N) AND (GetLastDWordLE(LOutput) MOD MAX_N = 0));
 end;
 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;
 function TRandomHash2.Compress(const AInputs : TArray<TBytes>; ASeed : UInt32): TBytes;
 var
 var
   i: Int32;
   i: Int32;
@@ -477,32 +547,39 @@ 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;
+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
 begin
-  Result := FCachedHashes.Last;
-  FCachedHashes.Delete(FCachedHashes.Count - 1);
-end;
+  LGen := LDisposables.AddObject( TMersenne32.Create (ASeed) ) as TMersenne32;
+  LSize := Length(AInput) + (AExpansionFactor * M);
+  LOutput := Copy(AInput);
+  LBytesToAdd := LSize - Length(AInput);
 
 
-function TRandomHash2.PeekCachedHash : TCachedHash;
-begin
-  Result := FCachedHashes.Last;
-end;
+  while LBytesToAdd > 0 do
+  begin
+    LNextChunk := Copy(LOutput);
+    if Length(LNextChunk) > LBytesToAdd then
+      SetLength(LNextChunk, LBytesToAdd);
 
 
-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));
+    LRandom := LGen.NextUInt32;
+    case LRandom mod 8 of
+      0: LOutput := ContencateBytes(LOutput, MemTransform1(LNextChunk));
+      1: LOutput := ContencateBytes(LOutput, MemTransform2(LNextChunk));
+      2: LOutput := ContencateBytes(LOutput, MemTransform3(LNextChunk));
+      3: LOutput := ContencateBytes(LOutput, MemTransform4(LNextChunk));
+      4: LOutput := ContencateBytes(LOutput, MemTransform5(LNextChunk));
+      5: LOutput := ContencateBytes(LOutput, MemTransform6(LNextChunk));
+      6: LOutput := ContencateBytes(LOutput, MemTransform7(LNextChunk));
+      7: LOutput := ContencateBytes(LOutput, MemTransform8(LNextChunk));
+    end;
+    LBytesToAdd := LBytesToAdd - Length(LNextChunk);
+  end;
+  Result := LOutput;
 end;
 end;
 
 
 function TRandomHash2.MemTransform1(const AChunk: TBytes): TBytes;
 function TRandomHash2.MemTransform1(const AChunk: TBytes): TBytes;
@@ -620,43 +697,9 @@ begin
   for i := 0 to High(AChunk) do
   for i := 0 to High(AChunk) do
     Result[i] := TBits.RotateRight8(AChunk[i], LChunkLength - i);
     Result[i] := TBits.RotateRight8(AChunk[i], LChunkLength - i);
 end;
 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. 
 
 
 ```
 ```
 
 

+ 18 - 9
src/core/UPoolMinerThreads.pas

@@ -802,7 +802,7 @@ Var
   LRoundJobNum : Integer;
   LRoundJobNum : Integer;
   AuxStats : TMinerStats;
   AuxStats : TMinerStats;
   dstep : Integer;
   dstep : Integer;
-  LUseRandomHash : boolean;
+  LUseRandomHash, LUseCachedHash : boolean;
   LRandomHasher : TRandomHashFast;
   LRandomHasher : TRandomHashFast;
   LRandomHasher2 : TRandomHash2Fast;
   LRandomHasher2 : TRandomHash2Fast;
   LCachedItem : TRandomHash2Fast.TCachedHash;
   LCachedItem : TRandomHash2Fast.TCachedHash;
@@ -818,6 +818,8 @@ begin
   LRandomHasher2 := LDisposables.AddObject( TRandomHash2Fast.Create ) as TRandomHash2Fast;
   LRandomHasher2 := LDisposables.AddObject( TRandomHash2Fast.Create ) as TRandomHash2Fast;
   LResultsToCheck := LDisposables.AddObject( TList<TNonceResult>.Create ) as TList<TNonceResult>;
   LResultsToCheck := LDisposables.AddObject( TList<TNonceResult>.Create ) as TList<TNonceResult>;
   LRandomHasher2.EnableCaching := True;
   LRandomHasher2.EnableCaching := True;
+  LRandomHasher2.Cache.EnablePartiallyComputed:=True;
+  LUseCachedHash := False;
   Try
   Try
     while (Not Terminated) And (Not FCPUDeviceThread.Terminated) do begin
     while (Not Terminated) And (Not FCPUDeviceThread.Terminated) do begin
       Try
       Try
@@ -848,14 +850,14 @@ begin
           // Timestamp
           // Timestamp
           ts := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
           ts := UnivDateTimeToUnix(DateTime2UnivDateTime(now));
           if ts<=FCurrentMinerValuesForWork.timestamp then ts := FCurrentMinerValuesForWork.timestamp+1;
           if ts<=FCurrentMinerValuesForWork.timestamp then ts := FCurrentMinerValuesForWork.timestamp+1;
-
           If FDigestStreamMsg.Size>8 then begin
           If FDigestStreamMsg.Size>8 then begin
             if FCPUDeviceThread.FUseOpenSSLFunctions OR LUseRandomHash then begin
             if FCPUDeviceThread.FUseOpenSSLFunctions OR LUseRandomHash then begin
 
 
               // update timestamp
               // update timestamp
               FDigestStreamMsg.Position:=FDigestStreamMsg.Size - 8;
               FDigestStreamMsg.Position:=FDigestStreamMsg.Size - 8;
               FDigestStreamMsg.Write(ts,4);
               FDigestStreamMsg.Write(ts,4);
-
+              LRandomHasher2.Cache.Clear;
+              LUseCachedHash:=False;
               baseHashingTC:=TPlatform.GetTickCount;
               baseHashingTC:=TPlatform.GetTickCount;
               dstep := 4;
               dstep := 4;
               LRoundJobNum := FJobNum;
               LRoundJobNum := FJobNum;
@@ -879,8 +881,12 @@ begin
                     LResultsToCheck.Add(LNonceResult);
                     LResultsToCheck.Add(LNonceResult);
                     Inc(LRoundsPerformed);
                     Inc(LRoundsPerformed);
                   end else begin
                   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);
+                    if LUseCachedHash then begin
+                      //if NOT BytesEqual(FDigestStreamMsg.ToBytes, LCachedItem.Header) then raise Exception.Create('Cache consistency bug');
+                      resultPow := LRandomHasher2.ResumeHash(LCachedItem);
+                      LUseCachedHash := False;
+                    end else
+                      resultPoW := LRandomHasher2.Hash(FDigestStreamMsg.ToBytes);
                     LNonceResult.Nonce := nonce;
                     LNonceResult.Nonce := nonce;
                     LNonceResult.PoW := resultPoW;
                     LNonceResult.PoW := resultPoW;
                     LResultsToCheck.Add(LNonceResult);
                     LResultsToCheck.Add(LNonceResult);
@@ -920,11 +926,14 @@ begin
 
 
                 // select next nonce
                 // select next nonce
                 if LUseRandomHash then begin
                 if LUseRandomHash then begin
-                  if (FCurrentMinerValuesForWork.version < CT_PROTOCOL_5) then begin
-                    nonce := LRandomHasher.NextNonce;
-                  end else begin
+                  if (FCurrentMinerValuesForWork.version < CT_PROTOCOL_5) then
+                    nonce := LRandomHasher.NextNonce
+                  else if LRandomHasher2.Cache.HasNextPartiallyComputedHash then begin
+                    LCachedItem := LRandomHasher2.Cache.PopNextPartiallyComputedHash;
+                    LUseCachedHash := True;
+                    Nonce := LCachedItem.Nonce;
+                  end else
                     nonce := Random(FMaxNOnce - FMinNOnce) + FMinNOnce;
                     nonce := Random(FMaxNOnce - FMinNOnce) + FMinNOnce;
-                  end;
                 end else if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
                 end else if (nonce)<FMaxNOnce then inc(nonce) else nonce := FMinNOnce;
               end;
               end;
               finalHashingTC:=TPlatform.GetTickCount;
               finalHashingTC:=TPlatform.GetTickCount;

+ 189 - 79
src/core/URandomHash2.pas

@@ -1,6 +1,6 @@
 unit URandomHash2;
 unit URandomHash2;
 
 
-{ Copyright (c) 2018 by Herman Schoenfeld
+{ Copyright (c) 2019 by Herman Schoenfeld
 
 
   RandomHash2 Reference Implementation
   RandomHash2 Reference Implementation
 
 
@@ -110,14 +110,12 @@ type
       MIN_J = 1; // Min-number of dependent neighbouring nonces required to evaluate a nonce round
       MIN_J = 1; // 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
       MAX_J = 8; // Max-number of dependent neighbouring nonces required to evaluate a nonce round
       M = 64;    // The memory expansion unit (in bytes)
       M = 64;    // The memory expansion unit (in bytes)
-      NUM_HASH_ALGO = 18;
+      NUM_HASH_ALGO = 77;
+      SHA2_256_IX = 47;
 
 
     {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
     {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
-      FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
-      FMemStats : TStatistics;
-      FCaptureMemStats : Boolean;
+      FHashAlg : array[0..NUM_HASH_ALGO-1] of IHash;  // declared here to avoid race-condition during mining
 
 
-      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;
       function MemTransform3(const AChunk: TBytes): TBytes; inline;
       function MemTransform3(const AChunk: TBytes): TBytes; inline;
@@ -133,7 +131,6 @@ type
     public
     public
       constructor Create;
       constructor Create;
       destructor Destroy; override;
       destructor Destroy; override;
-      property CaptureMemStats : Boolean read FCaptureMemStats write FCaptureMemStats;
       function TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
       function TryHash(const ABlockHeader: TBytes; AMaxRound : UInt32; out AHash : TBytes) : Boolean;
       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;
@@ -148,8 +145,8 @@ type
       MIN_J = 1; // Min-number of dependent neighbouring nonces required to evaluate a nonce round
       MIN_J = 1; // 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
       MAX_J = 8; // Max-number of dependent neighbouring nonces required to evaluate a nonce round
       M = 64;    // The memory expansion unit (in bytes)
       M = 64;    // The memory expansion unit (in bytes)
-      NUM_HASH_ALGO = 18;
-
+      NUM_HASH_ALGO = 77;
+      SHA2_256_IX = 47;
       public type
       public type
 
 
         TCachedHash = record
         TCachedHash = record
@@ -166,29 +163,29 @@ type
             FHeaderTemplate : TBytes;
             FHeaderTemplate : TBytes;
             FComputed : TList<TCachedHash>;
             FComputed : TList<TCachedHash>;
             FPartiallyComputed : TList<TCachedHash>;
             FPartiallyComputed : TList<TCachedHash>;
-            procedure PreProcessNewHash(const AHeader : TBytes);            
+            procedure PreProcessNewHash(const AHeader : TBytes);
           public
           public
             constructor Create;
             constructor Create;
             destructor Destroy;
             destructor Destroy;
             property EnablePartiallyComputed : boolean read FEnablePartiallyComputed write FEnablePartiallyComputed;
             property EnablePartiallyComputed : boolean read FEnablePartiallyComputed write FEnablePartiallyComputed;
             procedure AddPartiallyComputed(const AHeader : TBytes; ALevel : Integer; const AOutputs : TArray<TBytes>); inline;
             procedure AddPartiallyComputed(const AHeader : TBytes; ALevel : Integer; const AOutputs : TArray<TBytes>); inline;
             procedure AddFullyComputed(const AHeader : TBytes; ALevel : Integer; const AHash : TBytes); inline;
             procedure AddFullyComputed(const AHeader : TBytes; ALevel : Integer; const AHash : TBytes); inline;
+            procedure Clear;
             function HasComputedHash : Boolean; inline;
             function HasComputedHash : Boolean; inline;
             function PopComputedHash : TCachedHash; inline;
             function PopComputedHash : TCachedHash; inline;
-            function HasNextPartiallyComputedHash : Boolean; inline; 
+            function HasNextPartiallyComputedHash : Boolean; inline;
             function PeekNextPartiallyComputedHash : TCachedHash; inline;
             function PeekNextPartiallyComputedHash : TCachedHash; inline;
             function PopNextPartiallyComputedHash : TCachedHash; inline;
             function PopNextPartiallyComputedHash : TCachedHash; inline;
             function ComputeMemorySize : Integer;
             function ComputeMemorySize : Integer;
         end;
         end;
 
 
     {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
     {$IFNDEF UNITTESTS}private{$ELSE}public{$ENDIF}
-      FHashAlg : array[0..17] of IHash;  // declared here to avoid race-condition during mining
+      FHashAlg : array[0..NUM_HASH_ALGO-1] of IHash;  // declared here to avoid race-condition during mining
       FCache : TCache;
       FCache : TCache;
       FMemStats : TStatistics;
       FMemStats : TStatistics;
       FCaptureMemStats : Boolean;
       FCaptureMemStats : Boolean;
       FEnableCaching : Boolean;
       FEnableCaching : Boolean;
 
 
-      function ContencateByteArrays(const AChunk1, AChunk2: TBytes): TBytes; inline;
       procedure MemTransform1(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform1(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform2(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform2(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform3(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
       procedure MemTransform3(var ABuffer: TBytes; AReadStart, AWriteStart, ALength : Integer); inline;
@@ -233,25 +230,83 @@ uses UMemory, URandomHash;
 
 
 constructor TRandomHash2.Create;
 constructor TRandomHash2.Create;
 begin
 begin
-  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();
-  FMemStats.Reset;
+  FHashAlg[0] := THashFactory.TCrypto.CreateBlake2B_160();
+  FHashAlg[1] := THashFactory.TCrypto.CreateBlake2B_256();
+  FHashAlg[2] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateBlake2B_384();
+  FHashAlg[4] := THashFactory.TCrypto.CreateBlake2S_128();
+  FHashAlg[5] := THashFactory.TCrypto.CreateBlake2S_160();
+  FHashAlg[6] := THashFactory.TCrypto.CreateBlake2S_224();
+  FHashAlg[7] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateGost();
+  FHashAlg[9] := THashFactory.TCrypto.CreateGOST3411_2012_256();
+  FHashAlg[10] := THashFactory.TCrypto.CreateGOST3411_2012_512();
+  FHashAlg[11] := THashFactory.TCrypto.CreateGrindahl256();
+  FHashAlg[12] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[13] := THashFactory.TCrypto.CreateHAS160();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_3_128();
+  FHashAlg[15] := THashFactory.TCrypto.CreateHaval_3_160();
+  FHashAlg[16] := THashFactory.TCrypto.CreateHaval_3_192();
+  FHashAlg[17] := THashFactory.TCrypto.CreateHaval_3_224();
+  FHashAlg[18] := THashFactory.TCrypto.CreateHaval_3_256();
+  FHashAlg[19] := THashFactory.TCrypto.CreateHaval_4_128();
+  FHashAlg[20] := THashFactory.TCrypto.CreateHaval_4_160();
+  FHashAlg[21] := THashFactory.TCrypto.CreateHaval_4_192();
+  FHashAlg[22] := THashFactory.TCrypto.CreateHaval_4_224();
+  FHashAlg[23] := THashFactory.TCrypto.CreateHaval_4_256();
+  FHashAlg[24] := THashFactory.TCrypto.CreateHaval_5_128();
+  FHashAlg[25] := THashFactory.TCrypto.CreateHaval_5_160();
+  FHashAlg[26] := THashFactory.TCrypto.CreateHaval_5_192();
+  FHashAlg[27] := THashFactory.TCrypto.CreateHaval_5_224();
+  FHashAlg[28] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[29] := THashFactory.TCrypto.CreateKeccak_224();
+  FHashAlg[30] := THashFactory.TCrypto.CreateKeccak_256();
+  FHashAlg[31] := THashFactory.TCrypto.CreateKeccak_288();
+  FHashAlg[32] := THashFactory.TCrypto.CreateKeccak_384();
+  FHashAlg[33] := THashFactory.TCrypto.CreateKeccak_512();
+  FHashAlg[34] := THashFactory.TCrypto.CreateMD2();
+  FHashAlg[35] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[36] := THashFactory.TCrypto.CreateMD4();
+  FHashAlg[37] := THashFactory.TCrypto.CreatePanama();
+  FHashAlg[38] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[39] := THashFactory.TCrypto.CreateRIPEMD();
+  FHashAlg[40] := THashFactory.TCrypto.CreateRIPEMD128();
+  FHashAlg[41] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[42] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[43] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[44] := THashFactory.TCrypto.CreateSHA0();
+  FHashAlg[45] := THashFactory.TCrypto.CreateSHA1();
+  FHashAlg[46] := THashFactory.TCrypto.CreateSHA2_224();
+  FHashAlg[47] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[48] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[49] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[50] := THashFactory.TCrypto.CreateSHA2_512_224();
+  FHashAlg[51] := THashFactory.TCrypto.CreateSHA2_512_256();
+  FHashAlg[52] := THashFactory.TCrypto.CreateSHA3_224();
+  FHashAlg[53] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[54] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[55] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[56] := THashFactory.TCrypto.CreateSnefru_8_128();
+  FHashAlg[57] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[58] := THashFactory.TCrypto.CreateTiger_3_128();
+  FHashAlg[59] := THashFactory.TCrypto.CreateTiger_3_160();
+  FHashAlg[60] := THashFactory.TCrypto.CreateTiger_3_192();
+  FHashAlg[61] := THashFactory.TCrypto.CreateTiger_4_128();
+  FHashAlg[62] := THashFactory.TCrypto.CreateTiger_4_160();
+  FHashAlg[63] := THashFactory.TCrypto.CreateTiger_4_192();
+  FHashAlg[64] := THashFactory.TCrypto.CreateTiger_5_128();
+  FHashAlg[65] := THashFactory.TCrypto.CreateTiger_5_160();
+  FHashAlg[66] := THashFactory.TCrypto.CreateTiger_5_192();
+  FHashAlg[67] := THashFactory.TCrypto.CreateTiger2_3_128();
+  FHashAlg[68] := THashFactory.TCrypto.CreateTiger2_3_160();
+  FHashAlg[69] := THashFactory.TCrypto.CreateTiger2_3_192();
+  FHashAlg[70] := THashFactory.TCrypto.CreateTiger2_4_128();
+  FHashAlg[71] := THashFactory.TCrypto.CreateTiger2_4_160();
+  FHashAlg[72] := THashFactory.TCrypto.CreateTiger2_4_192();
+  FHashAlg[73] := THashFactory.TCrypto.CreateTiger2_5_128();
+  FHashAlg[74] := THashFactory.TCrypto.CreateTiger2_5_160();
+  FHashAlg[75] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[76] := THashFactory.TCrypto.CreateWhirlPool();
 end;
 end;
 
 
 destructor TRandomHash2.Destroy;
 destructor TRandomHash2.Destroy;
@@ -294,7 +349,7 @@ var
 begin
 begin
   LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
   LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
   // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
   // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
-  Result := FHashAlg[0].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
+  Result := FHashAlg[SHA2_256_IX].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
 end;
 end;
 
 
 function TRandomHash2.CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean;
 function TRandomHash2.CalculateRoundOutputs(const ABlockHeader: TBytes; ARound: Int32; out ARoundOutputs : TArray<TBytes>) : Boolean;
@@ -316,7 +371,7 @@ begin
   LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
   LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
   LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
   LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
   if ARound = 1 then begin
   if ARound = 1 then begin
-    LRoundInput := FHashAlg[0].ComputeBytes(ABlockHeader).GetBytes;
+    LRoundInput := FHashAlg[SHA2_256_IX].ComputeBytes(ABlockHeader).GetBytes;
     LSeed := GetLastDWordLE( LRoundInput );
     LSeed := GetLastDWordLE( LRoundInput );
     LGen.Initialize(LSeed);
     LGen.Initialize(LSeed);
   end else begin
   end else begin
@@ -392,14 +447,14 @@ begin
 
 
     LRandom := LGen.NextUInt32;
     LRandom := LGen.NextUInt32;
     case LRandom mod 8 of
     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));
+      0: LOutput := ContencateBytes(LOutput, MemTransform1(LNextChunk));
+      1: LOutput := ContencateBytes(LOutput, MemTransform2(LNextChunk));
+      2: LOutput := ContencateBytes(LOutput, MemTransform3(LNextChunk));
+      3: LOutput := ContencateBytes(LOutput, MemTransform4(LNextChunk));
+      4: LOutput := ContencateBytes(LOutput, MemTransform5(LNextChunk));
+      5: LOutput := ContencateBytes(LOutput, MemTransform6(LNextChunk));
+      6: LOutput := ContencateBytes(LOutput, MemTransform7(LNextChunk));
+      7: LOutput := ContencateBytes(LOutput, MemTransform8(LNextChunk));
     end;
     end;
     LBytesToAdd := LBytesToAdd - Length(LNextChunk);
     LBytesToAdd := LBytesToAdd - Length(LNextChunk);
   end;
   end;
@@ -522,12 +577,6 @@ begin
     Result[i] := TBits.RotateRight8(AChunk[i], LChunkLength - i);
     Result[i] := TBits.RotateRight8(AChunk[i], LChunkLength - i);
 end;
 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;
 
 
 { TRandomHash2Fast }
 { TRandomHash2Fast }
 
 
@@ -535,30 +584,91 @@ constructor TRandomHash2Fast.Create;
 begin
 begin
   FEnableCaching := False;
   FEnableCaching := False;
   FCache := TCache.Create;
   FCache := TCache.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();
+  FHashAlg[0] := THashFactory.TCrypto.CreateBlake2B_160();
+  FHashAlg[1] := THashFactory.TCrypto.CreateBlake2B_256();
+  FHashAlg[2] := THashFactory.TCrypto.CreateBlake2B_512();
+  FHashAlg[3] := THashFactory.TCrypto.CreateBlake2B_384();
+  FHashAlg[4] := THashFactory.TCrypto.CreateBlake2S_128();
+  FHashAlg[5] := THashFactory.TCrypto.CreateBlake2S_160();
+  FHashAlg[6] := THashFactory.TCrypto.CreateBlake2S_224();
+  FHashAlg[7] := THashFactory.TCrypto.CreateBlake2S_256();
+  FHashAlg[8] := THashFactory.TCrypto.CreateGost();
+  FHashAlg[9] := THashFactory.TCrypto.CreateGOST3411_2012_256();
+  FHashAlg[10] := THashFactory.TCrypto.CreateGOST3411_2012_512();
+  FHashAlg[11] := THashFactory.TCrypto.CreateGrindahl256();
+  FHashAlg[12] := THashFactory.TCrypto.CreateGrindahl512();
+  FHashAlg[13] := THashFactory.TCrypto.CreateHAS160();
+  FHashAlg[14] := THashFactory.TCrypto.CreateHaval_3_128();
+  FHashAlg[15] := THashFactory.TCrypto.CreateHaval_3_160();
+  FHashAlg[16] := THashFactory.TCrypto.CreateHaval_3_192();
+  FHashAlg[17] := THashFactory.TCrypto.CreateHaval_3_224();
+  FHashAlg[18] := THashFactory.TCrypto.CreateHaval_3_256();
+  FHashAlg[19] := THashFactory.TCrypto.CreateHaval_4_128();
+  FHashAlg[20] := THashFactory.TCrypto.CreateHaval_4_160();
+  FHashAlg[21] := THashFactory.TCrypto.CreateHaval_4_192();
+  FHashAlg[22] := THashFactory.TCrypto.CreateHaval_4_224();
+  FHashAlg[23] := THashFactory.TCrypto.CreateHaval_4_256();
+  FHashAlg[24] := THashFactory.TCrypto.CreateHaval_5_128();
+  FHashAlg[25] := THashFactory.TCrypto.CreateHaval_5_160();
+  FHashAlg[26] := THashFactory.TCrypto.CreateHaval_5_192();
+  FHashAlg[27] := THashFactory.TCrypto.CreateHaval_5_224();
+  FHashAlg[28] := THashFactory.TCrypto.CreateHaval_5_256();
+  FHashAlg[29] := THashFactory.TCrypto.CreateKeccak_224();
+  FHashAlg[30] := THashFactory.TCrypto.CreateKeccak_256();
+  FHashAlg[31] := THashFactory.TCrypto.CreateKeccak_288();
+  FHashAlg[32] := THashFactory.TCrypto.CreateKeccak_384();
+  FHashAlg[33] := THashFactory.TCrypto.CreateKeccak_512();
+  FHashAlg[34] := THashFactory.TCrypto.CreateMD2();
+  FHashAlg[35] := THashFactory.TCrypto.CreateMD5();
+  FHashAlg[36] := THashFactory.TCrypto.CreateMD4();
+  FHashAlg[37] := THashFactory.TCrypto.CreatePanama();
+  FHashAlg[38] := THashFactory.TCrypto.CreateRadioGatun32();
+  FHashAlg[39] := THashFactory.TCrypto.CreateRIPEMD();
+  FHashAlg[40] := THashFactory.TCrypto.CreateRIPEMD128();
+  FHashAlg[41] := THashFactory.TCrypto.CreateRIPEMD160();
+  FHashAlg[42] := THashFactory.TCrypto.CreateRIPEMD256();
+  FHashAlg[43] := THashFactory.TCrypto.CreateRIPEMD320();
+  FHashAlg[44] := THashFactory.TCrypto.CreateSHA0();
+  FHashAlg[45] := THashFactory.TCrypto.CreateSHA1();
+  FHashAlg[46] := THashFactory.TCrypto.CreateSHA2_224();
+  FHashAlg[47] := THashFactory.TCrypto.CreateSHA2_256();
+  FHashAlg[48] := THashFactory.TCrypto.CreateSHA2_384();
+  FHashAlg[49] := THashFactory.TCrypto.CreateSHA2_512();
+  FHashAlg[50] := THashFactory.TCrypto.CreateSHA2_512_224();
+  FHashAlg[51] := THashFactory.TCrypto.CreateSHA2_512_256();
+  FHashAlg[52] := THashFactory.TCrypto.CreateSHA3_224();
+  FHashAlg[53] := THashFactory.TCrypto.CreateSHA3_256();
+  FHashAlg[54] := THashFactory.TCrypto.CreateSHA3_384();
+  FHashAlg[55] := THashFactory.TCrypto.CreateSHA3_512();
+  FHashAlg[56] := THashFactory.TCrypto.CreateSnefru_8_128();
+  FHashAlg[57] := THashFactory.TCrypto.CreateSnefru_8_256();
+  FHashAlg[58] := THashFactory.TCrypto.CreateTiger_3_128();
+  FHashAlg[59] := THashFactory.TCrypto.CreateTiger_3_160();
+  FHashAlg[60] := THashFactory.TCrypto.CreateTiger_3_192();
+  FHashAlg[61] := THashFactory.TCrypto.CreateTiger_4_128();
+  FHashAlg[62] := THashFactory.TCrypto.CreateTiger_4_160();
+  FHashAlg[63] := THashFactory.TCrypto.CreateTiger_4_192();
+  FHashAlg[64] := THashFactory.TCrypto.CreateTiger_5_128();
+  FHashAlg[65] := THashFactory.TCrypto.CreateTiger_5_160();
+  FHashAlg[66] := THashFactory.TCrypto.CreateTiger_5_192();
+  FHashAlg[67] := THashFactory.TCrypto.CreateTiger2_3_128();
+  FHashAlg[68] := THashFactory.TCrypto.CreateTiger2_3_160();
+  FHashAlg[69] := THashFactory.TCrypto.CreateTiger2_3_192();
+  FHashAlg[70] := THashFactory.TCrypto.CreateTiger2_4_128();
+  FHashAlg[71] := THashFactory.TCrypto.CreateTiger2_4_160();
+  FHashAlg[72] := THashFactory.TCrypto.CreateTiger2_4_192();
+  FHashAlg[73] := THashFactory.TCrypto.CreateTiger2_5_128();
+  FHashAlg[74] := THashFactory.TCrypto.CreateTiger2_5_160();
+  FHashAlg[75] := THashFactory.TCrypto.CreateTiger2_5_192();
+  FHashAlg[76] := THashFactory.TCrypto.CreateWhirlPool();
   FMemStats.Reset;
   FMemStats.Reset;
 end;
 end;
 
 
 destructor TRandomHash2Fast.Destroy;
 destructor TRandomHash2Fast.Destroy;
 var i : integer;
 var i : integer;
 begin
 begin
+  for i := Low(FHashAlg) to High(FHashAlg) do
+    FHashAlg[i] := nil;
  FCache.Destroy;
  FCache.Destroy;
  inherited Destroy;
  inherited Destroy;
 end;
 end;
@@ -607,7 +717,7 @@ var
 begin
 begin
   LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
   LSeed := GetLastDWordLE(ARoundOutputs[High(ARoundOutputs)]);
   // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
   // Final "veneer" round of RandomHash is a SHA2-256 of compression of prior round outputs
-  Result := FHashAlg[0].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
+  Result := FHashAlg[SHA2_256_IX].ComputeBytes(Compress(ARoundOutputs, LSeed)).GetBytes;
   if FCaptureMemStats then begin
   if FCaptureMemStats then begin
     LSize := 0;
     LSize := 0;
     for i :=  Low(ARoundOutputs) to High(ARoundOutputs) do
     for i :=  Low(ARoundOutputs) to High(ARoundOutputs) do
@@ -643,7 +753,7 @@ begin
   LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
   LRoundOutputs := LDisposables.AddObject( TList<TBytes>.Create() ) as TList<TBytes>;
   LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
   LGen := LDisposables.AddObject( TMersenne32.Create(0) ) as TMersenne32;
   if ARound = 1 then begin
   if ARound = 1 then begin
-    LRoundInput := FHashAlg[0].ComputeBytes(ABlockHeader).GetBytes;
+    LRoundInput := FHashAlg[SHA2_256_IX].ComputeBytes(ABlockHeader).GetBytes;
     LSeed := GetLastDWordLE( LRoundInput );
     LSeed := GetLastDWordLE( LRoundInput );
     LGen.Initialize(LSeed);
     LGen.Initialize(LSeed);
   end else begin
   end else begin
@@ -901,12 +1011,6 @@ begin
     ABuffer[AWriteStart + i] := TBits.RotateRight8(ABuffer[AReadStart + i], ALength - i);
     ABuffer[AWriteStart + i] := TBits.RotateRight8(ABuffer[AReadStart + i], ALength - i);
 end;
 end;
 
 
-function TRandomHash2Fast.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;
 
 
 { TRandomHash2Fast.TCache }
 { TRandomHash2Fast.TCache }
 
 
@@ -965,14 +1069,20 @@ var i : integer;
 begin
 begin
   // if header is a new template, flush cache
   // if header is a new template, flush cache
   if NOT BytesEqual(FHeaderTemplate, AHeader, 0, 32 - 4) then begin
   if NOT BytesEqual(FHeaderTemplate, AHeader, 0, 32 - 4) then begin
-    FComputed.Clear;
-    FComputed.Capacity := 100;    
-    FPartiallyComputed.Clear;
-    FPartiallyComputed.Capacity := 1000;
+    Clear;
     FHeaderTemplate := SetLastDWordLE(AHeader, 0);
     FHeaderTemplate := SetLastDWordLE(AHeader, 0);
   end;  
   end;  
 end;
 end;
-     
+
+procedure TRandomHash2Fast.TCache.Clear;
+begin
+  FComputed.Clear;
+  FComputed.Capacity := 100;
+  FPartiallyComputed.Clear;
+  FPartiallyComputed.Capacity := 1000;
+  SetLength(FHeaderTemplate, 0);
+end;
+
 function TRandomHash2Fast.TCache.HasComputedHash : Boolean;
 function TRandomHash2Fast.TCache.HasComputedHash : Boolean;
 begin
 begin
   Result := FComputed.Count > 0;
   Result := FComputed.Count > 0;

+ 13 - 23
src/gui-classic/UFRMDiagnosticTool.pas

@@ -73,6 +73,7 @@ type
       property Notify : TAlgorithmNotify read FNotify write FNotify;
       property Notify : TAlgorithmNotify read FNotify write FNotify;
       property NotifyFinish : TAlgorithmFinishNotify read FNotifyFinish write FNotifyFinish;
       property NotifyFinish : TAlgorithmFinishNotify read FNotifyFinish write FNotifyFinish;
       constructor Create(const ATitle : String); virtual;
       constructor Create(const ATitle : String); virtual;
+      destructor Destroy; override;
       procedure Finish;
       procedure Finish;
   end;
   end;
 
 
@@ -154,6 +155,13 @@ begin
   SetLength(FLastHash, 32);
   SetLength(FLastHash, 32);
 end;
 end;
 
 
+destructor TAlgorithmThread.Destroy;
+begin
+  Self.Suspended := True;
+  Finish;
+  Inherited Destroy;
+end;
+
 procedure TAlgorithmThread.Finish;
 procedure TAlgorithmThread.Finish;
 begin
 begin
   FExitLoop := True;
   FExitLoop := True;
@@ -351,12 +359,14 @@ end;
 
 
 procedure TFRMDiagnosticTool.FormCreate(Sender: TObject);
 procedure TFRMDiagnosticTool.FormCreate(Sender: TObject);
 begin
 begin
-  FRH2CachedThread := TRandomHash2CachedThread.Create;
-  FRH2Thread := TRandomHash2Thread.Create;
   FRHThread := TRandomHashThread.Create;
   FRHThread := TRandomHashThread.Create;
   FRHCachedThread := TRandomHashCachedThread.Create;
   FRHCachedThread := TRandomHashCachedThread.Create;
+  FRH2CachedThread := TRandomHash2CachedThread.Create;
+  FRH2Thread := TRandomHash2Thread.Create;
   FRH2NonceScanThread := TRandomHash2NonceScan.Create;
   FRH2NonceScanThread := TRandomHash2NonceScan.Create;
+
   FDisposables.AddObject(FRHThread);
   FDisposables.AddObject(FRHThread);
+  FDisposables.AddObject(FRHCachedThread);
   FDisposables.AddObject(FRH2Thread);
   FDisposables.AddObject(FRH2Thread);
   FDisposables.AddObject(FRH2CachedThread);
   FDisposables.AddObject(FRH2CachedThread);
   FDisposables.AddObject(FRH2NonceScanThread);
   FDisposables.AddObject(FRH2NonceScanThread);
@@ -397,34 +407,14 @@ begin
 end;
 end;
 
 
 procedure TFRMDiagnosticTool.btnEntropyClick(Sender: TObject);
 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;
 var LIn, LOut : TBytes; i : Integer; TXT : String;
 begin
 begin
   SetLength(LIn, 200);
   SetLength(LIn, 200);
-  FillChar(LIn, 200, 1);
   TXT := '';
   TXT := '';
   for I := 1 to 10 do begin
   for I := 1 to 10 do begin
     LOut := TRandomHash2.Compute(LIn);
     LOut := TRandomHash2.Compute(LIn);
     TXT := TXT + Format('RH2( %s ) = %s %s', [ TCrypto.ToHexaString(LIn), TCrypto.ToHexaString(LOut), sLineBreak]);
     TXT := TXT + Format('RH2( %s ) = %s %s', [ TCrypto.ToHexaString(LIn), TCrypto.ToHexaString(LOut), sLineBreak]);
-    SetLastDWordLE(LIn, I);
+    LIn := SetLastDWordLE(LIn, I);
   end;
   end;
   txtLog.Text := TXT;
   txtLog.Text := TXT;
 end;
 end;

+ 41 - 4
src/libraries/sphere10/UCommon.pas

@@ -52,6 +52,7 @@ function BinStrComp(const Str1, Str2 : String): Integer; // Binary-safe StrComp
 function BytesCompare(const ABytes1, ABytes2: TBytes): integer;
 function BytesCompare(const ABytes1, ABytes2: TBytes): integer;
 function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean; overload; inline;
 function BytesEqual(const ABytes1, ABytes2 : TBytes) : boolean; overload; inline;
 function BytesEqual(const ABytes1, ABytes2 : TBytes; AFrom, ALength : UInt32) : boolean; overload; inline;
 function BytesEqual(const ABytes1, ABytes2 : TBytes; AFrom, ALength : UInt32) : boolean; overload; inline;
+function ContencateBytes(const AChunk1, AChunk2: TBytes): TBytes; inline;
 function SetLastDWordLE(const ABytes: TBytes; AValue: UInt32): TBytes;
 function SetLastDWordLE(const ABytes: TBytes; AValue: UInt32): TBytes;
 function GetLastDWordLE(const ABytes: TBytes) : UInt32;
 function GetLastDWordLE(const ABytes: TBytes) : UInt32;
 function GetDWordLE(const ABytes: TBytes; AOffset : Integer) : UInt32;
 function GetDWordLE(const ABytes: TBytes; AOffset : Integer) : UInt32;
@@ -395,14 +396,22 @@ type
       class function NumericBetweenExclusive(const AValue, Lower, Upper : Variant) : boolean;
       class function NumericBetweenExclusive(const AValue, Lower, Upper : Variant) : boolean;
   end;
   end;
 
 
-  { TFileStreamHelper }
+  { TStreamHelper }
 
 
-  TFileStreamHelper = class helper for TFileStream
+  TStreamHelper = class helper for TStream
     {$IFNDEF FPC}
     {$IFNDEF FPC}
     procedure WriteString(const AString : String);
     procedure WriteString(const AString : String);
     {$ENDIF}
     {$ENDIF}
+    function ReadBytes(ACount : Int32) : TBytes; inline;
   end;
   end;
 
 
+  { TMemoryStreamHelper }
+
+   TMemoryStreamHelper = class helper for TMemoryStream
+     function ToBytes(ASize : Integer = -1) : TBytes; inline;
+   end;
+
+
   { TFileTool }
   { TFileTool }
 
 
   TFileTool = class
   TFileTool = class
@@ -644,6 +653,13 @@ begin
   Result := CompareMem(@ABytes1[AFrom], @ABytes2[AFrom], ALength);
   Result := CompareMem(@ABytes1[AFrom], @ABytes2[AFrom], ALength);
 end;
 end;
 
 
+function ContencateBytes(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 SetLastDWordLE(const ABytes: TBytes; AValue: UInt32): TBytes;
 function SetLastDWordLE(const ABytes: TBytes; AValue: UInt32): TBytes;
 var
 var
   ABytesLength : Integer;
   ABytesLength : Integer;
@@ -2066,14 +2082,35 @@ begin
   Result := (lowercmp = 1) AND (uppercmp = -1);
   Result := (lowercmp = 1) AND (uppercmp = -1);
 end;
 end;
 
 
-{ TFileStreamHelper }
+{ TStreamHelper }
+
 {$IFNDEF FPC}
 {$IFNDEF FPC}
-procedure TFileStreamHelper.WriteString(const AString : String);
+procedure TStreamHelper.WriteString(const AString : String);
 begin
 begin
    Self.WriteBuffer(Pointer(AString)^, Length(AString));
    Self.WriteBuffer(Pointer(AString)^, Length(AString));
 end;
 end;
 {$ENDIF}
 {$ENDIF}
 
 
+function TStreamHelper.ReadBytes(ACount : Int32) : TBytes;
+begin
+  SetLength(Result, ACount);
+  Read(Result, ACount);
+end;
+
+{ TMemoryStreamHelper }
+
+function TMemoryStreamHelper.ToBytes(ASize : Integer = -1) : TBytes;
+var
+  LTakeAmount : Integer;
+begin
+  if ASize < 0 then
+    LTakeAmount := Self.Size
+  else
+    LTakeAmount := ASize;
+  SetLength(Result, LTakeAmount);
+  Move(Self.Memory^, Result[0], LTakeAmount);
+end;
+
 { TFileTool }
 { TFileTool }
 
 
 class procedure TFileTool.AppendText(const AFileName: string; const AText: string);
 class procedure TFileTool.AppendText(const AFileName: string; const AText: string);

+ 9 - 9
src/tests/URandomHash2.Tests.Delphi.pas

@@ -53,9 +53,9 @@ const
   );
   );
 
 
   DATA_RANDOMHASH2_STANDARD_EXPECTED : array[1..3] of String = (
   DATA_RANDOMHASH2_STANDARD_EXPECTED : array[1..3] of String = (
-    '0x1340164c73924ecdcff3866bd1a5c75c5c9f517c480336fa8189b2a4c2c745c3',
-    '0xe2ba36ce31ed3b01a5f254dfafb392f29b8eafef24c52fd0480c4db5b2d2e999',
-    '0x88624077976ac8327b38092fff01428125d914b98d76b5c9270177ec105a6976'
+    '0x76821dd68e384bdc2a69fea66a70191c1d7df5799bddc70f2dfaaeda2393899b',
+    '0x65ec5370f913497abc57621e9b6703b51d541a320ac2422a51c16e48d0d1fc05',
+    '0xfabbfcd96c9ef4734bd82d59bfa4bc9c0ff389a53c7c247abb1c1c1794e6dea8'
   );
   );
 
 
 { TRandomHash2Test }
 { TRandomHash2Test }
@@ -117,9 +117,9 @@ begin
 
 
   for i := 1 to 100 do begin
   for i := 1 to 100 do begin
     LBuff := LHasher.Hash(LBuff);
     LBuff := LHasher.Hash(LBuff);
-    while LHasher.HasCachedHash do begin
-      LCachedHash := LHasher.PopCachedHash;
-      AssertEquals(TRandomHash2Fast.Compute(LCachedHash.Header), LCachedHash.Hash);
+    while LHasher.Cache.HasComputedHash do begin
+      LCachedHash := LHasher.Cache.PopComputedHash;
+      AssertEquals(TRandomHash2Fast.Compute(LCachedHash.Header), LCachedHash.RoundOutputs[0]);
     end;
     end;
   end;
   end;
 end;
 end;
@@ -174,9 +174,9 @@ begin
   LHasher2 := LDisposables.AddObject( TRandomHash2Fast.Create ) as TRandomHash2Fast;
   LHasher2 := LDisposables.AddObject( TRandomHash2Fast.Create ) as TRandomHash2Fast;
 
 
 
 
-  for LStartPad := 0 to 5 do
-    for LEndPad := 0 to 5 do begin
-      for LTransformAmount := 4 to 10 do begin
+  for LStartPad := 0 to 3 do
+    for LEndPad := 0 to 3 do begin
+      for LTransformAmount := 4 to 7 do begin
         // Ref
         // Ref
         LInput := TRandomHash.Compute( LInput );
         LInput := TRandomHash.Compute( LInput );
         SetLength(LInput, LTransformAmount);
         SetLength(LInput, LTransformAmount);

+ 22 - 22
src/tests/URandomHash2.Tests.pas

@@ -54,31 +54,31 @@ const
   { RandomHash Official Values }
   { RandomHash Official Values }
 
 
   DATA_RANDOMHASH_STANDARD : array[1..3] of TTestItem<String, String> = (
   DATA_RANDOMHASH_STANDARD : array[1..3] of TTestItem<String, String> = (
-    (Input: '0x0';                                         Expected: '0x1340164c73924ecdcff3866bd1a5c75c5c9f517c480336fa8189b2a4c2c745c3'),
-    (Input: 'The quick brown fox jumps over the lazy dog'; Expected: '0xe2ba36ce31ed3b01a5f254dfafb392f29b8eafef24c52fd0480c4db5b2d2e999'),
-    (Input: '0x000102030405060708090a0b0c0d0e0f';          Expected: '0x88624077976ac8327b38092fff01428125d914b98d76b5c9270177ec105a6976')
+    (Input: '0x0';                                         Expected: '0x76821dd68e384bdc2a69fea66a70191c1d7df5799bddc70f2dfaaeda2393899b'),
+    (Input: 'The quick brown fox jumps over the lazy dog'; Expected: '0x65ec5370f913497abc57621e9b6703b51d541a320ac2422a51c16e48d0d1fc05'),
+    (Input: '0x000102030405060708090a0b0c0d0e0f';          Expected: '0xfabbfcd96c9ef4734bd82d59bfa4bc9c0ff389a53c7c247abb1c1c1794e6dea8')
   );
   );
 
 
   {  Hash Test Data }
   {  Hash Test Data }
 
 
   DATA_RANDOMHASH : array[1..16] of TTestItem<Integer, String> = (
   DATA_RANDOMHASH : array[1..16] of TTestItem<Integer, String> = (
     { NOTE: Input denotes the number of bytes to take from DATA_BYTES when executing test }
     { NOTE: Input denotes the number of bytes to take from DATA_BYTES when executing test }
-    (Input: 17;  Expected: '0x4cc3fb94697855e5d124823331516e5a18623d87d735e67ca3f2f7b87fcdabee'),
-    (Input: 31;  Expected: '0xb4655c784cc21d0da5a19420c66ed275c8881952b9119d0b215882880069813c'),
-    (Input: 32;  Expected: '0x2e2de78bb4db374779108541ac1eeb36a1f1144b2767e7e1aa8dac19c5430958'),
-    (Input: 33;  Expected: '0xb096070aec5b4f6146cc5b70c85ab4e0b94efb5aa3ca41e7fd671fd3b98c8588'),
-    (Input: 34;  Expected: '0x015b89e6c6b2b4580702555adda7b445e1064f481442475061a4768e65879eb0'),
-    (Input: 63;  Expected: '0x868b30a0091d6562fa3dffe9c5ec5df2214383f2c6f48bbf2b314f8791e089e9'),
-    (Input: 64;  Expected: '0xfe90fe080b0dca79aec5e0b269e58d368d9a83ce73171d155f475c42642034d0'),
-    (Input: 65;  Expected: '0xec04a9b78b4f4ecf1e48d5c11b89f87f461ea5ae23eaff9d2baa0c703e14d01b'),
-    (Input: 100; Expected: '0x42e42defa9c03dd9ff74053a6bffbc966321aa14df446bb36a270844fdb8e838'),
-    (Input: 117; Expected: '0xaf4257f5d406ab3e3d0453129fefdf6633016676bb3aa45a40fdac2f5bca4951'),
-    (Input: 127; Expected: '0xa998117cd3f3505b4ed863601abc175a393653ce3eec3b375235ad530845045f'),
-    (Input: 128; Expected: '0xb4a76d37c5f390986ace3e35b7306c65a62d92f3f449a5f7b523982a5e5496a4'),
-    (Input: 129; Expected: '0x432d52101c90b268a7127d84c080f8d5c60d0223620e5c463883b15ed81db390'),
-    (Input: 178; Expected: '0x4ab88684c0ef33bc8dad4a4aa1a9badd025bc7afe98657d325e3bf1e4f3706a2'),
-    (Input: 199; Expected: '0x25aa461b64f96f7b62b46e79273554a7b5866f26278929ae3f78203a714cca2a'),
-    (Input: 200; Expected: '0x3bd8f64cc0eeb0c8dedba03a1f142763c3e6f9aed80dcd6534eac28eba2f7510')
+    (Input: 17;  Expected: '0x5f66d30d5872652d6f7c88ade147e5a2dfb1082850c48ce3c4391a354c27f6ef'),
+    (Input: 31;  Expected: '0x36a8da1e7af559dd77bd9588c78b0f4a6a5424d049d9dec3379f5246bad26733'),
+    (Input: 32;  Expected: '0xf9ec7c6a442296c92352b4b8e74186bc12318f0f6c33cdbea45cf235ab4ba36c'),
+    (Input: 33;  Expected: '0xbaca33a265173ec828378e1842d50183d72fc74713e5814db8fd11258b139fc8'),
+    (Input: 34;  Expected: '0xeed8e703ab3bb2e525e5f753a401bf506316963c3721bbb006f1f14d0c7e77ed'),
+    (Input: 63;  Expected: '0x938256c0a20e8466829b0147215f37bbc21612b097f488680231830ccb6e8073'),
+    (Input: 64;  Expected: '0x22efd0c3a556b3b604ddff123c229ba28c37990a4bb2440419689ecc721aea51'),
+    (Input: 65;  Expected: '0x79c61dfa3f1c2851feddf768708eb9905f7b074912f1b67bc92c059996c0bb10'),
+    (Input: 100; Expected: '0x6a1eb54ce86e5737d03cf3818f4fac142ffb6c345fb7bf536a14700d650b6ce5'),
+    (Input: 117; Expected: '0xae9993eadbd78e61f5ef4058e8bf034d66bfd44a7ee4b8e95447f774c13b51f3'),
+    (Input: 127; Expected: '0xc2ac91fdd2cffa19c840a53ded2236aed5dccdff8566be4e4a6c95d2b9788f05'),
+    (Input: 128; Expected: '0x83c06103d03564515e17929e5083453c9ad7a35f92baa821a9758dbb7e6819a0'),
+    (Input: 129; Expected: '0xb8556216f6c2256faee05176bb1b429ff6aa3d514e9f49c2526ded4374ff8881'),
+    (Input: 178; Expected: '0xf4abd91b9392c636cf8b22ae4e54f72c5734bc05a80f8a430c6f41e1f7bd5fd1'),
+    (Input: 199; Expected: '0x3c392fc666bf0d1127e10989234c8f2f5d4d9cc1c4eba41c1d4736924988a8ac'),
+    (Input: 200; Expected: '0xbe8880a61f1039adca78c5d4d073044f142a033d1a31fb4f2cdb73d9501b424a')
   );
   );
 
 
 { TRandomHash2Test }
 { TRandomHash2Test }
@@ -145,9 +145,9 @@ begin
 
 
   for i := 1 to 100 do begin
   for i := 1 to 100 do begin
     LBuff := LHasher.Hash(LBuff);
     LBuff := LHasher.Hash(LBuff);
-    while LHasher.HasCachedHash do begin
-      LCachedHash := LHasher.PopCachedHash;
-      AssertEquals(TRandomHash2Fast.Compute(LCachedHash.Header), LCachedHash.Hash);
+    while LHasher.Cache.HasComputedHash do begin
+      LCachedHash := LHasher.Cache.PopComputedHash;
+      AssertEquals(TRandomHash2Fast.Compute(LCachedHash.Header), LCachedHash.RoundOutputs[0]);
     end;
     end;
   end;
   end;
 end;
 end;