commit 27de79bda46d11d75b223dc9c929aa7b45e224f5 Author: Ivan Folgueira Bande Date: Thu Oct 16 13:38:59 2025 +0200 First commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3cceda5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..effe43c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/README.md b/README.md new file mode 100644 index 0000000..44e6985 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ + +Waku wrapper for Dart projects :) diff --git a/ffigen.yaml b/ffigen.yaml new file mode 100644 index 0000000..056964c --- /dev/null +++ b/ffigen.yaml @@ -0,0 +1,6 @@ +name: WakuBindings +description: Bindings to Waku C API +output: lib/src/waku_ffi.dart +headers: + entry-points: + - native/libwaku.h diff --git a/lib/bindings.dart b/lib/bindings.dart new file mode 100644 index 0000000..1a47cb0 --- /dev/null +++ b/lib/bindings.dart @@ -0,0 +1,51 @@ +import 'dart:ffi'; +import 'dart:io'; + +import 'package:ffi/ffi.dart'; + +class CallbackResult { + late int callerRet; + late String msg; +} + +/// Callback typedef +typedef WakuCallBackC = Void Function( + Int32 callerRet, Pointer msg, IntPtr len, Pointer userData); +typedef WakuCallBackDart = void Function( + int callerRet, Pointer msg, int len, Pointer userData); + +/// waku_new typedefs +typedef WakuNewC = Pointer Function(Pointer configJson, + Pointer> callback, Pointer userData); + +typedef WakuNewDart = Pointer Function(Pointer configJson, + Pointer> callback, Pointer userData); + +/// no params functions typedefs +typedef WakuNoParamsC = Pointer Function(Pointer ctx, + Pointer> callback, Pointer userData); + +typedef WakuNoParamsDart = Pointer Function(Pointer ctx, + Pointer> callback, Pointer userData); + +/// Load the shared library +final DynamicLibrary wakuLib = DynamicLibrary.open( + Platform.isMacOS + ? 'libwaku.dylib' + : Platform.isLinux + ? 'libwaku.so' + : 'waku.dll', +); + +/// Lookup functions +final WakuNewDart waku_new = + wakuLib.lookupFunction('waku_new'); + +final WakuNoParamsDart waku_destroy = + wakuLib.lookupFunction('waku_destroy'); + +final WakuNoParamsDart waku_start = + wakuLib.lookupFunction('waku_start'); + +final WakuNoParamsDart waku_stop = + wakuLib.lookupFunction('waku_stop'); diff --git a/lib/waku.dart b/lib/waku.dart new file mode 100644 index 0000000..be952b9 --- /dev/null +++ b/lib/waku.dart @@ -0,0 +1,102 @@ +import 'dart:async'; +import 'dart:ffi'; +import 'package:ffi/ffi.dart'; +import 'bindings.dart'; + +class Waku { + final Pointer _context; + + Waku._(this._context); + + static Future newInstance(String configJson) async { + late final NativeCallable callback; + + final completer = Completer(); + void onCallCompleted( + int callerRet, Pointer msg, int len, Pointer userData) { + print("instance is created callback"); + completer.complete(CallbackResult() + ..callerRet = callerRet + ..msg = msg.toDartString(length: len)); + callback.close(); + } + + callback = NativeCallable.listener(onCallCompleted); + + final configPtr = configJson.toNativeUtf8(); + final context = waku_new(configPtr, callback.nativeFunction, nullptr); + + final callResult = await completer.future; + + malloc.free(configPtr); + + final wakuCompleter = Completer(); + + if (callResult.callerRet != 0) { + print("Could not create instance"); + wakuCompleter + .completeError('Failed to create Waku instance: ${callResult.msg}'); + } else { + wakuCompleter.complete(Waku._(context)); + } + + return wakuCompleter.future; + } + + Future destroyInstance() async { + late final NativeCallable callback; + + final completer = Completer(); + void onCallCompleted( + int callerRet, Pointer msg, int len, Pointer userData) { + completer.complete(CallbackResult() + ..callerRet = callerRet + ..msg = msg.toDartString(length: len)); + callback.close(); + } + + callback = NativeCallable.listener(onCallCompleted); + + waku_destroy(_context, callback.nativeFunction, nullptr); + + return completer.future; + } + + Future start() async { + late final NativeCallable callback; + + final completer = Completer(); + void onCallCompleted( + int callerRet, Pointer msg, int len, Pointer userData) { + completer.complete(CallbackResult() + ..callerRet = callerRet + ..msg = msg.toDartString(length: len)); + callback.close(); + } + + callback = NativeCallable.listener(onCallCompleted); + + waku_start(_context, callback.nativeFunction, nullptr); + + return completer.future; + } + + Future stop() async { + late final NativeCallable callback; + + final completer = Completer(); + void onCallCompleted( + int callerRet, Pointer msg, int len, Pointer userData) { + completer.complete(CallbackResult() + ..callerRet = callerRet + ..msg = msg.toDartString(length: len)); + callback.close(); + } + + callback = NativeCallable.listener(onCallCompleted); + + waku_stop(_context, callback.nativeFunction, nullptr); + + return completer.future; + } +} diff --git a/native/libwaku.h b/native/libwaku.h new file mode 100644 index 0000000..525fec6 --- /dev/null +++ b/native/libwaku.h @@ -0,0 +1,251 @@ + +// Generated manually and inspired by the one generated by the Nim Compiler. +// In order to see the header file generated by Nim just run `make libwaku` +// from the root repo folder and the header should be created in +// nimcache/release/libwaku/libwaku.h +#ifndef __libwaku__ +#define __libwaku__ + +#include +#include + +// The possible returned values for the functions that return int +#define RET_OK 0 +#define RET_ERR 1 +#define RET_MISSING_CALLBACK 2 + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*WakuCallBack) (int callerRet, const char* msg, size_t len, void* userData); + +// Creates a new instance of the waku node. +// Sets up the waku node from the given configuration. +// Returns a pointer to the Context needed by the rest of the API functions. +void* waku_new( + const char* configJson, + WakuCallBack callback, + void* userData); + +int waku_start(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_stop(void* ctx, + WakuCallBack callback, + void* userData); + +// Destroys an instance of a waku node created with waku_new +int waku_destroy(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_version(void* ctx, + WakuCallBack callback, + void* userData); + +void waku_set_event_callback(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_content_topic(void* ctx, + const char* appName, + unsigned int appVersion, + const char* contentTopicName, + const char* encoding, + WakuCallBack callback, + void* userData); + +int waku_pubsub_topic(void* ctx, + const char* topicName, + WakuCallBack callback, + void* userData); + +int waku_default_pubsub_topic(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_relay_publish(void* ctx, + const char* pubSubTopic, + const char* jsonWakuMessage, + unsigned int timeoutMs, + WakuCallBack callback, + void* userData); + +int waku_lightpush_publish(void* ctx, + const char* pubSubTopic, + const char* jsonWakuMessage, + WakuCallBack callback, + void* userData); + +int waku_relay_subscribe(void* ctx, + const char* pubSubTopic, + WakuCallBack callback, + void* userData); + +int waku_relay_add_protected_shard(void* ctx, + int clusterId, + int shardId, + char* publicKey, + WakuCallBack callback, + void* userData); + +int waku_relay_unsubscribe(void* ctx, + const char* pubSubTopic, + WakuCallBack callback, + void* userData); + +int waku_filter_subscribe(void* ctx, + const char* pubSubTopic, + const char* contentTopics, + WakuCallBack callback, + void* userData); + +int waku_filter_unsubscribe(void* ctx, + const char* pubSubTopic, + const char* contentTopics, + WakuCallBack callback, + void* userData); + +int waku_filter_unsubscribe_all(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_relay_get_num_connected_peers(void* ctx, + const char* pubSubTopic, + WakuCallBack callback, + void* userData); + +int waku_relay_get_connected_peers(void* ctx, + const char* pubSubTopic, + WakuCallBack callback, + void* userData); + +int waku_relay_get_num_peers_in_mesh(void* ctx, + const char* pubSubTopic, + WakuCallBack callback, + void* userData); + +int waku_relay_get_peers_in_mesh(void* ctx, + const char* pubSubTopic, + WakuCallBack callback, + void* userData); + +int waku_store_query(void* ctx, + const char* jsonQuery, + const char* peerAddr, + int timeoutMs, + WakuCallBack callback, + void* userData); + +int waku_connect(void* ctx, + const char* peerMultiAddr, + unsigned int timeoutMs, + WakuCallBack callback, + void* userData); + +int waku_disconnect_peer_by_id(void* ctx, + const char* peerId, + WakuCallBack callback, + void* userData); + +int waku_disconnect_all_peers(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_dial_peer(void* ctx, + const char* peerMultiAddr, + const char* protocol, + int timeoutMs, + WakuCallBack callback, + void* userData); + +int waku_dial_peer_by_id(void* ctx, + const char* peerId, + const char* protocol, + int timeoutMs, + WakuCallBack callback, + void* userData); + +int waku_get_peerids_from_peerstore(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_get_connected_peers_info(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_get_peerids_by_protocol(void* ctx, + const char* protocol, + WakuCallBack callback, + void* userData); + +int waku_listen_addresses(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_get_connected_peers(void* ctx, + WakuCallBack callback, + void* userData); + +// Returns a list of multiaddress given a url to a DNS discoverable ENR tree +// Parameters +// char* entTreeUrl: URL containing a discoverable ENR tree +// char* nameDnsServer: The nameserver to resolve the ENR tree url. +// int timeoutMs: Timeout value in milliseconds to execute the call. +int waku_dns_discovery(void* ctx, + const char* entTreeUrl, + const char* nameDnsServer, + int timeoutMs, + WakuCallBack callback, + void* userData); + +// Updates the bootnode list used for discovering new peers via DiscoveryV5 +// bootnodes - JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]` +int waku_discv5_update_bootnodes(void* ctx, + char* bootnodes, + WakuCallBack callback, + void* userData); + +int waku_start_discv5(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_stop_discv5(void* ctx, + WakuCallBack callback, + void* userData); + +// Retrieves the ENR information +int waku_get_my_enr(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_get_my_peerid(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_get_metrics(void* ctx, + WakuCallBack callback, + void* userData); + +int waku_peer_exchange_request(void* ctx, + int numPeers, + WakuCallBack callback, + void* userData); + +int waku_ping_peer(void* ctx, + const char* peerAddr, + int timeoutMs, + WakuCallBack callback, + void* userData); + +int waku_is_online(void* ctx, + WakuCallBack callback, + void* userData); + +#ifdef __cplusplus +} +#endif + +#endif /* __libwaku__ */ diff --git a/native/libwaku.so b/native/libwaku.so new file mode 100755 index 0000000..27c751e Binary files /dev/null and b/native/libwaku.so differ diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..ea46be1 --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,14 @@ +name: waku_dart_bindings +description: Dart bindings for Waku C API +version: 1.0.0 + +environment: + sdk: ">3.4.0 <4.0.0" + +dependencies: + ffi: ^2.1.0 + +dev_dependencies: + ffigen: ^19.1.0 + lints: ^6.0.0 + test: ^1.26.0 diff --git a/test/waku_test.dart b/test/waku_test.dart new file mode 100644 index 0000000..6ed2106 --- /dev/null +++ b/test/waku_test.dart @@ -0,0 +1,27 @@ +import 'package:test/test.dart'; +import 'package:waku_dart_bindings/waku.dart'; // Your high-level wrapper + +void main() { + group('Waku FFI Tests', () { + Waku? wakuInstance; + + setUp(() async { + // Create a new Waku instance before each test + wakuInstance = await Waku.newInstance( + '{ "clusterId": 16, "shards": [ 1, 32, 64, 128, 256 ], "numShardsInNetwork": 257 }'); + }); + + tearDown(() { + // Optionally: clean up resources if your API exposes a free function + // e.g., waku_free(wakuInstance?.context); + wakuInstance?.destroyInstance(); + }); + + test('Start and stop waku instance', () async { + final startResult = await wakuInstance!.start(); + expect(startResult.callerRet, 0); + final stopResult = await wakuInstance!.stop(); + expect(stopResult.callerRet, 0); + }); + }); +}