From b8e3d3e80f14d7784c3cb008b4ca3e0cad48bf04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kajetan=20Sta=C5=84czak?= Date: Wed, 28 Aug 2019 13:39:35 +0100 Subject: [PATCH] Eip 152 (#839) * EIP 152 - Blake2 'F' unoptimized implementation fix #816 --- .../Nethermind.Core.Test/Blake2Tests.cs | 40 +++++ src/Nethermind/Nethermind.Core/Blake2.cs | 139 ++++++++++++++++++ .../Nethermind.Evm.Test/Eip152Tests.cs | 56 +++++++ src/Nethermind/Nethermind.Evm/Metrics.cs | 1 + .../Precompiles/AddressExtensions.cs | 9 +- .../Precompiles/Blake2BPrecompiledContract.cs | 71 +++++++++ .../Nethermind.Evm/VirtualMachine.cs | 3 +- 7 files changed, 316 insertions(+), 3 deletions(-) create mode 100644 src/Nethermind/Nethermind.Core.Test/Blake2Tests.cs create mode 100644 src/Nethermind/Nethermind.Core/Blake2.cs create mode 100644 src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs create mode 100644 src/Nethermind/Nethermind.Evm/Precompiles/Blake2BPrecompiledContract.cs diff --git a/src/Nethermind/Nethermind.Core.Test/Blake2Tests.cs b/src/Nethermind/Nethermind.Core.Test/Blake2Tests.cs new file mode 100644 index 00000000000..d6155f331df --- /dev/null +++ b/src/Nethermind/Nethermind.Core.Test/Blake2Tests.cs @@ -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 . + */ + +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); + } + } +} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Core/Blake2.cs b/src/Nethermind/Nethermind.Core/Blake2.cs new file mode 100644 index 00000000000..5b9625500c2 --- /dev/null +++ b/src/Nethermind/Nethermind.Core/Blake2.cs @@ -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 . + */ + +using System; +using Nethermind.Core.Extensions; + +namespace Nethermind.Core +{ + /// + /// Code adapted from pantheon (https://github.com/PegaSysEng/pantheon) + /// + 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)); + } + } +} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs b/src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs new file mode 100644 index 00000000000..80f8f9aaa0d --- /dev/null +++ b/src/Nethermind/Nethermind.Evm.Test/Eip152Tests.cs @@ -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 . + */ + +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); + } + } +} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Evm/Metrics.cs b/src/Nethermind/Nethermind.Evm/Metrics.cs index 48c93772bd5..d06428b88e6 100644 --- a/src/Nethermind/Nethermind.Evm/Metrics.cs +++ b/src/Nethermind/Nethermind.Evm/Metrics.cs @@ -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; } } } \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/AddressExtensions.cs b/src/Nethermind/Nethermind.Evm/Precompiles/AddressExtensions.cs index 55aa94044c5..1754466de7c 100644 --- a/src/Nethermind/Nethermind.Evm/Precompiles/AddressExtensions.cs +++ b/src/Nethermind/Nethermind.Evm/Precompiles/AddressExtensions.cs @@ -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; } @@ -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; } } } \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Evm/Precompiles/Blake2BPrecompiledContract.cs b/src/Nethermind/Nethermind.Evm/Precompiles/Blake2BPrecompiledContract.cs new file mode 100644 index 00000000000..3065d21efd8 --- /dev/null +++ b/src/Nethermind/Nethermind.Evm/Precompiles/Blake2BPrecompiledContract.cs @@ -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 . + */ + +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); + } + } +} \ No newline at end of file diff --git a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs index d758772c237..5df2d45daaa 100644 --- a/src/Nethermind/Nethermind.Evm/VirtualMachine.cs +++ b/src/Nethermind/Nethermind.Evm/VirtualMachine.cs @@ -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, }; }