Parcourir la source

PIP-0009: Various updates

* Adjust PIP-0009 to improve memory-hardness

As described at https://www.reddit.com/r/pascalcoin/comments/7vl7u8/hardening_randomhash_against_gpu_implementation/

* Fix top-level routine to accommodate return type change

* Update based on feedback on discord

* Restore Checksum and revert RandomNumberGenerator

* Restore ChangeNonce that I somehow deleted.
Ian Muldoon il y a 7 ans
Parent
commit
a96e17688c
1 fichiers modifiés avec 46 ajouts et 41 suppressions
  1. 46 41
      PIP/PIP-0009.md

+ 46 - 41
PIP/PIP-0009.md

@@ -7,7 +7,7 @@
   Comments-URI: https://discord.gg/sJqcgtD  (channel #pip-0009)
   Status: Proposed
   Created: 2017-12-17
-  Updated: 2017-12-29 (rev2), 2018-01-01 (typos)
+  Updated: 2017-12-29 (rev2), 2018-01-01 (typos), 2018-02-06 (rev3)
 </pre>
 
 ## Summary
@@ -37,14 +37,13 @@ A low-memory, GPU and ASIC-resistant hash algorithm called **Random Hash** is pr
 
 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
-3. The input at round ```x``` depends on the output from round ```x-1```
-4. The input at round ```x``` depends on the output from another previous round ```1..x-1```, randomly selected
-5. The input at round x depends on the output from round ```x-1``` **of a different nonce**
-6. The input at round ```x``` is a compression of (3), (4) and (5) to ```100 bytes```
-7. The output of every round is expanded for memory-hardness
-8. Randomness is generated using ```Mersenne Twister``` algorithm
-9. Randomness is seeded via ```MurMur3``` checksum of previous round
-10. The final round is then hashed again via ```SHA2_256```, in keeping with traditional cryptocurrency approaches.
+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```
+6. The output of every round is expanded for memory-hardness
+7. Randomness is generated using ```Mersenne Twister``` algorithm
+8. Randomness is seeded via ```MurMur3``` checksum of previous round
+9. The final round is then hashed again via ```SHA2_256```, in keeping with traditional cryptocurrency approaches.
 
 ### RandomHash Design
 
@@ -75,34 +74,41 @@ A low-memory, GPU and ASIC-resistant hash algorithm called **Random Hash** is pr
         M = 10KB        // The memory expansion unit (in bytes)
 
         Function RandomHash(blockHeader : ByteArray)
-        begin               
-            Result := SHA2_256( RandomHash( blockHeader, N) )
+        begin
+            let AllOutputs = RandomHash( blockHeader, N)
+            seed = Checksum(neighborOutputs[Length(AllOutputs) - 1])
+            Result := SHA2_256( Compress(AllOutputs, RandomNumberGenerator(seed)) )
         end
 
-        Function RandomHash(blockHeader : ByteArray, Round : Integer) : ByteArray
+        Function RandomHash(blockHeader : ByteArray, Round : Integer) : list of ByteArray
         begin
-            let RoundOutputs = array [1..Round] of ByteArray;
-            let seed = Checksum(blockHeader)      // can hash blockHeader first, but not required
-            let gen = RandomNumberGenerator(seed)
-            let input = blockHeader
+            let RoundOutputs = list of ByteArray;
             
-            for i = 1 to Round do 
-                let random = gen.NextDWord
-                let hashFunc = HASH_ALGO[random % 16]                
-                if i = 1 then
-                    let input = blockHeader
-                else
-                    let prevRound = RoundOutputs[i - 1]
-                    let randPrevRound = RoundOutputs[ random % (i - 1) + 1 ]
-                    let otherNonceHeader = ChangeNonce(blockHeader, random)
-                    let otherNonceRound = RandomHash(otherNonceBlockHeader, i - 1)
-                    let input = Compress(prevRound, randPrevRound, otherNonceRound, gen)
-                let output = hashFunc(input)                
-                output = Expand( output, N - i, gen )
-                gen.Seed = Checksum(output)                
-                RoundOutputs[i] = output 
-
-            Result = RoundOutputs[Round]
+            if Round = 1 then
+                let seed = Checksum(blockHeader)      // can hash blockHeader first, but not required
+                let gen = RandomNumberGenerator(seed)
+                let input = blockHeader
+            else
+                let parentOutputs = RandomHash(blockHeader, Round - 1)
+                RoundOutputs.addAll(parentOutputs)
+                let seed = Checksum(neighborOutputs[Length(neighborOutputs) - 1])
+                let gen = RandomNumberGenerator(seed)
+                
+                let otherNonceHeader = ChangeNonce(blockHeader, gen.NextDWord)
+                let neighborOutputs = RandomHash(blockHeader, Round - 1)
+                RoundOutputs.addAll(neighborOutput)
+                
+                // re-seed RNG based on random other nonce output
+                seed = Checksum(neighborOutputs[Length(neighborOutputs) - 1])
+                gen = RandomNumberGenerator(seed)
+                let input = Compress(RoundOutputs, gen)
+            
+            let hashFunc = HASH_ALGO[gen.NextDWord % 16]   
+            let output = hashFunc(input)                
+            output = Expand( output, N - i, gen )
+            RoundOutputs.add(output)
+
+            Result := RoundOutputs
         end
 
 
@@ -128,19 +134,15 @@ A low-memory, GPU and ASIC-resistant hash algorithm called **Random Hash** is pr
             Result = output
         end
 
-        function Compress(input1, input2, input3 : ByteArray, gen : RandomNumberGenerator) : ByteArray
+        function Compress(inputs : list of ByteArray, gen : RandomNumberGenerator) : ByteArray
         begin
             let output = Byte[0..99]
 
             for i = 0 to 99 do
-                var random = gen.NextDWord
-                case random % 3 do
-                    0: let source = input1
-                    1: let source = input2
-                    2: let source = input3
-                output[i] = source[random % Length(source)]
+                let source = inputs[ gen.NextDWord % Length(inputs) ]
+                output[i] = source[ gen.NextDWord % Length(source) ]
 
-            Result = output
+            Result := output
         end 
 
         function ChangeNonce(blockHeader : ByteArray, nonce : Integer) : ByteArray
@@ -338,6 +340,9 @@ This PIP is not backwards compatible and requires a hard-fork activation. Previo
 
 A reference implementation will be provided in the coming weeks.
  
+## Acknowledgements
+Refinements to improve GPU-hardness were provided by Ian Muldoon.
+ 
 ## Links
 
 1. [Mersennne Twister Implementation (Lazarus/FPC)][1]