|
@@ -27,11 +27,11 @@ First of all, RandomHash introduces the concept of nonce-dependency between nonc
|
|
|
|
|
|
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 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 does.
|
|
|
+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.
|
|
|
|
|
|
-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 massively optimize 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 order of magnitude improvement in both validation hashrate 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, 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 ~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.
|
|
|
|
|
|
#### RandomHash vs RandomHash2 Measured Performance
|
|
|
|
|
@@ -57,9 +57,9 @@ RandomHash2 is similarly structured to RandomHash, but has some key differences.
|
|
|
1. Hashing a nonce requires ```N``` iterations (called levels), which are evaluated recursively;
|
|
|
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;
|
|
|
-4. The value ```J``` is restricted to ```MIN_J=2``` and ```MAX_J=4```;
|
|
|
-5. Each round selects a random hash function from a set of 18 well-known hash algorithms;
|
|
|
-6. The input digest hashed at level ```x``` is a compression of the transitive closure of the hash outputs of the it's prior-levels (1) and it's neighbouring nonce's prior-levels (3);
|
|
|
+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;
|
|
|
+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;
|
|
|
8. Randomness is generated using ```Mersenne Twister``` algorithm;
|
|
|
9. Randomness is always seeded using last DWORD of a cryptographic hash algorithm output;
|
|
@@ -70,7 +70,7 @@ RandomHash2 is similarly structured to RandomHash, but has some key differences.
|
|
|
|
|
|
The primary differences to predecessor are:
|
|
|
|
|
|
-** Constants
|
|
|
+**Constants**
|
|
|
```pascal
|
|
|
MIN_N = 2;
|
|
|
MAX_N = 4;
|
|
@@ -81,15 +81,16 @@ The primary differences to predecessor are:
|
|
|
|
|
|
**N is now random per nonce**
|
|
|
```pascal
|
|
|
- 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));
|
|
|
```
|
|
|
|
|
|
**Block Header is pre-hashed before used (last DWORD is seed)**
|
|
|
```pascal
|
|
|
- if ARound = 1 then begin
|
|
|
- LRoundInput := FHashAlg[0].ComputeBytes(ABlockHeader).GetBytes;
|
|
|
- LSeed := GetLastDWordLE( LRoundInput );
|
|
|
- LGen.Initialize(LSeed);
|
|
|
+ if ARound = 1 then begin
|
|
|
+ LRoundInput := FHashAlg[0].ComputeBytes(ABlockHeader).GetBytes;
|
|
|
+ LSeed := GetLastDWordLE( LRoundInput );
|
|
|
+ LGen.Initialize(LSeed);
|
|
|
+ ...
|
|
|
```
|
|
|
|
|
|
**Random number of dependent neighbour nonces**
|
|
@@ -98,6 +99,16 @@ The primary differences to predecessor are:
|
|
|
for i := 1 to LNumNeighbours do begin
|
|
|
```
|
|
|
|
|
|
+**MurMur3 checksumming removed completely***
|
|
|
+```pascal
|
|
|
+ // MurMur3 checksumming has been removed completely and seeding now uses last dword of a cryptographic hash
|
|
|
+ LSeed := GetLastDWordLE( LRoundInput );
|
|
|
+ ...
|
|
|
+ // Compression & Expansion no longer use MurMur3 checksum for seeding mersenne twister, instead seed is passed as argument
|
|
|
+ function Expand(const AInput: TBytes; AExpansionFactor: Int32; ASeed : UInt32) : TBytes;
|
|
|
+ function Compress(const AInputs: TArray<TBytes>; ASeed : UInt32): TBytes; inline;
|
|
|
+```
|
|
|
+
|
|
|
**Neighbouring nonces are only cached when fully evaluated**
|
|
|
```pascal
|
|
|
LNeighbourNonceHeader := SetLastDWordLE(ABlockHeader, LGen.NextUInt32); // change nonce
|