Skip to content

Commit

Permalink
Eip 152 (#839)
Browse files Browse the repository at this point in the history
* EIP 152 - Blake2 'F' unoptimized implementation fix #816
  • Loading branch information
tkstanczak authored Aug 28, 2019
1 parent 971671b commit b8e3d3e
Show file tree
Hide file tree
Showing 7 changed files with 316 additions and 3 deletions.
40 changes: 40 additions & 0 deletions src/Nethermind/Nethermind.Core.Test/Blake2Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2018 Demerzel Solutions Limited
* This file is part of the Nethermind library.
*
* The Nethermind library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Nethermind library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.
*/

using FluentAssertions;
using Nethermind.Core.Extensions;
using NUnit.Framework;

namespace Nethermind.Core.Test
{
public class Blake2Tests
{
[Test]
[TestCase("0000000048c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", "08c9bcf367e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d282e6ad7f520e511f6c3e2b8c68059b9442be0454267ce079217e1319cde05b")]
[TestCase("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", "ba80a53f981c4d0d6a2797b69f12f6e94c212f14685ac4b74b12bb6fdbffa2d17d87c5392aab792dc252d5de4533cc9518d38aa8dbf1925ab92386edd4009923")]
[TestCase("0000000c48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000", "75ab69d3190a562c51aef8d88f1c2775876944407270c42c9844252c26d2875298743e7f6d5ea2f2d3e8d226039cd31b4e426ac4f2d3d666a610c2116fde4735")]
[TestCase("0000000148c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", "b63a380cb2897d521994a85234ee2c181b5f844d2c624c002677e9703449d2fba551b3a8333bcdf5f2f7e08993d53923de3d64fcc68c034e717b9293fed7a421")]
// [TestCase("ffffffff48c9bdf267e6096a3ba7ca8485ae67bb2bf894fe72f36e3cf1361d5f3af54fa5d182e6ad7f520e511f6c3e2b8c68059b6bbd41fbabd9831f79217e1319cde05b61626300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000001", "fc59093aafa9ab43daae0e914c57635c5402d8e3d2130eb9b3cc181de7f0ecf9b22bf99a7815ce16419e200e01846e6b5df8cc7703041bbceb571de6631d2615")]
public void compression_function_should_return_valid_output(string input, string output)
{
var blake2 = new Blake2();
var result = blake2.Compress(Bytes.FromHexString(input)).ToHexString();
result.Should().BeEquivalentTo(output);
}
}
}
139 changes: 139 additions & 0 deletions src/Nethermind/Nethermind.Core/Blake2.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright (c) 2018 Demerzel Solutions Limited
* This file is part of the Nethermind library.
*
* The Nethermind library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Nethermind library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using Nethermind.Core.Extensions;

namespace Nethermind.Core
{
/// <summary>
/// Code adapted from pantheon (https://github.com/PegaSysEng/pantheon)
/// </summary>
public class Blake2
{
private static readonly byte[][] Precomputed =
{
new byte[] {0, 2, 4, 6, 1, 3, 5, 7, 8, 10, 12, 14, 9, 11, 13, 15},
new byte[] {14, 4, 9, 13, 10, 8, 15, 6, 1, 0, 11, 5, 12, 2, 7, 3},
new byte[] {11, 12, 5, 15, 8, 0, 2, 13, 10, 3, 7, 9, 14, 6, 1, 4},
new byte[] {7, 3, 13, 11, 9, 1, 12, 14, 2, 5, 4, 15, 6, 10, 0, 8},
new byte[] {9, 5, 2, 10, 0, 7, 4, 15, 14, 11, 6, 3, 1, 12, 8, 13},
new byte[] {2, 6, 0, 8, 12, 10, 11, 3, 4, 7, 15, 1, 13, 5, 14, 9},
new byte[] {12, 1, 14, 4, 5, 15, 13, 10, 0, 6, 9, 8, 7, 3, 2, 11},
new byte[] {13, 7, 12, 3, 11, 14, 1, 9, 5, 15, 8, 2, 0, 4, 6, 10},
new byte[] {6, 14, 11, 0, 15, 9, 3, 8, 12, 13, 1, 10, 2, 7, 4, 5},
new byte[] {10, 8, 7, 1, 2, 4, 6, 5, 15, 9, 3, 13, 11, 14, 12, 0}
};

private static readonly ulong[] IV =
{
0x6a09e667f3bcc908ul, 0xbb67ae8584caa73bul, 0x3c6ef372fe94f82bul,
0xa54ff53a5f1d36f1ul, 0x510e527fade682d1ul, 0x9b05688c2b3e6c1ful,
0x1f83d9abfb41bd6bul, 0x5be0cd19137e2179ul
};

private ulong[] _h = new ulong[8];
private ulong[] _m = new ulong[16];
private ulong[] _t = new ulong[2];
private ulong[] _v = new ulong[16];
private bool _f;
private uint _rounds = 12;

public byte[] Compress(byte[] input)
{
Init(input);

Array.Copy(_h, 0, _v, 0, 8);
Array.Copy(IV, 0, _v, 8, 8);

_v[12] ^= _t[0];
_v[13] ^= _t[1];

if (_f)
{
_v[14] ^= 0xfffffffffffffffful;
}

for (var i = 0; i < _rounds; ++i)
{
var s = Precomputed[i % 10];
Compute(_m[s[0]], _m[s[4]], 0, 4, 8, 12);
Compute(_m[s[1]], _m[s[5]], 1, 5, 9, 13);
Compute(_m[s[2]], _m[s[6]], 2, 6, 10, 14);
Compute(_m[s[3]], _m[s[7]], 3, 7, 11, 15);
Compute(_m[s[8]], _m[s[12]], 0, 5, 10, 15);
Compute(_m[s[9]], _m[s[13]], 1, 6, 11, 12);
Compute(_m[s[10]], _m[s[14]], 2, 7, 8, 13);
Compute(_m[s[11]], _m[s[15]], 3, 4, 9, 14);
}

for (var offset = 0; offset < _h.Length; offset++)
{
_h[offset] ^= _v[offset] ^ _v[offset + 8];
}

var result = new byte[_h.Length * 8];
for (var i = 0; i < _h.Length; i++)
{
Array.Copy(_h[i].ToByteArray(Bytes.Endianness.Little), 0, result, i * 8, 8);
}

return result;
}

private void Init(byte[] input)
{
_rounds = input.Slice(0, 4).ToUInt32();
var h = input.Slice(4, 64);
var m = input.Slice(68, 128);
for (var i = 0; i < _h.Length; i++)
{
var offset = i * 8;
_h[i] = h.Slice(offset, 8).ToUInt64(Bytes.Endianness.Little);
}

for (var i = 0; i < _m.Length; i++)
{
var offset = i * 8;
_m[i] = m.Slice(offset, 8).ToUInt64(Bytes.Endianness.Little);
}

_t[0] = input.Slice(196, 8).ToUInt64(Bytes.Endianness.Little);
_t[1] = input.Slice(204, 8).ToUInt64(Bytes.Endianness.Little);
_f = input[212] != 0;
}

private void Compute(ulong a, ulong b, int i, int j, int k, int l)
{
_v[i] += a + _v[j];
_v[l] = RotateLeft(_v[l] ^ _v[i], -32);
_v[k] += _v[l];
_v[j] = RotateLeft(_v[j] ^ _v[k], -24);

_v[i] += b + _v[j];
_v[l] = RotateLeft(_v[l] ^ _v[i], -16);
_v[k] += _v[l];
_v[j] = RotateLeft(_v[j] ^ _v[k], -63);
}

private static ulong RotateLeft(ulong value, int count)
{
return (value << count) | (value >> (64 - count));
}
}
}
56 changes: 56 additions & 0 deletions src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/*
* Copyright (c) 2018 Demerzel Solutions Limited
* This file is part of the Nethermind library.
*
* The Nethermind library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Nethermind library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.
*/

using Nethermind.Core.Specs;
using Nethermind.Evm.Precompiles;
using NUnit.Framework;

namespace Nethermind.Evm.Test
{
public class Eip152Tests : VirtualMachineTestsBase
{
private const int InputLength = 213;
protected override long BlockNumber => MainNetSpecProvider.IstanbulBlockNumber + _blockNumberAdjustment;

private int _blockNumberAdjustment;

[TearDown]
public void TearDown()
{
_blockNumberAdjustment = 0;
}

[Test]
public void before_istanbul()
{
_blockNumberAdjustment = -1;
var precompileAddress = Blake2BPrecompiledContract.Instance.Address;
Assert.False(precompileAddress.IsPrecompiled(Spec));
}

[Test]
public void after_istanbul()
{
var code = Prepare.EvmCode
.CallWithInput(Blake2BPrecompiledContract.Instance.Address, 1000L, new byte[InputLength])
.Done;
var result = Execute(code);
Assert.AreEqual(StatusCode.Success, result.StatusCode);
}
}
}
1 change: 1 addition & 0 deletions src/Nethermind/Nethermind.Evm/Metrics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@ public class Metrics
public static long ModExpPrecompile { get; set; }
public static long Ripemd160Precompile { get; set; }
public static long Sha256Precompile { get; set; }
public static long Blake2BPrecompile { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public static bool IsPrecompiled(this Address address, IReleaseSpec releaseSpec)
}

BigInteger asInt = address.Bytes.ToUnsignedBigInteger();
if (asInt == 0 || asInt > 8)
if (asInt == 0 || asInt > 9)
{
return false;
}
Expand All @@ -52,8 +52,13 @@ public static bool IsPrecompiled(this Address address, IReleaseSpec releaseSpec)
{
return releaseSpec.IsEip196Enabled;
}

if (asInt == 8)
{
return releaseSpec.IsEip197Enabled;
}

return asInt == 8 && releaseSpec.IsEip197Enabled;
return asInt == 9 && releaseSpec.IsEip152Enabled;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright (c) 2018 Demerzel Solutions Limited
* This file is part of the Nethermind library.
*
* The Nethermind library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The Nethermind library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the Nethermind. If not, see <http://www.gnu.org/licenses/>.
*/

using Nethermind.Core;
using Nethermind.Core.Extensions;
using Nethermind.Core.Specs;

namespace Nethermind.Evm.Precompiles
{
public class Blake2BPrecompiledContract : IPrecompiledContract
{
private const int RequiredInputLength = 213;
public static readonly IPrecompiledContract Instance = new Blake2BPrecompiledContract();

public Address Address { get; } = Address.FromNumber(9);

public long BaseGasCost(IReleaseSpec releaseSpec) => 0;

public long DataGasCost(byte[] inputData, IReleaseSpec releaseSpec)
{
if (inputData.Length != RequiredInputLength)
{
return 0;
}

var finalByte = inputData[212];
if (finalByte != 0 && finalByte != 1)
{
return 0;
}

var rounds = inputData.Slice(0, 4).ToUInt32();

return rounds;
}

public (byte[], bool) Run(byte[] inputData)
{
if (inputData.Length != RequiredInputLength)
{
return (Bytes.Empty, false);
}

var finalByte = inputData[212];
if (finalByte != 0 && finalByte != 1)
{
return (Bytes.Empty, false);
}

var blake = new Blake2();
var result = blake.Compress(inputData);

return (result, true);
}
}
}
3 changes: 2 additions & 1 deletion src/Nethermind/Nethermind.Evm/VirtualMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,7 +395,8 @@ private void InitializePrecompiledContracts()
[Bn128AddPrecompiledContract.Instance.Address] = Bn128AddPrecompiledContract.Instance,
[Bn128MulPrecompiledContract.Instance.Address] = Bn128MulPrecompiledContract.Instance,
[Bn128PairingPrecompiledContract.Instance.Address] = Bn128PairingPrecompiledContract.Instance,
[ModExpPrecompiledContract.Instance.Address] = ModExpPrecompiledContract.Instance
[ModExpPrecompiledContract.Instance.Address] = ModExpPrecompiledContract.Instance,
[Blake2BPrecompiledContract.Instance.Address] = Blake2BPrecompiledContract.Instance,
};
}

Expand Down

0 comments on commit b8e3d3e

Please sign in to comment.