-
Notifications
You must be signed in to change notification settings - Fork 438
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
16 changed files
with
490 additions
and
51 deletions.
There are no files selected for viewing
60 changes: 60 additions & 0 deletions
60
src/Nethermind/Nethermind.Optimism.Test/CL/BlobDecoderTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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" | ||
}; | ||
} | ||
} | ||
} |
97 changes: 97 additions & 0 deletions
97
src/Nethermind/Nethermind.Optimism/CL/Decoders/BlobDecoder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
src/Nethermind/Nethermind.Optimism/CL/Decoders/ChannelDecoder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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]}"); | ||
} | ||
} | ||
} |
64 changes: 64 additions & 0 deletions
64
src/Nethermind/Nethermind.Optimism/CL/Decoders/FrameDecoder.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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<Frame> frames = new List<Frame>(); | ||
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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.