mirror of
https://github.com/status-im/go-waku.git
synced 2025-01-17 01:00:54 +00:00
543 lines
23 KiB
C#
543 lines
23 KiB
C#
using System.Runtime.InteropServices;
|
|
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace Waku
|
|
{
|
|
public class Config
|
|
{
|
|
public string? host { get; set; }
|
|
public int? port { get; set; }
|
|
public string? advertiseAddr { get; set; }
|
|
public string? nodeKey { get; set; }
|
|
public int? keepAliveInterval { get; set; }
|
|
public bool? relay { get; set; }
|
|
public int? minPeersToPublish {get; set; }
|
|
}
|
|
|
|
public enum EventType
|
|
{
|
|
Unknown, Message
|
|
}
|
|
|
|
public class Message
|
|
{
|
|
public byte[] payload { get; set; } = new byte[0];
|
|
public string contentTopic { get; set; } = "";
|
|
public int version { get; set; } = 0;
|
|
public long? timestamp { get; set; }
|
|
}
|
|
|
|
public class DecodedPayload
|
|
{
|
|
public string? pubkey { get; set; }
|
|
public string? signature { get; set; }
|
|
public byte[] data { get; set; } = new byte[0];
|
|
public byte[] padding { get; set; } = new byte[0];
|
|
}
|
|
|
|
public class Peer
|
|
{
|
|
public string peerID { get; set; } = "";
|
|
public bool connected { get; set; } = false;
|
|
public IList<string> protocols { get; set; } = new List<string>();
|
|
public IList<string> addrs { get; set; } = new List<string>();
|
|
}
|
|
|
|
public class Event
|
|
{
|
|
public EventType type { get; set; } = EventType.Unknown;
|
|
}
|
|
|
|
public class MessageEventData
|
|
{
|
|
public string messageID { get; set; } = "";
|
|
|
|
public string pubsubTopic { get; set; } = Utils.DefaultPubsubTopic();
|
|
|
|
public Message wakuMessage { get; set; } = new Message();
|
|
}
|
|
|
|
public class MessageEvent : Event
|
|
{
|
|
[JsonPropertyName("event")]
|
|
public MessageEventData data { get; set; } = new();
|
|
}
|
|
|
|
public class ContentFilter
|
|
{
|
|
public ContentFilter(string contentTopic)
|
|
{
|
|
this.contentTopic = contentTopic;
|
|
}
|
|
public string contentTopic { get; set; } = "";
|
|
}
|
|
|
|
public class Cursor
|
|
{
|
|
public byte[] digest { get; set; } = new byte[0];
|
|
public string pubsubTopic { get; set; } = Utils.DefaultPubsubTopic();
|
|
public long receiverTime { get; set; } = 0;
|
|
public long senderTime { get; set; } = 0;
|
|
}
|
|
|
|
public class PagingOptions
|
|
{
|
|
public PagingOptions(int pageSize, Cursor? cursor, bool forward)
|
|
{
|
|
this.pageSize = pageSize;
|
|
this.cursor = cursor;
|
|
this.forward = forward;
|
|
}
|
|
public int pageSize { get; set; } = 100;
|
|
public Cursor? cursor { get; set; } = new();
|
|
public bool forward { get; set; } = true;
|
|
}
|
|
|
|
public class StoreQuery
|
|
{
|
|
public string? pubsubTopic { get; set; } = Utils.DefaultPubsubTopic();
|
|
public long? startTime { get; set; }
|
|
public long? endTime { get; set; }
|
|
public IList<ContentFilter>? contentFilters { get; set; }
|
|
public PagingOptions? pagingOptions { get; set; }
|
|
}
|
|
|
|
public class StoreResponse
|
|
{
|
|
public IList<Message> messages { get; set; } = new List<Message>();
|
|
public PagingOptions? pagingInfo { get; set; }
|
|
}
|
|
|
|
public class Node
|
|
{
|
|
private bool _running;
|
|
private static SignalHandlerDelegate? _signalHandler;
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_new(string configJSON);
|
|
|
|
/// <summary>
|
|
/// Initialize a go-waku node.
|
|
/// </summary>
|
|
/// <param name="c">Waku.Config containing the options used to initialize a go-waku node. It can be NULL to use defaults. All the keys from the configuration are optional </param>
|
|
/// <returns>The node id</returns>
|
|
public Node(Config? c = null)
|
|
{
|
|
string jsonString = "{}";
|
|
if (c != null)
|
|
{
|
|
jsonString = JsonSerializer.Serialize(c);
|
|
}
|
|
|
|
IntPtr ptr = waku_new(jsonString);
|
|
|
|
Response.HandleResponse(ptr);
|
|
|
|
SetEventCallback();
|
|
}
|
|
|
|
~Node()
|
|
{
|
|
if (_running)
|
|
{
|
|
Stop();
|
|
}
|
|
}
|
|
|
|
private static void DefaultEventHandler(IntPtr signalJSON)
|
|
{
|
|
if (_signalHandler != null)
|
|
{
|
|
string signalStr = Response.PtrToStringUtf8(signalJSON, false);
|
|
JsonSerializerOptions options = new JsonSerializerOptions();
|
|
options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
|
|
|
|
Event? evt = JsonSerializer.Deserialize<Event>(signalStr, options);
|
|
if (evt == null) return;
|
|
|
|
switch (evt.type)
|
|
{
|
|
case EventType.Message:
|
|
Event? msgEvt = JsonSerializer.Deserialize<MessageEvent>(signalStr, options);
|
|
if (msgEvt != null) _signalHandler(msgEvt);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
|
|
internal delegate void WakuEventHandlerDelegate(IntPtr signalJSON);
|
|
|
|
[DllImport(Constants.dllName, CallingConvention = CallingConvention.Cdecl)]
|
|
internal static extern void waku_set_event_callback(WakuEventHandlerDelegate cb);
|
|
|
|
private void SetEventCallback()
|
|
{
|
|
waku_set_event_callback(DefaultEventHandler);
|
|
}
|
|
|
|
public delegate void SignalHandlerDelegate(Event evt);
|
|
|
|
/// <summary>
|
|
/// Register callback to act as signal handler and receive application signals which are used to react to asyncronous events in waku.
|
|
/// <param name="h">SignalHandler with the signature `void SignalHandler(Waku.Event evt){ }`</param>
|
|
public void SetEventHandler(SignalHandlerDelegate h)
|
|
{
|
|
_signalHandler = h;
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_start();
|
|
|
|
/// <summary>
|
|
/// Initialize a go-waku node mounting all the protocols that were enabled during the waku node instantiation.
|
|
/// </summary>
|
|
public void Start()
|
|
{
|
|
if(_running) {
|
|
return
|
|
}
|
|
|
|
IntPtr ptr = waku_start();
|
|
Response.HandleResponse(ptr);
|
|
|
|
_running = true;
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_stop();
|
|
|
|
/// <summary>
|
|
/// Stops a go-waku node
|
|
/// </summary>
|
|
public void Stop()
|
|
{
|
|
if(!_running) {
|
|
return
|
|
}
|
|
|
|
IntPtr ptr = waku_stop();
|
|
Response.HandleResponse(ptr);
|
|
|
|
_running = false;
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_peerid();
|
|
|
|
/// <summary>
|
|
/// Obtain the peer ID of the go-waku node.
|
|
/// </summary>
|
|
/// <returns>The base58 encoded peer Id</returns>
|
|
public string PeerId()
|
|
{
|
|
IntPtr ptr = waku_peerid();
|
|
return Response.HandleResponse(ptr, "could not obtain the peerId");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_peer_cnt();
|
|
|
|
/// <summary>
|
|
/// Obtain number of connected peers
|
|
/// </summary>
|
|
/// <returns>The number of peers connected to this node</returns>
|
|
public int PeerCnt()
|
|
{
|
|
IntPtr ptr = waku_start();
|
|
return Response.HandleResponse<int>(ptr, "could not obtain the peer count");
|
|
}
|
|
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_listen_addresses();
|
|
|
|
/// <summary>
|
|
/// Obtain the multiaddresses the wakunode is listening to
|
|
/// </summary>
|
|
/// <returns>List of multiaddresses</returns>
|
|
public IList<string> ListenAddresses()
|
|
{
|
|
IntPtr ptr = waku_listen_addresses();
|
|
return Response.HandleListResponse<string>(ptr, "could not obtain the addresses");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_add_peer(string address, string protocolId);
|
|
|
|
/// <summary>
|
|
/// Add node multiaddress and protocol to the wakunode peerstore
|
|
/// </summary>
|
|
/// <param name="address">multiaddress of the peer being added</param>
|
|
/// <param name="protocolId">protocol supported by the peer</param>
|
|
/// <returns>Base58 encoded peer Id</returns>
|
|
public string AddPeer(string address, string protocolId)
|
|
{
|
|
IntPtr ptr = waku_add_peer(address, protocolId);
|
|
return Response.HandleResponse(ptr, "could not obtain the peer id");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_connect(string address, int ms);
|
|
|
|
/// <summary>
|
|
/// Connect to peer at multiaddress.
|
|
/// </summary>
|
|
/// <param name="address">multiaddress of the peer being dialed</param>
|
|
/// <param name="ms">max duration in milliseconds this function might take to execute. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use 0 for unlimited duration</param>
|
|
public void Connect(string address, int ms = 0)
|
|
{
|
|
IntPtr ptr = waku_connect(address, ms);
|
|
Response.HandleResponse(ptr);
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_connect_peerid( string peerId, int ms);
|
|
|
|
/// <summary>
|
|
/// Connect to peer using peerID.
|
|
/// </summary>
|
|
/// <param name="peerId"> peerID to dial. The peer must be already known, added with `AddPeer`</param>
|
|
/// <param name="ms">max duration in milliseconds this function might take to execute. If the function execution takes longer than this value, the execution will be canceled and an error returned. Use 0 for unlimited duration</param>
|
|
public void ConnectPeerId(string peerId, int ms = 0)
|
|
{
|
|
IntPtr ptr = waku_connect_peerid(peerId, ms);
|
|
Response.HandleResponse(ptr);
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_disconnect(string peerID);
|
|
|
|
/// <summary>
|
|
/// Close connection to a known peer by peerID
|
|
/// </summary>
|
|
/// <param name="peerId">Base58 encoded peer ID to disconnect</param>
|
|
public void Disconnect(string peerId)
|
|
{
|
|
IntPtr ptr = waku_disconnect(peerId);
|
|
Response.HandleResponse(ptr);
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_relay_publish(string messageJSON, string? topic, int ms);
|
|
|
|
/// <summary>
|
|
/// Publish a message using waku relay.
|
|
/// </summary>
|
|
/// <param name="msg">Message to broadcast</param>
|
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
|
/// <returns></returns>
|
|
public string RelayPublish(Message msg, string? topic = null, int ms = 0)
|
|
{
|
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
|
IntPtr ptr = waku_relay_publish(jsonMsg, topic, ms);
|
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_lightpush_publish(string messageJSON, string? topic, string? peerID, int ms);
|
|
|
|
/// <summary>
|
|
/// Publish a message using waku lightpush.
|
|
/// </summary>
|
|
/// <param name="msg">Message to broadcast</param>
|
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
|
/// <param name="peerID">ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node</param>
|
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
|
/// <returns></returns>
|
|
public string LightpushPublish(Message msg, string? topic = null, string? peerID = null, int ms = 0)
|
|
{
|
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
|
IntPtr ptr = waku_lightpush_publish(jsonMsg, topic, peerID, ms);
|
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_relay_publish_enc_asymmetric(string messageJSON, string? topic, string publicKey, string? optionalSigningKey, int ms);
|
|
|
|
/// <summary>
|
|
/// Publish a message encrypted with an secp256k1 public key using waku relay.
|
|
/// </summary>
|
|
/// <param name="msg">Message to broadcast</param>
|
|
/// <param name="publicKey">Secp256k1 public key</param>
|
|
/// <param name="optionalSigningKey">Optional secp256k1 private key for signing the message</param>
|
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
|
/// <returns></returns>
|
|
public string RelayPublishEncodeAsymmetric(Message msg, string publicKey, string? optionalSigningKey = null, string? topic = null, int ms = 0)
|
|
{
|
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
|
IntPtr ptr = waku_relay_publish_enc_asymmetric(jsonMsg, topic, publicKey, optionalSigningKey, ms);
|
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_lightpush_publish_enc_asymmetric(string messageJSON, string? topic, string? peerID, string publicKey, string? optionalSigningKey, int ms);
|
|
|
|
/// <summary>
|
|
/// Publish a message encrypted with an secp256k1 public key using waku lightpush.
|
|
/// </summary>
|
|
/// <param name="msg">Message to broadcast</param>
|
|
/// <param name="publicKey">Secp256k1 public key</param>
|
|
/// <param name="optionalSigningKey">Optional secp256k1 private key for signing the message</param>
|
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
|
/// <param name="peerID">ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node</param>
|
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
|
/// <returns></returns>
|
|
public string LightpushPublishEncodeAsymmetric(Message msg, string publicKey, string? optionalSigningKey = null, string? topic = null, string? peerID = null, int ms = 0)
|
|
{
|
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
|
IntPtr ptr = waku_lightpush_publish_enc_asymmetric(jsonMsg, topic, peerID, publicKey, optionalSigningKey, ms);
|
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_relay_publish_enc_symmetric(string messageJSON, string? topic, string symmetricKey, string? optionalSigningKey, int ms);
|
|
|
|
/// <summary>
|
|
/// Publish a message encrypted with a 32 bytes symmetric key using waku relay.
|
|
/// </summary>
|
|
/// <param name="msg">Message to broadcast</param>
|
|
/// <param name="symmetricKey">32 byte hex string containing a symmetric key</param>
|
|
/// <param name="optionalSigningKey">Optional secp256k1 private key for signing the message</param>
|
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
|
/// <returns></returns>
|
|
public string RelayPublishEncodeSymmetric(Message msg, string symmetricKey, string? optionalSigningKey = null, string? topic = null, int ms = 0)
|
|
{
|
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
|
IntPtr ptr = waku_relay_publish_enc_symmetric(jsonMsg, topic, symmetricKey, optionalSigningKey, ms);
|
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_lightpush_publish_enc_symmetric(string messageJSON, string? topic, string? peerID, string symmetricKey, string? optionalSigningKey, int ms);
|
|
|
|
/// <summary>
|
|
/// Publish a message encrypted with a 32 bytes symmetric key using waku lightpush.
|
|
/// </summary>
|
|
/// <param name="msg">Message to broadcast</param>
|
|
/// <param name="symmetricKey">32 byte hex string containing a symmetric key</param>
|
|
/// <param name="optionalSigningKey">Optional secp256k1 private key for signing the message</param>
|
|
/// <param name="topic">Pubsub topic. Set to `null` to use the default pubsub topic</param>
|
|
/// <param name="peerID">ID of a peer supporting the lightpush protocol. Use NULL to automatically select a node</param>
|
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
|
/// <returns></returns>
|
|
public string RelayPublishEncodeSymmetric(Message msg, string symmetricKey, string? optionalSigningKey = null, string? topic = null, string? peerID = null, int ms = 0)
|
|
{
|
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
|
IntPtr ptr = waku_lightpush_publish_enc_symmetric(jsonMsg, topic, peerID, symmetricKey, optionalSigningKey, ms);
|
|
return Response.HandleResponse(ptr, "could not obtain the message id");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_decode_symmetric(string messageJSON, string symmetricKey);
|
|
|
|
/// <summary>
|
|
/// Decode a waku message using a symmetric key
|
|
/// </summary>
|
|
/// <param name="msg">Message to decode</param>
|
|
/// <param name="symmetricKey">Symmetric key used to decode the message</param>
|
|
/// <returns>DecodedPayload containing the decrypted message, padding, public key and signature (if available)</returns>
|
|
public DecodedPayload DecodeSymmetric(Message msg, string symmetricKey)
|
|
{
|
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
|
IntPtr ptr = waku_decode_symmetric(jsonMsg, symmetricKey);
|
|
return Response.HandleDecodedPayloadResponse(ptr, "could not decode message");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_decode_asymmetric(string messageJSON, string privateKey);
|
|
|
|
/// <summary>
|
|
/// Decode a waku message using an asymmetric key
|
|
/// </summary>
|
|
/// <param name="msg">Message to decode</param>
|
|
/// <param name="privateKey">Secp256k1 private key used to decode the message</param>
|
|
/// <returns>DecodedPayload containing the decrypted message, padding, public key and signature (if available)</returns>
|
|
public DecodedPayload DecodeAsymmetric(Message msg, string privateKey)
|
|
{
|
|
string jsonMsg = JsonSerializer.Serialize(msg);
|
|
IntPtr ptr = waku_decode_asymmetric(jsonMsg, privateKey);
|
|
return Response.HandleDecodedPayloadResponse(ptr, "could not decode message");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_relay_enough_peers(string topic);
|
|
|
|
/// <summary>
|
|
/// Determine if there are enough peers to publish a message on a topic.
|
|
/// </summary>
|
|
/// <param name="topic">pubsub topic to verify. Use NULL to verify the number of peers in the default pubsub topic</param>
|
|
/// <returns>boolean indicating if there are enough peers or not</returns>
|
|
public bool RelayEnoughPeers(string topic = null)
|
|
{
|
|
IntPtr ptr = waku_relay_enough_peers(topic);
|
|
return Response.HandleResponse<bool>(ptr, "could not determine if there are enough peers");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_relay_subscribe(string? topic);
|
|
|
|
/// <summary>
|
|
/// Subscribe to a WakuRelay topic to receive messages.
|
|
/// </summary>
|
|
/// <param name="topic">Pubsub topic to subscribe to. Use NULL for subscribing to the default pubsub topic</param>
|
|
/// <returns>Subscription Id</returns>
|
|
public void RelaySubscribe(string? topic = null)
|
|
{
|
|
IntPtr ptr = waku_relay_subscribe(topic);
|
|
Response.HandleResponse(ptr);
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_relay_unsubscribe(string? topic);
|
|
|
|
/// <summary>
|
|
/// Closes the pubsub subscription to a pubsub topic.
|
|
/// </summary>
|
|
/// <param name="topic">Pubsub topic to unsubscribe. Use NULL for unsubscribe from the default pubsub topic</param>
|
|
public void RelayUnsubscribe(string? topic = null)
|
|
{
|
|
IntPtr ptr = waku_relay_unsubscribe(topic);
|
|
Response.HandleResponse(ptr);
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_peers();
|
|
|
|
/// <summary>
|
|
/// Get Peers
|
|
/// </summary>
|
|
/// <returns>Retrieve list of peers and their supported protocols</returns>
|
|
public IList<Peer> Peers()
|
|
{
|
|
IntPtr ptr = waku_peers();
|
|
return Response.HandleListResponse<Peer>(ptr, "could not obtain the peers");
|
|
}
|
|
|
|
[DllImport(Constants.dllName)]
|
|
internal static extern IntPtr waku_store_query(string queryJSON, string? peerID, int ms);
|
|
/// <summary>
|
|
/// Query message history
|
|
/// </summary>
|
|
/// <param name="query">Query</param>
|
|
/// <param name="peerID">PeerID to ask the history from. Use NULL to automatically select a peer</param>
|
|
/// <param name="ms">If ms is greater than 0, the broadcast of the message must happen before the timeout (in milliseconds) is reached, or an error will be returned</param>
|
|
/// <returns>Response containing the messages and cursor for pagination. Use the cursor in further queries to retrieve more results</returns>
|
|
public StoreResponse StoreQuery(StoreQuery query, string? peerID = null, int ms = 0)
|
|
{
|
|
string queryJSON = JsonSerializer.Serialize(query);
|
|
IntPtr ptr = waku_store_query(queryJSON, peerID, ms);
|
|
|
|
return Response.HandleStoreResponse(ptr, "could not extract query response");
|
|
}
|
|
|
|
|
|
|
|
}
|
|
}
|
|
|