Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WSS: Add Web Socket Server #795

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions neo-modules.sln
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SQLiteWallet", "src\SQLiteW
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StorageDumper", "src\StorageDumper\StorageDumper.csproj", "{938D86EA-0F48-436B-9255-4AD9A8E6B9AC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebSocketServer", "src\WebSocketServer\WebSocketServer.csproj", "{776B6533-D073-4541-8288-3736B8F9DD9D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -121,6 +123,10 @@ Global
{938D86EA-0F48-436B-9255-4AD9A8E6B9AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{938D86EA-0F48-436B-9255-4AD9A8E6B9AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{938D86EA-0F48-436B-9255-4AD9A8E6B9AC}.Release|Any CPU.Build.0 = Release|Any CPU
{776B6533-D073-4541-8288-3736B8F9DD9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{776B6533-D073-4541-8288-3736B8F9DD9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{776B6533-D073-4541-8288-3736B8F9DD9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{776B6533-D073-4541-8288-3736B8F9DD9D}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -144,6 +150,7 @@ Global
{8D2EE375-2E2D-45FE-A4E9-0254D12C7554} = {59D802AB-C552-422A-B9C3-64D329FBCDCC}
{D121D57A-512E-4F74-ADA1-24482BF5C42B} = {97E81C78-1637-481F-9485-DA1225E94C23}
{938D86EA-0F48-436B-9255-4AD9A8E6B9AC} = {97E81C78-1637-481F-9485-DA1225E94C23}
{776B6533-D073-4541-8288-3736B8F9DD9D} = {97E81C78-1637-481F-9485-DA1225E94C23}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {61D3ADE6-BBFC-402D-AB42-1C71C9F9EDE3}
Expand Down
16 changes: 16 additions & 0 deletions src/WebSocketServer/Events/BlockEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

namespace Neo.Plugins.WebSocketServer.Events;

public class BlockEvent : WebSocketEvent
{
public int Height { get; set; }
}
120 changes: 120 additions & 0 deletions src/WebSocketServer/Events/Events.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using System;
using System.Linq;
using Neo.Json;
using Neo.Plugins.WebSocketServer.Filters;
using Neo.Plugins.WebSocketServer.Subscriptions;
namespace Neo.Plugins.WebSocketServer.Events;

// Enum representing WebSocket server event IDs
public enum WssEventId : byte
{
InvalidEventId = 0,
BlockEventId,
TransactionEventId,
NotificationEventId,
ExecutionEventId,
MissedEventId = 255
}

public static class EventExtensions
{
// Convert event ID to its string representation
public static string ToMethod(this WssEventId e)
{
return e switch
{
WssEventId.BlockEventId => "block_added",
WssEventId.TransactionEventId => "transaction_added",
WssEventId.NotificationEventId => "notification_from_execution",
WssEventId.ExecutionEventId => "transaction_executed",
WssEventId.MissedEventId => "event_missed",
_ => "unknown"
};
}

// Convert string to its corresponding event ID
public static WssEventId FromMethod(this string s)
{
return s switch
{
"block_added" => WssEventId.BlockEventId,
"transaction_added" => WssEventId.TransactionEventId,
"notification_from_execution" => WssEventId.NotificationEventId,
"transaction_executed" => WssEventId.ExecutionEventId,
"event_missed" => WssEventId.MissedEventId,
_ => throw new ArgumentException($"Invalid event ID string: {s}")
};
}

// Check if a subscription matches a WebSocket event
public static bool Matches(this Subscription subscription, JObject wssEvent, WssEventId eventId)
{
if (subscription.WssEvent != eventId) return false;

switch (subscription.WssEvent)
{
case WssEventId.BlockEventId:
return HandleBlockEvent(subscription, wssEvent);
case WssEventId.TransactionEventId:
return HandleTransactionEvent(subscription, wssEvent);
case WssEventId.NotificationEventId:
return HandleNotificationEvent(subscription, wssEvent);
case WssEventId.ExecutionEventId:
return HandleExecutionEvent(subscription, wssEvent);
default:
return false;
}
}

private static bool HandleBlockEvent(Subscription subscription, JObject wssEvent)
{
var blockFilter = (BlockFilter)subscription.Filter;
var blockIndex = wssEvent["index"]!.GetInt32();
var primaryIndex = wssEvent["primary"]!.GetInt32();

return (blockFilter.Primary == null || blockFilter.Primary == primaryIndex) &&
(blockFilter.Since == null || blockFilter.Since <= blockIndex) &&
(blockFilter.Till == null || blockIndex <= blockFilter.Till);
}

private static bool HandleTransactionEvent(Subscription subscription, JObject wssEvent)
{
var transactionFilter = (TransactionFilter)subscription.Filter;
var txSender = UInt160.Parse(wssEvent["sender"]!.GetString());

if (transactionFilter.Signer == null) return txSender.Equals(transactionFilter.Sender);

var txSigners = (JArray)wssEvent["signers"];
return txSigners.Any(signer => UInt160.Parse(signer["account"]!.GetString()).Equals(transactionFilter.Signer));
}

private static bool HandleNotificationEvent(Subscription subscription, JObject wssEvent)
{
var notificationFilter = (NotificationFilter)subscription.Filter;
var notificationContract = UInt160.Parse(wssEvent["contract"]!.GetString());
var notificationName = wssEvent["eventname"]!.GetString();

return (notificationFilter.Contract == null || notificationContract.Equals(notificationFilter.Contract)) &&
(notificationFilter.Name == null || notificationName.Equals(notificationFilter.Name));
}

private static bool HandleExecutionEvent(Subscription subscription, JObject wssEvent)
{
var executionFilter = (ExecutionFilter)subscription.Filter;
var execResultVmState = wssEvent["executions"]!["vmstate"]!.GetString();
var execResultContainer = UInt256.Parse(wssEvent["txid"]!.GetString());

return (executionFilter.State == null || execResultVmState == executionFilter.State) &&
(executionFilter.Container == null || execResultContainer.Equals(executionFilter.Container));
}
}
17 changes: 17 additions & 0 deletions src/WebSocketServer/Events/ExecutionEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

namespace Neo.Plugins.WebSocketServer.Events;

public class ExecutionEvent : WebSocketEvent
{
public string VmState { get; set; }
public UInt256 Container { get; set; }
}
17 changes: 17 additions & 0 deletions src/WebSocketServer/Events/NotificationEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

namespace Neo.Plugins.WebSocketServer.Events;

public class NotificationEvent : WebSocketEvent
{
public UInt160 Contract { get; set; }
public string Name { get; set; }
}
16 changes: 16 additions & 0 deletions src/WebSocketServer/Events/TransactionEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

namespace Neo.Plugins.WebSocketServer.Events;

public class TransactionEvent : WebSocketEvent
{
public UInt256 Container { get; set; }
}
18 changes: 18 additions & 0 deletions src/WebSocketServer/Events/WebSocketEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Json;
namespace Neo.Plugins.WebSocketServer.Events;

public abstract class WebSocketEvent
{
public WssEventId WssEvent { get; set; }
public JObject Data { get; set; }
}
27 changes: 27 additions & 0 deletions src/WebSocketServer/Filters/BlockFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Json;
namespace Neo.Plugins.WebSocketServer.Filters;

public class BlockFilter : Filter
{
public int? Primary { get; set; }
public uint? Since { get; set; }
public uint? Till { get; set; }

public override Filter FromJson(JObject json)
{
Primary = json["primary"]?.GetInt32();
Since = (uint?)json["since"]?.GetInt32();
Till = (uint?)json["till"]?.GetInt32();
return this;
}
}
25 changes: 25 additions & 0 deletions src/WebSocketServer/Filters/ExecutionFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Json;
namespace Neo.Plugins.WebSocketServer.Filters;

public class ExecutionFilter : Filter
{
public string? State { get; set; }
public UInt256? Container { get; set; }

public override Filter FromJson(JObject json)
{
State = json["state"]?.GetString();
Container = UInt256.Parse(json["container"]?.GetString());
return this;
}
}
26 changes: 26 additions & 0 deletions src/WebSocketServer/Filters/Filter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

#nullable enable
using Neo.Json;
namespace Neo.Plugins.WebSocketServer.Filters;

public abstract class Filter
{
public abstract Filter FromJson(JObject json);
}

// public record BlockFilter(int? Primary, uint? Since, uint? Till);
//
// public record TransactionFilter(UInt160? Sender, UInt160? Signer);
//
// public record NotificationFilter(UInt160? Contract, string? Name);
//
// public record ExecutionFilter(string? State, UInt256? Container);
25 changes: 25 additions & 0 deletions src/WebSocketServer/Filters/NotificationFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Json;
namespace Neo.Plugins.WebSocketServer.Filters;

public class NotificationFilter : Filter
{
public UInt160? Contract { get; set; }
public string? Name { get; set; }

public override Filter FromJson(JObject json)
{
Contract = UInt160.Parse(json["contract"]?.GetString());
Name = json["name"]?.GetString();
return this;
}
}
25 changes: 25 additions & 0 deletions src/WebSocketServer/Filters/TransactionFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Json;
namespace Neo.Plugins.WebSocketServer.Filters;

public class TransactionFilter : Filter
{
public UInt160? Sender { get; set; }
public UInt160? Signer { get; set; }

public override Filter FromJson(JObject json)
{
Sender = UInt160.Parse(json["sender"]?.GetString());
Signer = UInt160.Parse(json["signer"]?.GetString());
return this;
}
}
17 changes: 17 additions & 0 deletions src/WebSocketServer/Handler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (C) 2015-2023 The Neo Project.
//
// The Neo.Network.RPC is free software distributed under the MIT software license,
// see the accompanying file LICENSE in the main directory of the
// project or http://www.opensource.org/licenses/mit-license.php
// for more details.
//
// Redistribution and use in source and binary forms with or without
// modifications are permitted.

using Neo.Plugins.WebSocketServer.Events;
namespace Neo.Plugins.WebSocketServer;

public static class Handler
{
public delegate void WebSocketEventHandler(WebSocketEvent @event);
}
Loading