|
@@ -37,12 +37,12 @@ A low-memory, GPU and ASIC-resistant hash algorithm called **Random Hash** is pr
|
|
|
|
|
|
1. Hashing a nonce requires ```N``` iterations (called rounds)
|
|
1. Hashing a nonce requires ```N``` iterations (called rounds)
|
|
2. Each round selects a random hash function from a set of 16 well-known hash algorithms
|
|
2. Each round selects a random hash function from a set of 16 well-known hash algorithms
|
|
-3. The input at round ```x``` depends on round ```x-1```
|
|
|
|
-4. The input at round x depends on round ```x-1``` **of a different nonce** determined based on the output from round ```x-1``` for the current nonce
|
|
|
|
-5. The input at round ```x``` is a compression of the outputs of every contributing stage to ```100 bytes```
|
|
|
|
|
|
+3. The input at round ```x``` is salted with the outputs of all prior rounds
|
|
|
|
+4. The input at round ```x``` is salted with the output of all prior rounds **of a different nonce**, randomly determined
|
|
|
|
+5. The input at round ```x``` is a compression of the transitive closure of prior/neighbouring round outputs to the size of ```100 bytes```
|
|
6. The output of every round is expanded for memory-hardness
|
|
6. The output of every round is expanded for memory-hardness
|
|
7. Randomness is generated using ```Mersenne Twister``` algorithm
|
|
7. Randomness is generated using ```Mersenne Twister``` algorithm
|
|
-8. Randomness is seeded via ```MurMur3``` checksum of previous round
|
|
|
|
|
|
+8. Randomness is seeded via ```MurMur3``` checksum of current round
|
|
9. The final round is then hashed again via ```SHA2_256```, in keeping with traditional cryptocurrency approaches.
|
|
9. The final round is then hashed again via ```SHA2_256```, in keeping with traditional cryptocurrency approaches.
|
|
|
|
|
|
### RandomHash Design
|
|
### RandomHash Design
|
|
@@ -56,16 +56,18 @@ A low-memory, GPU and ASIC-resistant hash algorithm called **Random Hash** is pr
|
|
SHA2_256
|
|
SHA2_256
|
|
SHA2_384
|
|
SHA2_384
|
|
SHA2_512
|
|
SHA2_512
|
|
- SHA3,
|
|
|
|
|
|
+ SHA3_256,
|
|
|
|
+ SHA3_384,
|
|
|
|
+ SHA3_512,
|
|
RIPEMD160,
|
|
RIPEMD160,
|
|
RIPEMD256,
|
|
RIPEMD256,
|
|
RIPEMD320,
|
|
RIPEMD320,
|
|
Blake2b,
|
|
Blake2b,
|
|
Blake2s,
|
|
Blake2s,
|
|
- Tiger2,
|
|
|
|
- Snerfu,
|
|
|
|
|
|
+ Tiger2_5_192,
|
|
|
|
+ Snefru_8_256,
|
|
Grindahl512,
|
|
Grindahl512,
|
|
- Haval,
|
|
|
|
|
|
+ Haval_5_256,
|
|
MD5
|
|
MD5
|
|
RadioGatun32
|
|
RadioGatun32
|
|
Whirlpool
|
|
Whirlpool
|
|
@@ -73,54 +75,56 @@ A low-memory, GPU and ASIC-resistant hash algorithm called **Random Hash** is pr
|
|
N = 5 // Number of hashing rounds required to compute a nonce (total rounds = 2^N - 1)
|
|
N = 5 // Number of hashing rounds required to compute a nonce (total rounds = 2^N - 1)
|
|
M = 10KB // The memory expansion unit (in bytes)
|
|
M = 10KB // The memory expansion unit (in bytes)
|
|
|
|
|
|
- Function RandomHash(blockHeader : ByteArray)
|
|
|
|
|
|
+ Function RandomHash(blockHeader : ByteArray) : ByteArray
|
|
begin
|
|
begin
|
|
- let AllOutputs = RandomHash( blockHeader, N)
|
|
|
|
- seed = Checksum(neighborOutputs[Length(AllOutputs) - 1])
|
|
|
|
- Result := SHA2_256( Compress(AllOutputs, RandomNumberGenerator(seed)) )
|
|
|
|
|
|
+ let allOutputs = RandomHash( blockHeader, N)
|
|
|
|
+ Result := SHA2_256( Compress( allOutputs ) )
|
|
end
|
|
end
|
|
|
|
|
|
- Function RandomHash(blockHeader : ByteArray, Round : Integer) : list of ByteArray
|
|
|
|
|
|
+ Function RandomHash(blockHeader : ByteArray, Round : Integer) : List of ByteArray
|
|
begin
|
|
begin
|
|
- let RoundOutputs = list of ByteArray;
|
|
|
|
|
|
+ if Round < 1 or Round > N then
|
|
|
|
+ Error 'Round must be between 0 and N inclusive'
|
|
|
|
+
|
|
|
|
+ let roundOutputs = new List of ByteArray;
|
|
|
|
|
|
if Round = 1 then
|
|
if Round = 1 then
|
|
let seed = Checksum(blockHeader) // can hash blockHeader first, but not required
|
|
let seed = Checksum(blockHeader) // can hash blockHeader first, but not required
|
|
let gen = RandomNumberGenerator(seed)
|
|
let gen = RandomNumberGenerator(seed)
|
|
- let input = blockHeader
|
|
|
|
|
|
+ let roundInput = blockHeader
|
|
else
|
|
else
|
|
let parentOutputs = RandomHash(blockHeader, Round - 1)
|
|
let parentOutputs = RandomHash(blockHeader, Round - 1)
|
|
- RoundOutputs.addAll(parentOutputs)
|
|
|
|
- let seed = Checksum(neighborOutputs[Length(neighborOutputs) - 1])
|
|
|
|
|
|
+ let seed = Checksum(parentOutputs)
|
|
let gen = RandomNumberGenerator(seed)
|
|
let gen = RandomNumberGenerator(seed)
|
|
|
|
+
|
|
|
|
+ roundOutputs.AddMany(parentOutputs)
|
|
|
|
|
|
let otherNonceHeader = ChangeNonce(blockHeader, gen.NextDWord)
|
|
let otherNonceHeader = ChangeNonce(blockHeader, gen.NextDWord)
|
|
let neighborOutputs = RandomHash(blockHeader, Round - 1)
|
|
let neighborOutputs = RandomHash(blockHeader, Round - 1)
|
|
- RoundOutputs.addAll(neighborOutput)
|
|
|
|
|
|
+ roundOutputs.AddMany(neighborOutputs)
|
|
|
|
|
|
- // re-seed RNG based on random other nonce output
|
|
|
|
- seed = Checksum(neighborOutputs[Length(neighborOutputs) - 1])
|
|
|
|
- gen = RandomNumberGenerator(seed)
|
|
|
|
- let input = Compress(RoundOutputs, gen)
|
|
|
|
|
|
+ let roundInput = Compress(roundOutputs)
|
|
|
|
|
|
- let hashFunc = HASH_ALGO[gen.NextDWord % 16]
|
|
|
|
- let output = hashFunc(input)
|
|
|
|
- output = Expand( output, N - i, gen )
|
|
|
|
- RoundOutputs.add(output)
|
|
|
|
|
|
+ let hashFunc = HASH_ALGO[gen.NextDWord % 18]
|
|
|
|
+ let output = hashFunc(roundInput)
|
|
|
|
+ output = Expand( output, N - Round )
|
|
|
|
+ roundOutputs.Add(output)
|
|
|
|
|
|
- Result := RoundOutputs
|
|
|
|
|
|
+ Result := roundOutputs
|
|
end
|
|
end
|
|
|
|
|
|
|
|
|
|
- function Expand(input : ByteArray, ExpansionFactor : Integer, gen : RandomNumberGenerator) : ByteArray
|
|
|
|
|
|
+ function Expand(input : ByteArray, ExpansionFactor : Integer) : ByteArray
|
|
begin
|
|
begin
|
|
- let Size = Length(input) + ExpansionFactor*M;
|
|
|
|
|
|
+ let seed = Checksum(input)
|
|
|
|
+ let gen = RandomNumberGenerator(seed)
|
|
|
|
+ let size = Length(input) + ExpansionFactor*M;
|
|
let output = input.Clone
|
|
let output = input.Clone
|
|
- let bytesToAdd = Size - Length(input)
|
|
|
|
- while output < Size do
|
|
|
|
|
|
+ let bytesToAdd = size - Length(input)
|
|
|
|
+ while bytesToAdd > 0 do
|
|
let nextChunk = output.Clone
|
|
let nextChunk = output.Clone
|
|
- if Length(output) + Length(nextChunk) > Size then
|
|
|
|
- SetLength(nextChunk, Size - Length(output))
|
|
|
|
|
|
+ if Length(nextChunk) > bytesToAdd then
|
|
|
|
+ SetLength(nextChunk, bytesToAdd)
|
|
|
|
|
|
let random = gen.NextDWord
|
|
let random = gen.NextDWord
|
|
case random % 8 do
|
|
case random % 8 do
|
|
@@ -130,31 +134,35 @@ A low-memory, GPU and ASIC-resistant hash algorithm called **Random Hash** is pr
|
|
.
|
|
.
|
|
.
|
|
.
|
|
7: output = output ++ MemTransform8(nextChunk)
|
|
7: output = output ++ MemTransform8(nextChunk)
|
|
-
|
|
|
|
|
|
+ bytesToAdd = bytesToAdd - Length(nextChunk)
|
|
Result = output
|
|
Result = output
|
|
end
|
|
end
|
|
|
|
|
|
- function Compress(inputs : list of ByteArray, gen : RandomNumberGenerator) : ByteArray
|
|
|
|
|
|
+ function Compress(inputs : list of ByteArray) : ByteArray
|
|
begin
|
|
begin
|
|
|
|
+ let seed = Checksum(inputs)
|
|
|
|
+ let gen = RandomNumberGenerator(seed)
|
|
let output = Byte[0..99]
|
|
let output = Byte[0..99]
|
|
-
|
|
|
|
for i = 0 to 99 do
|
|
for i = 0 to 99 do
|
|
let source = inputs[ gen.NextDWord % Length(inputs) ]
|
|
let source = inputs[ gen.NextDWord % Length(inputs) ]
|
|
output[i] = source[ gen.NextDWord % Length(source) ]
|
|
output[i] = source[ gen.NextDWord % Length(source) ]
|
|
-
|
|
|
|
Result := output
|
|
Result := output
|
|
end
|
|
end
|
|
|
|
|
|
function ChangeNonce(blockHeader : ByteArray, nonce : Integer) : ByteArray
|
|
function ChangeNonce(blockHeader : ByteArray, nonce : Integer) : ByteArray
|
|
begin
|
|
begin
|
|
- // changes nonce in blockHeader by knowing offset of nonce
|
|
|
|
|
|
+ // clones and changes nonce in blockHeader (by determining offset of nonce)
|
|
end
|
|
end
|
|
|
|
|
|
-
|
|
|
|
Function Checksum(input : ByteArray) : DWord
|
|
Function Checksum(input : ByteArray) : DWord
|
|
begin
|
|
begin
|
|
// standard MurMu3 algorithm
|
|
// standard MurMu3 algorithm
|
|
end
|
|
end
|
|
|
|
+
|
|
|
|
+ Function Checksum(inputs : List of ByteArray) : DWord
|
|
|
|
+ begin
|
|
|
|
+ // standard MurMu3 algorithm run over list of inputs
|
|
|
|
+ end
|
|
|
|
|
|
Function RandomNumberGenerator(seed : DWord) : TMersenneTwister
|
|
Function RandomNumberGenerator(seed : DWord) : TMersenneTwister
|
|
// standard Mersenne Twister random number generator (or other suitably chosen algorithm)
|
|
// standard Mersenne Twister random number generator (or other suitably chosen algorithm)
|
|
@@ -163,7 +171,7 @@ A low-memory, GPU and ASIC-resistant hash algorithm called **Random Hash** is pr
|
|
|
|
|
|
#### Memory transform methods
|
|
#### Memory transform methods
|
|
|
|
|
|
-These methods are iteratively and randomly applied to a hash output in order to rapidly expand it for compression in the next round
|
|
|
|
|
|
+These methods are iteratively and randomly applied to a hash output in order to rapidly expand it for compression in the next round. **Note**: the length of the output is always the same as the length of the input.
|
|
```
|
|
```
|
|
- Method 1: No-Op (e.g. input = 123456 output = 123456)
|
|
- Method 1: No-Op (e.g. input = 123456 output = 123456)
|
|
- Method 2: Swap-LR (e.g. input = 123456 output = 456123)
|
|
- Method 2: Swap-LR (e.g. input = 123456 output = 456123)
|
|
@@ -341,6 +349,7 @@ This PIP is not backwards compatible and requires a hard-fork activation. Previo
|
|
A reference implementation will be provided in the coming weeks.
|
|
A reference implementation will be provided in the coming weeks.
|
|
|
|
|
|
## Acknowledgements
|
|
## Acknowledgements
|
|
|
|
+
|
|
Refinements to improve GPU-hardness were provided by Ian Muldoon.
|
|
Refinements to improve GPU-hardness were provided by Ian Muldoon.
|
|
|
|
|
|
## Links
|
|
## Links
|