private byte[] Compress(ChecksummedByteCollection aInputs) { byte[] result = new byte[100]; uint seed = aInputs.Checksum; Mersenne32 gen = new Mersenne32(seed); int inputCount = aInputs.Count; for (int i = 0; i < 100; i++) { byte[] source = aInputs.Get((int)(gen.NextUInt32() % inputCount)); result[i] = source[gen.NextUInt32() % source.Length]; } return(result); }
private ChecksummedByteCollection Hash(byte[] aBlockHeader, int aRound) { if ((aRound < 1) || (aRound > N)) { throw new ArgumentOutOfRangeException(string.Format("Round '{0}' must be between 0 and N inclusive", nameof(aRound))); } // NOTE: instance is destroyed by caller! ChecksummedByteCollection roundOutputs = new ChecksummedByteCollection(); Mersenne32 gen = new Mersenne32(0); byte[] roundInput; uint seed; if (aRound == 1) { seed = Checksum(aBlockHeader); gen.Initialize(seed); roundInput = aBlockHeader; } else { ChecksummedByteCollection parentOutputs; if ((aRound == N) && (aBlockHeader.Length >= 4) && (NextNonce == GetNonce(aBlockHeader)) && Arrays.AreEqual(aBlockHeader, _cachedHeader)) { // Parent (round N - 1) has already been calculated so re-use values. This saves 50% of calculations! parentOutputs = _cachedOutput; } else { // Need to calculate parent output parentOutputs = Hash(aBlockHeader, aRound - 1); } seed = parentOutputs.Checksum; gen.Initialize(seed); roundOutputs.AddRange(parentOutputs); // Determine the neighbouring nonce uint neighbourNonce = gen.NextUInt32(); byte[] neighbourNonceHeader = ChangeNonce(aBlockHeader, neighbourNonce); ChecksummedByteCollection neighbourOutputs = Hash(neighbourNonceHeader, aRound - 1); // Cache neighbour nonce n-1 calculation if on final round (neighbour will be next nonce) if (aRound == N) { _cachedNonce = neighbourNonce; _cachedHeader = neighbourNonceHeader; _cachedOutput = neighbourOutputs.Clone(); } roundOutputs.AddRange(neighbourOutputs); roundInput = Compress(roundOutputs); } Func <byte[], byte[]> hashFunc = _hashAlg[gen.NextUInt32() % 18]; byte[] output = hashFunc(roundInput); output = Expand(output, N - aRound); roundOutputs.Add(output); return(roundOutputs); }
private byte[] Expand(byte[] aInput, int aExpansionFactor) { int inputSize = aInput.Length; Mersenne32 gen = new Mersenne32(Checksum(aInput)); byte[] output = new byte[inputSize + (aExpansionFactor * M)]; // Copy the genesis blob Array.Copy(aInput, 0, output, 0, inputSize); int readEnd = inputSize - 1; int copyLen = inputSize; while (readEnd < (output.Length - 1)) { if ((readEnd + 1 + copyLen) > output.Length) { copyLen = (output.Length) - (readEnd + 1); } switch (gen.NextUInt32() % 8) { case 0: MemTransform1(ref output, 0, readEnd + 1, copyLen); break; case 1: MemTransform2(ref output, 0, readEnd + 1, copyLen); break; case 2: MemTransform3(ref output, 0, readEnd + 1, copyLen); break; case 3: MemTransform4(ref output, 0, readEnd + 1, copyLen); break; case 4: MemTransform5(ref output, 0, readEnd + 1, copyLen); break; case 5: MemTransform6(ref output, 0, readEnd + 1, copyLen); break; case 6: MemTransform7(ref output, 0, readEnd + 1, copyLen); break; case 7: MemTransform8(ref output, 0, readEnd + 1, copyLen); break; default: throw new ArgumentException("invalid argument"); } readEnd += copyLen; copyLen += copyLen; } return(output); }