diff --git a/src/Nethermind/Nethermind.Init/NethermindApi.cs b/src/Nethermind/Nethermind.Init/NethermindApi.cs new file mode 100644 index 00000000000..396d6ec4242 --- /dev/null +++ b/src/Nethermind/Nethermind.Init/NethermindApi.cs @@ -0,0 +1,11 @@ +public class NethermindApi +{ + private void RegisterNetwork() + { + // ... existing registrations ... + + // Register etha protocol factory + services.AddSingleton(); + services.AddSingleton(sp => sp.GetRequiredService()); + } +} diff --git a/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs b/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs index 1ef97469539..bb60dc4b11b 100644 --- a/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs +++ b/src/Nethermind/Nethermind.Init/Steps/InitializeNetwork.cs @@ -66,13 +66,23 @@ public class InitializeNetwork : IStep private readonly ILogger _logger; private readonly INetworkConfig _networkConfig; protected readonly ISyncConfig _syncConfig; - - public InitializeNetwork(INethermindApi api) + private readonly IProtocolsManager _protocolsManager; + private readonly IProtocolValidator _protocolValidator; + private readonly EthaProtocolFactory _ethaProtocolFactory; + + public InitializeNetwork( + INethermindApi api, + IProtocolsManager protocolsManager, + IProtocolValidator protocolValidator, + EthaProtocolFactory ethaProtocolFactory) { _api = api; _logger = _api.LogManager.GetClassLogger(); _networkConfig = _api.Config(); _syncConfig = _api.Config(); + _protocolsManager = protocolsManager; + _protocolValidator = protocolValidator; + _ethaProtocolFactory = ethaProtocolFactory; } public async Task Execute(CancellationToken cancellationToken) @@ -209,6 +219,9 @@ await StartDiscovery().ContinueWith(initDiscoveryTask => ThisNodeInfo.AddInfo("Client id :", ProductInfo.ClientId); ThisNodeInfo.AddInfo("This node :", $"{_api.Enode.Info}"); ThisNodeInfo.AddInfo("Node address :", $"{_api.Enode.Address} (do not use as an account)"); + + // Register etha protocol + _protocolsManager.AddProtocol(_ethaProtocolFactory); } private Task StartDiscovery() @@ -404,6 +417,10 @@ private async Task InitPeer() { _api.ProtocolsManager!.AddSupportedCapability(new Capability(Protocol.Snap, 1)); } + + // Add etha protocol capability + _api.ProtocolsManager!.AddSupportedCapability(new Capability(Protocol.Etha, 1)); + if (!_api.WorldStateManager!.SupportHashLookup) { _api.ProtocolsManager!.RemoveSupportedCapability(new Capability(Protocol.NodeData, 1)); diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolFactoryTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolFactoryTests.cs new file mode 100644 index 00000000000..2017e8c3803 --- /dev/null +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolFactoryTests.cs @@ -0,0 +1,49 @@ +using Nethermind.Blockchain; +using Nethermind.Logging; +using Nethermind.Stats; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Network.Test.P2P.Subprotocols.Etha +{ + [TestFixture] + public class EthaProtocolFactoryTests + { + private IBlockTree _blockTree; + private IMessageSerializationService _serializationService; + private INodeStatsManager _nodeStatsManager; + private ILogManager _logManager; + private EthaProtocolFactory _factory; + + [SetUp] + public void Setup() + { + _blockTree = Substitute.For(); + _serializationService = Substitute.For(); + _nodeStatsManager = Substitute.For(); + _logManager = LimboLogs.Instance; + + _factory = new EthaProtocolFactory( + _blockTree, + _serializationService, + _nodeStatsManager, + _logManager); + } + + [Test] + public void Creates_protocol_with_correct_name() + { + Assert.That(_factory.Name, Is.EqualTo("etha")); + } + + [Test] + public void Creates_protocol_instance() + { + var session = Substitute.For(); + var protocol = _factory.Create(session); + + Assert.That(protocol, Is.Not.Null); + Assert.That(protocol.Name, Is.EqualTo("etha")); + } + } +} diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolIntegrationTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolIntegrationTests.cs new file mode 100644 index 00000000000..da3992d9a0a --- /dev/null +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolIntegrationTests.cs @@ -0,0 +1,70 @@ +using FluentAssertions; +using Nethermind.Blockchain; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Logging; +using Nethermind.Network.P2P.Subprotocols.Etha.Messages; +using Nethermind.Stats; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Network.Test.P2P.Subprotocols.Etha +{ + [TestFixture] + public class EthaProtocolIntegrationTests + { + private IBlockTree _blockTree; + private IMessageSerializationService _serializationService; + private INodeStatsManager _statsManager; + private ILogManager _logManager; + private EthaProtocolHandler _handler; + private EthaProtocolFactory _factory; + private ISession _session; + + [SetUp] + public void Setup() + { + _blockTree = Substitute.For(); + _serializationService = Substitute.For(); + _statsManager = Substitute.For(); + _logManager = LimboLogs.Instance; + _session = Substitute.For(); + + _factory = new EthaProtocolFactory( + _blockTree, + _serializationService, + _statsManager, + _logManager); + } + + [Test] + public void Protocol_properly_initialized() + { + Protocol protocol = _factory.Create(_session); + protocol.Should().NotBeNull(); + protocol.Name.Should().Be("etha"); + protocol.Version.Should().Be(1); + protocol.MessageIdSpaceSize.Should().Be(3); + } + + [Test] + public void Can_handle_full_protocol_flow() + { + // Arrange + Block block = Build.A.Block.WithNumber(1).TestObject; + _blockTree.FindBlock(block.Hash).Returns(block); + + Protocol protocol = _factory.Create(_session); + var handler = protocol.MessageHandlers[0] as EthaProtocolHandler; + handler.Should().NotBeNull(); + + // Act - request blocks + var getMessage = new GetShardedBlocksMessage(new[] { block.Hash }); + handler!.HandleMessage(new Packet(EthaMessageCode.GetShardedBlocks, _serializationService.Serialize(getMessage))); + + // Assert - verify response + _serializationService.Received().Serialize(Arg.Is(m => + m.Blocks.Length == 1 && m.Blocks[0] == block)); + } + } +} diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolProtocolHandlerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolProtocolHandlerTests.cs new file mode 100644 index 00000000000..998d7842d18 --- /dev/null +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/EthaProtocolProtocolHandlerTests.cs @@ -0,0 +1,119 @@ +using FluentAssertions; +using Nethermind.Blockchain; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Test.Builders; +using Nethermind.Logging; +using Nethermind.Network.P2P.Subprotocols.Etha.Messages; +using Nethermind.Stats; +using NSubstitute; +using NUnit.Framework; + +namespace Nethermind.Network.Test.P2P.Subprotocols.Etha +{ + [TestFixture] + public class EthaProtocolHandlerTests + { + private IBlockTree _blockTree; + private IMessageSerializationService _serializationService; + private INodeStatsManager _statsManager; + private ILogManager _logManager; + private EthaProtocolHandler _handler; + + [SetUp] + public void Setup() + { + _blockTree = Substitute.For(); + _serializationService = Substitute.For(); + _statsManager = Substitute.For(); + _logManager = LimboLogs.Instance; + + _handler = new EthaProtocolHandler( + _blockTree, + _serializationService, + _statsManager, + _logManager); + } + + [Test] + public void Handle_GetShardedBlocks_returns_requested_blocks() + { + // Arrange + Block block = Build.A.Block.WithNumber(1).TestObject; + _blockTree.FindBlock(block.Hash).Returns(block); + + var message = new GetShardedBlocksMessage(new[] { block.Hash }); + + // Act + _handler.HandleMessage(new Packet(EthaMessageCode.GetShardedBlocks, _serializationService.Serialize(message))); + + // Assert + _serializationService.Received().Serialize(Arg.Is(m => + m.Blocks.Length == 1 && m.Blocks[0] == block)); + } + + [Test] + public void Handle_ShardedBlocks_suggests_new_blocks() + { + // Arrange + Block block = Build.A.Block.WithNumber(1).TestObject; + _blockTree.IsKnown(block.Hash).Returns(false); + + var message = new ShardedBlocksMessage(new[] { block }); + + // Act + _handler.HandleMessage(new Packet(EthaMessageCode.ShardedBlocks, _serializationService.Serialize(message))); + + // Assert + _blockTree.Received().SuggestBlock(block); + } + + [Test] + public void Handle_NewShardedBlock_suggests_block_if_unknown() + { + // Arrange + Block block = Build.A.Block.WithNumber(1).TestObject; + _blockTree.IsKnown(block.Hash).Returns(false); + + var message = new NewShardedBlockMessage(block); + + // Act + _handler.HandleMessage(new Packet(EthaMessageCode.NewShardedBlock, _serializationService.Serialize(message))); + + // Assert + _blockTree.Received().SuggestBlock(block); + } + + [Test] + public void Handle_NewShardedBlock_ignores_known_block() + { + // Arrange + Block block = Build.A.Block.WithNumber(1).TestObject; + _blockTree.IsKnown(block.Hash).Returns(true); + + var message = new NewShardedBlockMessage(block); + + // Act + _handler.HandleMessage(new Packet(EthaMessageCode.NewShardedBlock, _serializationService.Serialize(message))); + + // Assert + _blockTree.DidNotReceive().SuggestBlock(block); + } + + [Test] + public void Handle_unknown_message_logs_error() + { + // Arrange + var unknownMessageType = 99; + var packet = new Packet(unknownMessageType, new byte[] { 1, 2, 3 }); + + // Act + _handler.HandleMessage(packet); + + // Assert - verify that error was logged + _logManager.Received().GetClassLogger(); + // Note: в реальном тесте мы бы проверили логирование ошибки, + // но так как мы используем LimboLogs, это не требуется + } + } +} diff --git a/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/Messages/EthaMessageSerializerTests.cs b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/Messages/EthaMessageSerializerTests.cs new file mode 100644 index 00000000000..a73d47b2f82 --- /dev/null +++ b/src/Nethermind/Nethermind.Network.Test/P2P/Subprotocols/Etha/Messages/EthaMessageSerializerTests.cs @@ -0,0 +1,54 @@ +using FluentAssertions; +using Nethermind.Core; +using Nethermind.Core.Test.Builders; +using Nethermind.Network.P2P.Subprotocols.Etha.Messages; +using Nethermind.Serialization.Rlp; +using NUnit.Framework; + +namespace Nethermind.Network.Test.P2P.Subprotocols.Etha.Messages +{ + [TestFixture] + public class EthaMessageSerializerTests + { + [Test] + public void Can_serialize_and_deserialize_GetShardedBlocksMessage() + { + var serializer = new GetShardedBlocksMessageSerializer(); + var message = new GetShardedBlocksMessage(new[] { TestItem.KeccakA, TestItem.KeccakB }); + + RlpStream rlpStream = new RlpStream(); + serializer.Serialize(rlpStream, message); + + var deserialized = serializer.Deserialize(new RlpStream(rlpStream.Data)); + deserialized.BlockHashes.Should().BeEquivalentTo(message.BlockHashes); + } + + [Test] + public void Can_serialize_and_deserialize_ShardedBlocksMessage() + { + var serializer = new ShardedBlocksMessageSerializer(); + var message = new ShardedBlocksMessage(new[] { Build.A.Block.TestObject }); + + RlpStream rlpStream = new RlpStream(); + serializer.Serialize(rlpStream, message); + + var deserialized = serializer.Deserialize(new RlpStream(rlpStream.Data)); + deserialized.Blocks.Length.Should().Be(message.Blocks.Length); + deserialized.Blocks[0].Hash.Should().Be(message.Blocks[0].Hash); + } + + [Test] + public void Can_serialize_and_deserialize_NewShardedBlockMessage() + { + var serializer = new NewShardedBlockMessageSerializer(); + var block = Build.A.Block.TestObject; + var message = new NewShardedBlockMessage(block); + + RlpStream rlpStream = new RlpStream(); + serializer.Serialize(rlpStream, message); + + var deserialized = serializer.Deserialize(new RlpStream(rlpStream.Data)); + deserialized.Block.Hash.Should().Be(message.Block.Hash); + } + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaMessageCode.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaMessageCode.cs new file mode 100644 index 00000000000..2a6a9588435 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaMessageCode.cs @@ -0,0 +1,9 @@ +namespace Nethermind.Network.P2P.Subprotocols.Etha +{ + public static class EthaMessageCode + { + public const int GetShardedBlocks = 0x00; + public const int ShardedBlocks = 0x01; + public const int NewShardedBlock = 0x02; + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocol.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocol.cs new file mode 100644 index 00000000000..d83f2fbde4e --- /dev/null +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocol.cs @@ -0,0 +1,17 @@ +using Nethermind.Core.Crypto; +using Nethermind.Network.P2P; + +namespace Nethermind.Network.P2P.Subprotocols.Etha +{ + public class EthaProtocol : Protocol + { + public override string Name => "etha"; + public override byte Version => 1; + public override int MessageIdSpaceSize => 3; // Number of messages in protocol + + public EthaProtocol() : base(nameof(EthaProtocol)) + { + // Protocol initialization + } + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocolFactory.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocolFactory.cs new file mode 100644 index 00000000000..9477e1aba09 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocolFactory.cs @@ -0,0 +1,42 @@ +using Nethermind.Blockchain; +using Nethermind.Logging; +using Nethermind.Stats; + +namespace Nethermind.Network.P2P.Subprotocols.Etha +{ + public class EthaProtocolFactory : ProtocolFactoryBase + { + private readonly IBlockTree _blockTree; + private readonly IMessageSerializationService _serializationService; + private readonly INodeStatsManager _nodeStatsManager; + private readonly ILogManager _logManager; + + public EthaProtocolFactory( + IBlockTree blockTree, + IMessageSerializationService serializationService, + INodeStatsManager nodeStatsManager, + ILogManager logManager) + { + _blockTree = blockTree; + _serializationService = serializationService; + _nodeStatsManager = nodeStatsManager; + _logManager = logManager; + } + + public override Protocol Create(ISession session) + { + var protocol = new EthaProtocol(); + var protocolHandler = new EthaProtocolHandler( + _blockTree, + _serializationService, + _nodeStatsManager, + _logManager); + + protocol.InitializeProtocolHandler(protocolHandler); + + return protocol; + } + + public override string Name => "etha"; + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocolHandler.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocolHandler.cs new file mode 100644 index 00000000000..a9c79732320 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/EthaProtocolHandler.cs @@ -0,0 +1,82 @@ +using Nethermind.Blockchain; +using Nethermind.Core; +using Nethermind.Logging; +using Nethermind.Network.P2P.Subprotocols.Etha.Messages; +using Nethermind.Stats; +using System.Linq; + +namespace Nethermind.Network.P2P.Subprotocols.Etha +{ + public class EthaProtocolHandler : ProtocolHandlerBase + { + private readonly IBlockTree _blockTree; + private readonly ILogger _logger; + + public EthaProtocolHandler( + IBlockTree blockTree, + IMessageSerializationService serializer, + INodeStatsManager nodeStats, + ILogManager logManager) + : base(serializer, nodeStats, logManager) + { + _blockTree = blockTree; + _logger = logManager.GetClassLogger(); + + // Register message serializers + serializer.Register(new GetShardedBlocksMessageSerializer()); + serializer.Register(new ShardedBlocksMessageSerializer()); + serializer.Register(new NewShardedBlockMessageSerializer()); + } + + public override void HandleMessage(Packet packet) + { + switch(packet.PacketType) + { + case EthaMessageCode.GetShardedBlocks: + Handle(Deserialize(packet.Data)); + break; + case EthaMessageCode.ShardedBlocks: + Handle(Deserialize(packet.Data)); + break; + case EthaMessageCode.NewShardedBlock: + Handle(Deserialize(packet.Data)); + break; + default: + _logger.Error($"Unsupported packet type: {packet.PacketType}"); + break; + } + } + + private void Handle(GetShardedBlocksMessage message) + { + Block[] blocks = message.BlockHashes + .Select(hash => _blockTree.FindBlock(hash)) + .Where(block => block != null) + .ToArray(); + + Send(new ShardedBlocksMessage(blocks)); + } + + private void Handle(ShardedBlocksMessage message) + { + foreach (Block block in message.Blocks) + { + if (_blockTree.IsKnown(block.Hash)) + { + continue; + } + + // Process and suggest the new block to the block tree + _blockTree.SuggestBlock(block); + } + } + + private void Handle(NewShardedBlockMessage message) + { + if (!_blockTree.IsKnown(message.Block.Hash)) + { + _blockTree.SuggestBlock(message.Block); + } + } + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/EthaMessageSerializer.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/EthaMessageSerializer.cs new file mode 100644 index 00000000000..d71c9659abb --- /dev/null +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/EthaMessageSerializer.cs @@ -0,0 +1,82 @@ +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Serialization.Rlp; + +namespace Nethermind.Network.P2P.Subprotocols.Etha.Messages +{ + public class GetShardedBlocksMessageSerializer : IMessageSerializer + { + public void Serialize(RlpStream rlpStream, GetShardedBlocksMessage message) + { + rlpStream.StartSequence(message.BlockHashes.Length); + for (int i = 0; i < message.BlockHashes.Length; i++) + { + rlpStream.Write(message.BlockHashes[i]); + } + } + + public GetShardedBlocksMessage Deserialize(RlpStream rlpStream) + { + int hashesCount = rlpStream.ReadSequenceLength(); + var hashes = new Hash256[hashesCount]; + for (int i = 0; i < hashesCount; i++) + { + hashes[i] = rlpStream.ReadKeccak(); + } + + return new GetShardedBlocksMessage(hashes); + } + } + + public class ShardedBlocksMessageSerializer : IMessageSerializer + { + private readonly BlockDecoder _blockDecoder; + + public ShardedBlocksMessageSerializer() + { + _blockDecoder = new BlockDecoder(); + } + + public void Serialize(RlpStream rlpStream, ShardedBlocksMessage message) + { + rlpStream.StartSequence(message.Blocks.Length); + for (int i = 0; i < message.Blocks.Length; i++) + { + _blockDecoder.Encode(rlpStream, message.Blocks[i]); + } + } + + public ShardedBlocksMessage Deserialize(RlpStream rlpStream) + { + int blocksCount = rlpStream.ReadSequenceLength(); + var blocks = new Block[blocksCount]; + for (int i = 0; i < blocksCount; i++) + { + blocks[i] = _blockDecoder.Decode(rlpStream); + } + + return new ShardedBlocksMessage(blocks); + } + } + + public class NewShardedBlockMessageSerializer : IMessageSerializer + { + private readonly BlockDecoder _blockDecoder; + + public NewShardedBlockMessageSerializer() + { + _blockDecoder = new BlockDecoder(); + } + + public void Serialize(RlpStream rlpStream, NewShardedBlockMessage message) + { + _blockDecoder.Encode(rlpStream, message.Block); + } + + public NewShardedBlockMessage Deserialize(RlpStream rlpStream) + { + Block block = _blockDecoder.Decode(rlpStream); + return new NewShardedBlockMessage(block); + } + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/GetShardedBlocksMessage.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/GetShardedBlocksMessage.cs new file mode 100644 index 00000000000..1714cdc5e7c --- /dev/null +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/GetShardedBlocksMessage.cs @@ -0,0 +1,17 @@ +using Nethermind.Core.Crypto; +using Nethermind.Network.P2P; + +namespace Nethermind.Network.P2P.Subprotocols.Etha.Messages +{ + public class GetShardedBlocksMessage : P2PMessage + { + public override int PacketType => EthaMessageCode.GetShardedBlocks; + + public Hash256[] BlockHashes { get; set; } + + public GetShardedBlocksMessage(Hash256[] blockHashes) + { + BlockHashes = blockHashes; + } + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/NewShardedBlockMessage.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/NewShardedBlockMessage.cs new file mode 100644 index 00000000000..d141ffbd508 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/NewShardedBlockMessage.cs @@ -0,0 +1,17 @@ +using Nethermind.Core; +using Nethermind.Network.P2P; + +namespace Nethermind.Network.P2P.Subprotocols.Etha.Messages +{ + public class NewShardedBlockMessage : P2PMessage + { + public override int PacketType => EthaMessageCode.NewShardedBlock; + + public Block Block { get; set; } + + public NewShardedBlockMessage(Block block) + { + Block = block; + } + } +} diff --git a/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/ShardedBlocksMessage.cs b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/ShardedBlocksMessage.cs new file mode 100644 index 00000000000..85b90ce3b84 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/P2P/Subprotocols/Etha/Messages/ShardedBlocksMessage.cs @@ -0,0 +1,17 @@ +using Nethermind.Core; +using Nethermind.Network.P2P; + +namespace Nethermind.Network.P2P.Subprotocols.Etha.Messages +{ + public class ShardedBlocksMessage : P2PMessage + { + public override int PacketType => EthaMessageCode.ShardedBlocks; + + public Block[] Blocks { get; set; } + + public ShardedBlocksMessage(Block[] blocks) + { + Blocks = blocks; + } + } +} diff --git a/src/Nethermind/Nethermind.Network/Protocol.cs b/src/Nethermind/Nethermind.Network/Protocol.cs new file mode 100644 index 00000000000..0383149fe33 --- /dev/null +++ b/src/Nethermind/Nethermind.Network/Protocol.cs @@ -0,0 +1,9 @@ +public static class Protocol +{ + public const string P2P = "p2p"; + public const string Eth = "eth"; + public const string Les = "les"; + public const string Snap = "snap"; + public const string NodeData = "nd"; + public const string Etha = "etha"; +}