From f7266d01008f5159325c2a178f71c4ef14a3685d Mon Sep 17 00:00:00 2001 From: Nikita Mescheryakov Date: Wed, 4 Sep 2024 09:00:16 +0300 Subject: [PATCH] Blob support --- .../CL/BlobDecoderTests.cs | 60 ++++++++ .../CL/Decoders/BlobDecoder.cs | 97 ++++++++++++ .../CL/Decoders/ChannelDecoder.cs | 33 +++++ .../CL/Decoders/FrameDecoder.cs | 64 ++++++++ .../Nethermind.Optimism/CL/Driver.cs | 54 +++++-- .../CL/EthereumBeaconApi.cs | 140 +++++++++++++++++- .../CL/EthereumL1Bridge.cs | 28 ++-- .../Nethermind.Optimism/CL/IBeaconApi.cs | 21 ++- .../Nethermind.Optimism/CL/IL1Bridge.cs | 5 +- .../Nethermind.Optimism/CL/OptimismCL.cs | 11 +- .../CL/{ => P2P}/IP2PBlockValidator.cs | 0 .../CL/{ => P2P}/IPayloadDecoder.cs | 0 .../CL/{ => P2P}/OptimismCLP2P.cs | 20 +-- .../CL/{ => P2P}/P2PBlockValidator.cs | 0 .../CL/{ => P2P}/PayloadDecoder.cs | 0 .../Nethermind.Optimism/OptimismPlugin.cs | 8 +- 16 files changed, 490 insertions(+), 51 deletions(-) create mode 100644 src/Nethermind/Nethermind.Optimism.Test/CL/BlobDecoderTests.cs create mode 100644 src/Nethermind/Nethermind.Optimism/CL/Decoders/BlobDecoder.cs create mode 100644 src/Nethermind/Nethermind.Optimism/CL/Decoders/ChannelDecoder.cs create mode 100644 src/Nethermind/Nethermind.Optimism/CL/Decoders/FrameDecoder.cs rename src/Nethermind/Nethermind.Optimism/CL/{ => P2P}/IP2PBlockValidator.cs (100%) rename src/Nethermind/Nethermind.Optimism/CL/{ => P2P}/IPayloadDecoder.cs (100%) rename src/Nethermind/Nethermind.Optimism/CL/{ => P2P}/OptimismCLP2P.cs (90%) rename src/Nethermind/Nethermind.Optimism/CL/{ => P2P}/P2PBlockValidator.cs (100%) rename src/Nethermind/Nethermind.Optimism/CL/{ => P2P}/PayloadDecoder.cs (100%) diff --git a/src/Nethermind/Nethermind.Optimism.Test/CL/BlobDecoderTests.cs b/src/Nethermind/Nethermind.Optimism.Test/CL/BlobDecoderTests.cs new file mode 100644 index 00000000000..ddfd4b59a66 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism.Test/CL/BlobDecoderTests.cs @@ -0,0 +1,60 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections; +using System.IO; +using System.Linq; +using Nethermind.Core.Extensions; +using Nethermind.Optimism.CL; +using NUnit.Framework; + +namespace Nethermind.Optimism.Test.CL; + +public class BlobDecoderTests +{ + private static byte[] StringToByteArray(string hex) { + return Enumerable.Range(0, hex.Length) + .Where(x => x % 2 == 0) + .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) + .ToArray(); + } + + [TestCase(994040)] + [TestCase(2243081)] + [TestCase(35443070)] + [TestCase(1484649)] + [TestCase(76888454)] + [TestCase(69007144)] // isLast = false + public void Blob_decode_test(int index) + { + StreamReader inputReader = new StreamReader($"/home/deffrian/Documents/testvectors/input{index}"); + StreamReader outputReader = new StreamReader($"/home/deffrian/Documents/testvectors/output{index}"); + byte[] blob = StringToByteArray(inputReader.ReadToEnd()); + byte[] decoded = StringToByteArray(outputReader.ReadToEnd()); + inputReader.Close(); + outputReader.Close(); + byte[] result = BlobDecoder.DecodeBlob(new BlobSidecar { Blob = blob }); + Assert.That(decoded, Is.EqualTo(result)); + + var frames = FrameDecoder.DecodeFrames(result); + Assert.That(frames.Length, Is.EqualTo(1)); + Assert.That(frames[0].IsLast, Is.True); + + var end = ChannelDecoder.DecodeChannel(frames[0]); + } + + public static IEnumerable BlobTestCases + { + get + { + yield return new TestCaseData( + Bytes.FromHexString(""), + Bytes.FromHexString("") + ) + { + TestName = "Sepolia blob 1" + }; + } + } +} diff --git a/src/Nethermind/Nethermind.Optimism/CL/Decoders/BlobDecoder.cs b/src/Nethermind/Nethermind.Optimism/CL/Decoders/BlobDecoder.cs new file mode 100644 index 00000000000..e1b55734044 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/CL/Decoders/BlobDecoder.cs @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; + +namespace Nethermind.Optimism.CL; + +public class BlobDecoder +{ + static public byte[] DecodeBlob(BlobSidecar blobSidecar) + { + int MaxBlobDataSize = (4 * 31 + 3) * 1024 - 4; + int BlobSize = 4096 * 32; + int length = ((int)blobSidecar.Blob[2] << 16) | ((int)blobSidecar.Blob[3] << 8) | + ((int)blobSidecar.Blob[4]); + if (length > MaxBlobDataSize) + { + throw new Exception("Blob size is too big"); + } + + byte[] output = new byte[MaxBlobDataSize]; + for (int i = 0; i < 27; ++i) + { + output[i] = blobSidecar.Blob[i + 5]; + } + + byte[] encodedByte = new byte[4]; + int blobPos = 32; + int outputPos = 28; + + encodedByte[0] = blobSidecar.Blob[0]; + for (int i = 1; i < 4; ++i) + { + (encodedByte[i], outputPos, blobPos) = DecodeFieldElement(blobSidecar.Blob, outputPos, blobPos, output); + } + + outputPos = ReassembleBytes(outputPos, encodedByte, output); + + for (int i = 1; i < 1024 && outputPos < length; i++) + { + for (int j = 0; j < 4; j++) + { + (encodedByte[j], outputPos, blobPos) = + DecodeFieldElement(blobSidecar.Blob, outputPos, blobPos, output); + } + + outputPos = ReassembleBytes(outputPos, encodedByte, output); + } + + for (int i = length; i < MaxBlobDataSize; i++) + { + if (output[i] != 0) + { + throw new Exception("Wrong output"); + } + } + + output = output[..length]; + for (; blobPos < BlobSize; blobPos++) + { + if (blobSidecar.Blob[blobPos] != 0) + { + throw new Exception("Blob excess data"); + } + } + + return output; + } + + static private (byte, int, int) DecodeFieldElement(byte[] blob, int outPos, int blobPos, byte[] output) { + // two highest order bits of the first byte of each field element should always be 0 + if ((blob[blobPos] & 0b1100_0000) != 0) { + // TODO: remove exception + throw new Exception("Invalid field element"); + } + + for (int i = 0; i < 31; i++) + { + output[outPos + i] = blob[blobPos + i + 1]; + } + + return (blob[blobPos], outPos + 32, blobPos + 32); + } + + static int ReassembleBytes(int outPos, byte[] encodedByte, byte[] output) + { + outPos--; // account for fact that we don't output a 128th byte + byte x = (byte)((encodedByte[0] & 0b0011_1111) | ((encodedByte[1] & 0b0011_0000) << 2)); + byte y = (byte)((encodedByte[1] & 0b0000_1111) | ((encodedByte[3] & 0b0000_1111) << 4)); + byte z = (byte)((encodedByte[2] & 0b0011_1111) | ((encodedByte[3] & 0b0011_0000) << 2)); + // put the re-assembled bytes in their appropriate output locations + output[outPos - 32] = z; + output[outPos - 32 * 2] = y; + output[outPos - 32 * 3] = x; + return outPos; + } +} diff --git a/src/Nethermind/Nethermind.Optimism/CL/Decoders/ChannelDecoder.cs b/src/Nethermind/Nethermind.Optimism/CL/Decoders/ChannelDecoder.cs new file mode 100644 index 00000000000..77bf1828395 --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/CL/Decoders/ChannelDecoder.cs @@ -0,0 +1,33 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.IO; +using System.IO.Compression; +using Nethermind.Core; +using Nethermind.Serialization.Rlp; + +namespace Nethermind.Optimism.CL; + +public class ChannelDecoder +{ + public static byte[] DecodeChannel(Frame frame) + { + if ((frame.FrameData[0] & 0x0F) == 8 || (frame.FrameData[0] & 0x0F) == 15) + { + // zlib + var deflateStream = new DeflateStream(new MemoryStream(frame.FrameData[2..]), CompressionMode.Decompress); + var memoryStream = new MemoryStream(); + deflateStream.CopyTo(memoryStream); + return memoryStream.ToArray(); + } else if (frame.FrameData[0] == 1) + { + // brotli + throw new NotImplementedException("Brotli is not supported"); + } + else + { + throw new Exception($"Unsupported compression algorithm {frame.FrameData[0]}"); + } + } +} diff --git a/src/Nethermind/Nethermind.Optimism/CL/Decoders/FrameDecoder.cs b/src/Nethermind/Nethermind.Optimism/CL/Decoders/FrameDecoder.cs new file mode 100644 index 00000000000..cb4ae0bce4e --- /dev/null +++ b/src/Nethermind/Nethermind.Optimism/CL/Decoders/FrameDecoder.cs @@ -0,0 +1,64 @@ +// SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited +// SPDX-License-Identifier: LGPL-3.0-only + +using System; +using System.Collections.Generic; +using System.Linq; +using Nethermind.Network.Rlpx; + +namespace Nethermind.Optimism.CL; + +public class FrameDecoder +{ + private static (Frame, int) DecodeFrame(byte[] data) + { + byte[] channelId = data[..16]; + UInt16 frameNumber = BitConverter.ToUInt16(data[16..18].Reverse().ToArray()); + UInt32 frameDataLength = BitConverter.ToUInt32(data[18..22].Reverse().ToArray()); + byte[] frameData = data[22..(22 + (int)frameDataLength)]; + byte isLast = data[22 + (int)frameDataLength]; + if (isLast != 0 && isLast != 1) + { + throw new Exception("Invalid isLast flag"); + } + return (new Frame() + { + ChannelId = channelId, + FrameNumber = frameNumber, + FrameData = frameData, + IsLast = isLast == 0 + }, 23 + (int)frameDataLength); + } + + public static Frame[] DecodeFrames(byte[] data) + { + byte version = data[0]; + if (version != 0) + { + throw new Exception($"Frame Decoder version {version} is not supported."); + } + + List frames = new List(); + int pos = 1; + while (pos < data.Length) + { + (Frame frame, int decoded) = DecodeFrame(data[pos..]); + pos += decoded; + frames.Add(frame); + } + + if (pos != data.Length) + { + throw new Exception("Excess frame data"); + } + return frames.ToArray(); + } +} + +public struct Frame +{ + public byte[] ChannelId; + public UInt16 FrameNumber; + public byte[] FrameData; + public bool IsLast; +} diff --git a/src/Nethermind/Nethermind.Optimism/CL/Driver.cs b/src/Nethermind/Nethermind.Optimism/CL/Driver.cs index 14d345e7c96..49e0ed6eb5c 100644 --- a/src/Nethermind/Nethermind.Optimism/CL/Driver.cs +++ b/src/Nethermind/Nethermind.Optimism/CL/Driver.cs @@ -1,12 +1,11 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; using System.Linq; -using Nethermind.Blockchain.Find; +using System.Threading; using Nethermind.Core; -using Nethermind.Core.Crypto; -using Nethermind.Facade.Eth; -using Nethermind.JsonRpc.Modules.Eth; +using Nethermind.Logging; namespace Nethermind.Optimism.CL; @@ -14,28 +13,34 @@ public class Driver { private readonly ICLConfig _config; private readonly IL1Bridge _l1Bridge; + private readonly ILogger _logger; - public Driver(IL1Bridge l1Bridge, ICLConfig config) + public Driver(IL1Bridge l1Bridge, ICLConfig config, ILogger logger) { _config = config; _l1Bridge = l1Bridge; + _logger = logger; } - private void Start() + public void Start() { _l1Bridge.OnNewL1Head += OnNewL1Head; } - private void OnNewL1Head(BlockForRpc block, ulong slotNumber) + private void OnNewL1Head(BeaconBlock block, ulong slotNumber) { + _logger.Error("INVOKED"); + Address sepoliaBatcher = new("0x8F23BB38F531600e5d8FDDaAEC41F13FaB46E98c"); + Address batcherInboxAddress = new("0xff00000000000000000000000000000011155420"); // Filter batch submitter transaction - foreach (TransactionForRpc transaction in block.Transactions.Cast()) + foreach (Transaction transaction in block.Transactions) { - if (_config.BatcherInboxAddress == transaction.To && _config.BatcherAddress == transaction.From) + // _logger.Error($"Tx To: {transaction.To}, From: {transaction.SenderAddress} end"); + if (batcherInboxAddress == transaction.To && sepoliaBatcher == transaction.SenderAddress) { if (transaction.Type == TxType.Blob) { - ProcessBlobBatcherTransaction(transaction); + ProcessBlobBatcherTransaction(transaction, slotNumber); } else { @@ -45,16 +50,35 @@ private void OnNewL1Head(BlockForRpc block, ulong slotNumber) } } - private void ProcessBlobBatcherTransaction(TransactionForRpc transaction) + private async void ProcessBlobBatcherTransaction(Transaction transaction, ulong slotNumber) { - int numberOfBlobs = transaction.BlobVersionedHashes!.Length; - for (int i = 0; i < numberOfBlobs; ++i) + if (_logger.IsError) { - + _logger.Error($"GOT BLOB TRANSACTION To: {transaction.To}, From: {transaction.SenderAddress}"); + } + BlobSidecar[] blobSidecars = await _l1Bridge.GetBlobSidecars(slotNumber); + for (int i = 0; i < transaction.BlobVersionedHashes!.Length; i++) + { + for (int j = 0; j < blobSidecars.Length; ++j) + { + if (blobSidecars[j].BlobVersionedHash.SequenceEqual(transaction.BlobVersionedHashes[i]!)) + { + _logger.Error($"GOT BLOB VERSIONED HASH: {BitConverter.ToString(transaction.BlobVersionedHashes[i]!).Replace("-", "")}"); + _logger.Error($"BLOB: {BitConverter.ToString(blobSidecars[j].Blob[..32]).Replace("-", "")}"); + byte[] data = BlobDecoder.DecodeBlob(blobSidecars[j]); + FrameDecoder.DecodeFrames(data); + // _logger.Error($"DATA: {BitConverter.ToString(data).Replace("-", "")}"); + } + } } + } - private void ProcessCalldataBatcherTransaction(TransactionForRpc transaction) + private void ProcessCalldataBatcherTransaction(Transaction transaction) { + if (_logger.IsError) + { + _logger.Error($"GOT REGULAR TRANSACTION"); + } } } diff --git a/src/Nethermind/Nethermind.Optimism/CL/EthereumBeaconApi.cs b/src/Nethermind/Nethermind.Optimism/CL/EthereumBeaconApi.cs index b70a5456843..87b33de9f8c 100644 --- a/src/Nethermind/Nethermind.Optimism/CL/EthereumBeaconApi.cs +++ b/src/Nethermind/Nethermind.Optimism/CL/EthereumBeaconApi.cs @@ -1,17 +1,149 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System; +using System.Linq; +using System.Net.Http; +using System.Security.Cryptography; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Nethermind.Core; +using Nethermind.Core.Collections; +using Nethermind.Core.Crypto; +using Nethermind.Crypto; +using Nethermind.Logging; +using Nethermind.Serialization.Json; +using Nethermind.Serialization.Rlp; + namespace Nethermind.Optimism.CL; public class EthereumBeaconApi : IBeaconApi { - public BeaconBlock GetHead() + private readonly HttpClient _client; + private readonly IJsonSerializer _jsonSerializer; + private readonly CancellationToken _cancellationToken; + private readonly IEthereumEcdsa _ecdsa; + private readonly ILogger _logger; + + public EthereumBeaconApi(Uri beaconApiUri, IJsonSerializer jsonSerializer, IEthereumEcdsa ecdsa, ILogger logger, CancellationToken cancellationToken) + { + _client = new HttpClient { BaseAddress = beaconApiUri }; + _jsonSerializer = jsonSerializer; + _cancellationToken = cancellationToken; + _ecdsa = ecdsa; + _logger = logger; + } + + public async Task GetHead() + { + GetBlockResponse data = await GetData("/eth/v2/beacon/blocks/head"); + return new BeaconBlock + { + PayloadNumber = data.Data.Message.Body.ExecutionPayload.BlockNumber, + SlotNumber = data.Data.Message.Slot, + ExecutionBlockHash = data.Data.Message.Body.ExecutionPayload.BlockHash, + Transactions = data.Data.Message.Body.ExecutionPayload.Transactions.Select(x => + { + // Should we remove this and use L1 EL to retrieve data? + var tx = Rlp.Decode(x); + tx.SenderAddress = _ecdsa.RecoverAddress(tx, true); + return tx; + }).ToArray() + }; + } + + public async Task GetFinalized() + { + GetBlockResponse data = await GetData("/eth/v2/beacon/blocks/finalized"); + return new BeaconBlock + { + PayloadNumber = data.Data.Message.Body.ExecutionPayload.BlockNumber, + SlotNumber = data.Data.Message.Slot, + ExecutionBlockHash = data.Data.Message.Body.ExecutionPayload.BlockHash, + Transactions = data.Data.Message.Body.ExecutionPayload.Transactions.Select(x => + { + // Should we remove this and use L1 EL to retrieve data? + var tx = Rlp.Decode(x); + tx.SenderAddress = _ecdsa.RecoverAddress(tx, true); + return tx; + }).ToArray() + }; + } + + public async Task GetBlobSidecars(ulong slot) + { + GetBlobSidecarsResponse data = await GetData($"/eth/v1/beacon/blob_sidecars/{slot}"); + for (int i = 0; i < data.Data.Length; ++i) + { + data.Data[i].BlobVersionedHash = (new byte[]{1}).Concat(SHA256.HashData(data.Data[i].KzgCommitment)[1..]).ToArray(); + } + return data.Data; + } + + private async Task GetData(string uri) + { + HttpResponseMessage response = await _client.GetAsync(uri, _cancellationToken); + + if (!response.IsSuccessStatusCode) + { + if (_logger.IsWarn) + { + _logger.Warn($"Unsuccessful {uri} request"); + } + // TODO: remove exception + throw new Exception($"Unsuccessful {uri} request"); + } + + if (_logger.IsDebug) + { + _logger.Debug($"GetData<{typeof(T)}({uri}) result: {await response.Content.ReadAsStringAsync(_cancellationToken)}"); + } + + T decoded = + _jsonSerializer.Deserialize(await response.Content.ReadAsStreamAsync(_cancellationToken)); + + return decoded; + } + + // TODO: remove +#pragma warning disable 0649 + private struct GetBlockResponse + { + public GetBlockData Data; + } + + // TODO: can we avoid additional structs + private struct GetBlockData + { + public GetBlockMessage Message; + } + + private struct GetBlockMessage + { + public ulong Slot; + public GetBlockBody Body; + } + + private struct GetBlockBody + { + [JsonPropertyName("execution_payload")] + public GetBlockExecutionPayload ExecutionPayload; + } + + private struct GetBlockExecutionPayload { - throw new System.NotImplementedException(); + [JsonPropertyName("block_number")] + public ulong BlockNumber; + [JsonPropertyName("block_hash")] + public Hash256 BlockHash; + public byte[][] Transactions; } - public BlobSidecar[] GetBlobSidecars(int slot) + private struct GetBlobSidecarsResponse { - throw new System.NotImplementedException(); + public BlobSidecar[] Data; } +#pragma warning restore 0649 } diff --git a/src/Nethermind/Nethermind.Optimism/CL/EthereumL1Bridge.cs b/src/Nethermind/Nethermind.Optimism/CL/EthereumL1Bridge.cs index 440a34fed4e..9d19d3671c5 100644 --- a/src/Nethermind/Nethermind.Optimism/CL/EthereumL1Bridge.cs +++ b/src/Nethermind/Nethermind.Optimism/CL/EthereumL1Bridge.cs @@ -20,7 +20,6 @@ public class EthereumL1Bridge : IL1Bridge public EthereumL1Bridge(IEthApi ethL1Rpc, IBeaconApi beaconApi, ICLConfig config, ILogManager logManager) { - ArgumentNullException.ThrowIfNull(config.L1EthApiEndpoint); _logger = logManager.GetClassLogger(); _config = config; _ethL1Api = ethL1Rpc; @@ -36,23 +35,26 @@ private async void HeadUpdateLoop() // TODO: Cancellation token while (true) { - BeaconBlock beaconBlock = _beaconApi.GetHead(); + // TODO: can we do it with subscription? + BeaconBlock beaconBlock = await _beaconApi.GetHead(); while (beaconBlock.SlotNumber <= _currentSlot) { - beaconBlock = _beaconApi.GetHead(); + await Task.Delay(100); + beaconBlock = await _beaconApi.GetHead(); } + _logger.Error($"HEAD UPDATED: slot {beaconBlock.SlotNumber}"); // new slot _currentSlot = beaconBlock.SlotNumber; - BlockForRpc? block = await _ethL1Api.GetBlockByNumber(beaconBlock.PayloadNumber); + // BlockForRpc? block = await _ethL1Api.GetBlockByNumber(beaconBlock.PayloadNumber); - if (block is null) - { - if (_logger.IsError) _logger.Error($"Unable to get L1 block"); - return; - } + // if (block is null) + // { + // if (_logger.IsError) _logger.Error($"Unable to get L1 block"); + // return; + // } - OnNewL1Head?.Invoke(block, _currentSlot); + OnNewL1Head?.Invoke(beaconBlock, _currentSlot); // Wait next slot await Task.Delay(12000); @@ -61,12 +63,14 @@ private async void HeadUpdateLoop() public void Start() { + // var res = await _beaconApi.GetHead(); + // await _beaconApi.GetBlobSidecars(res.SlotNumber); _headUpdateTask.Start(); } - public event Action? OnNewL1Head; + public event Action? OnNewL1Head; - public BlobSidecar[] GetBlobSidecars(int slotNumber) + public Task GetBlobSidecars(ulong slotNumber) { return _beaconApi.GetBlobSidecars(slotNumber); } diff --git a/src/Nethermind/Nethermind.Optimism/CL/IBeaconApi.cs b/src/Nethermind/Nethermind.Optimism/CL/IBeaconApi.cs index f04adb13f34..5407e169bfb 100644 --- a/src/Nethermind/Nethermind.Optimism/CL/IBeaconApi.cs +++ b/src/Nethermind/Nethermind.Optimism/CL/IBeaconApi.cs @@ -1,25 +1,40 @@ // SPDX-FileCopyrightText: 2024 Demerzel Solutions Limited // SPDX-License-Identifier: LGPL-3.0-only +using System.Text.Json.Serialization; +using System.Threading.Tasks; +using Nethermind.Core; +using Nethermind.Core.Crypto; + namespace Nethermind.Optimism.CL; public interface IBeaconApi { // /eth/v2/beacon/blocks/head - BeaconBlock GetHead(); + Task GetHead(); + + // /eth/v2/beacon/blocks/finalized + Task GetFinalized(); // /eth/v1/beacon/blob_sidecars/:slot: - BlobSidecar[] GetBlobSidecars(int slot); + Task GetBlobSidecars(ulong slot); } public struct BeaconBlock { public ulong SlotNumber; public ulong PayloadNumber; + public Hash256 ExecutionBlockHash; + public Hash256 BeaconBlockHash; + public Transaction[] Transactions; } public struct BlobSidecar { public byte[] Blob; - public byte[] Kzg; + + [JsonPropertyName("kzg_commitment")] + public byte[] KzgCommitment; + + public byte[] BlobVersionedHash; } diff --git a/src/Nethermind/Nethermind.Optimism/CL/IL1Bridge.cs b/src/Nethermind/Nethermind.Optimism/CL/IL1Bridge.cs index 670b151dab0..c8d1ce016dd 100644 --- a/src/Nethermind/Nethermind.Optimism/CL/IL1Bridge.cs +++ b/src/Nethermind/Nethermind.Optimism/CL/IL1Bridge.cs @@ -2,12 +2,13 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Threading.Tasks; using Nethermind.Facade.Eth; namespace Nethermind.Optimism.CL; public interface IL1Bridge { - event Action? OnNewL1Head; - BlobSidecar[] GetBlobSidecars(int slotNumber); + event Action? OnNewL1Head; + Task GetBlobSidecars(ulong slotNumber); } diff --git a/src/Nethermind/Nethermind.Optimism/CL/OptimismCL.cs b/src/Nethermind/Nethermind.Optimism/CL/OptimismCL.cs index 6d2b93515e3..c9e0d5d2253 100644 --- a/src/Nethermind/Nethermind.Optimism/CL/OptimismCL.cs +++ b/src/Nethermind/Nethermind.Optimism/CL/OptimismCL.cs @@ -6,6 +6,7 @@ using Microsoft.Extensions.DependencyInjection; using Nethermind.Core; using Nethermind.Core.Specs; +using Nethermind.Crypto; using Nethermind.Init.Steps; using Nethermind.JsonRpc.Client; using Nethermind.Libp2p.Protocols.Pubsub; @@ -23,20 +24,24 @@ public class OptimismCL private readonly Driver _driver; private readonly IOptimismEngineRpcModule _engineRpcModule; - public OptimismCL(ISpecProvider specProvider, ICLConfig config, IJsonSerializer jsonSerializer, ITimestamper timestamper, ILogManager logManager, IOptimismEngineRpcModule engineRpcModule) + public OptimismCL(ISpecProvider specProvider, ICLConfig config, IJsonSerializer jsonSerializer, IEthereumEcdsa ecdsa, + CancellationToken cancellationToken, ITimestamper timestamper, ILogManager logManager, + IOptimismEngineRpcModule engineRpcModule) { _logger = logManager.GetClassLogger(); _engineRpcModule = engineRpcModule; _p2p = new OptimismCLP2P(specProvider.ChainId, timestamper, logManager, engineRpcModule); IEthApi ethApi = new EthereumEthApi(config, jsonSerializer, logManager); - IBeaconApi beaconApi = new EthereumBeaconApi(); + IBeaconApi beaconApi = new EthereumBeaconApi(new Uri(config.L1BeaconApiEndpoint!), jsonSerializer, ecdsa, _logger, + cancellationToken); _l1Bridge = new EthereumL1Bridge(ethApi, beaconApi, config, logManager); - _driver = new Driver(_l1Bridge, config); + _driver = new Driver(_l1Bridge, config, _logger); } public void Start() { _p2p.Start(); _l1Bridge.Start(); + _driver.Start(); } } diff --git a/src/Nethermind/Nethermind.Optimism/CL/IP2PBlockValidator.cs b/src/Nethermind/Nethermind.Optimism/CL/P2P/IP2PBlockValidator.cs similarity index 100% rename from src/Nethermind/Nethermind.Optimism/CL/IP2PBlockValidator.cs rename to src/Nethermind/Nethermind.Optimism/CL/P2P/IP2PBlockValidator.cs diff --git a/src/Nethermind/Nethermind.Optimism/CL/IPayloadDecoder.cs b/src/Nethermind/Nethermind.Optimism/CL/P2P/IPayloadDecoder.cs similarity index 100% rename from src/Nethermind/Nethermind.Optimism/CL/IPayloadDecoder.cs rename to src/Nethermind/Nethermind.Optimism/CL/P2P/IPayloadDecoder.cs diff --git a/src/Nethermind/Nethermind.Optimism/CL/OptimismCLP2P.cs b/src/Nethermind/Nethermind.Optimism/CL/P2P/OptimismCLP2P.cs similarity index 90% rename from src/Nethermind/Nethermind.Optimism/CL/OptimismCLP2P.cs rename to src/Nethermind/Nethermind.Optimism/CL/P2P/OptimismCLP2P.cs index 3212b25ccae..fc0d59a5fa6 100644 --- a/src/Nethermind/Nethermind.Optimism/CL/OptimismCLP2P.cs +++ b/src/Nethermind/Nethermind.Optimism/CL/P2P/OptimismCLP2P.cs @@ -91,7 +91,7 @@ public void Start() proto.OnAddPeer?.Invoke(["/ip4/217.22.153.164/tcp/31660/p2p/16Uiu2HAmG5hBYavoanawCzz1cu5H7XNNSaA7BYNvwa7DNmojei6g"]); } - async void OnMessage(byte[] msg) + void OnMessage(byte[] msg) { int length = Snappy.GetUncompressedLength(msg); byte[] decompressed = new byte[length]; @@ -131,15 +131,15 @@ async void OnMessage(byte[] msg) // await Task.Delay(5000); - var npResult = await _engineRpcModule.engine_newPayloadV3(payloadDecoded, Array.Empty(), - payloadDecoded.ParentBeaconBlockRoot); - - _logger.Error($"NP RESULT {npResult.Data.Status}"); - - var fcuResult = await _engineRpcModule.engine_forkchoiceUpdatedV3( - new ForkchoiceStateV1(payloadDecoded.BlockHash, payloadDecoded.BlockHash, payloadDecoded.BlockHash), null); - - _logger.Error($"FCU RESULT {fcuResult.Data.PayloadStatus.Status}"); + // var npResult = await _engineRpcModule.engine_newPayloadV3(payloadDecoded, Array.Empty(), + // payloadDecoded.ParentBeaconBlockRoot); + // + // _logger.Error($"NP RESULT {npResult.Data.Status}"); + // + // var fcuResult = await _engineRpcModule.engine_forkchoiceUpdatedV3( + // new ForkchoiceStateV1(payloadDecoded.BlockHash, payloadDecoded.BlockHash, payloadDecoded.BlockHash), null); + // + // _logger.Error($"FCU RESULT {fcuResult.Data.PayloadStatus.Status}"); } private MessageId CalculateMessageId(Message message) diff --git a/src/Nethermind/Nethermind.Optimism/CL/P2PBlockValidator.cs b/src/Nethermind/Nethermind.Optimism/CL/P2P/P2PBlockValidator.cs similarity index 100% rename from src/Nethermind/Nethermind.Optimism/CL/P2PBlockValidator.cs rename to src/Nethermind/Nethermind.Optimism/CL/P2P/P2PBlockValidator.cs diff --git a/src/Nethermind/Nethermind.Optimism/CL/PayloadDecoder.cs b/src/Nethermind/Nethermind.Optimism/CL/P2P/PayloadDecoder.cs similarity index 100% rename from src/Nethermind/Nethermind.Optimism/CL/PayloadDecoder.cs rename to src/Nethermind/Nethermind.Optimism/CL/P2P/PayloadDecoder.cs diff --git a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs index ab5b155527d..2f0f2c0135a 100644 --- a/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs +++ b/src/Nethermind/Nethermind.Optimism/OptimismPlugin.cs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LGPL-3.0-only using System; +using System.Threading; using System.Threading.Tasks; using Nethermind.Api; using Nethermind.Api.Extensions; @@ -23,6 +24,7 @@ using Nethermind.Merge.Plugin.Synchronization; using Nethermind.Synchronization.ParallelSync; using Nethermind.HealthChecks; +using Nethermind.Init.Steps; using Nethermind.Optimism.CL; using Nethermind.Serialization.Json; using Nethermind.Specs.ChainSpecStyle; @@ -284,9 +286,11 @@ public async Task InitRpcModules() _api.RpcModuleProvider.RegisterSingle(opEngine); + StepDependencyException.ThrowIfNull(_api.EthereumEcdsa); + ICLConfig clConfig = _api.Config(); - _cl = new OptimismCL(_api.SpecProvider, clConfig, _api.EthereumJsonSerializer, _api.Timestamper, _api!.LogManager, opEngine); - _cl.Start(); + _cl = new OptimismCL(_api.SpecProvider, clConfig, _api.EthereumJsonSerializer, _api.EthereumEcdsa, new CancellationToken(), _api.Timestamper, _api!.LogManager, opEngine); + // _cl.Start(); if (_logger.IsInfo) _logger.Info("Optimism Engine Module has been enabled"); }