Skip to content

Commit

Permalink
feat(c-bindings): support creating multiple instances
Browse files Browse the repository at this point in the history
  • Loading branch information
richard-ramos committed Nov 27, 2023
1 parent fb49752 commit 03e34b8
Show file tree
Hide file tree
Showing 27 changed files with 736 additions and 346 deletions.
29 changes: 17 additions & 12 deletions examples/c-bindings/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@ void on_error(int ret, const char *result, void *user_data)
return;
}

printf("function execution failed. Returned code: %d\n", ret);
printf("function execution failed. Returned code: %d, %s\n", ret, result);
exit(1);
}

void on_response(int ret, const char *result, void *user_data)
{
if (ret != 0)
{
printf("function execution failed. Returned code: %d\n", ret);
printf("function execution failed. Returned code: %d, %s\n", ret, result);
exit(1);
}

Expand Down Expand Up @@ -119,26 +119,28 @@ void callBack(int ret, const char *signal, void *user_data)

int main(int argc, char *argv[])
{
void* ctx = waku_init();

// Set callback to be executed each time a message is received
waku_set_event_callback(callBack);
waku_set_event_callback(ctx, callBack);

// configJSON can be NULL too to use defaults. Any value not defined will have
// a default set
char *configJSON = "{\"host\": \"0.0.0.0\", \"port\": 60000, "
"\"logLevel\":\"error\", \"store\":true}";
waku_new(configJSON, on_error, NULL);
waku_new(ctx, configJSON, on_error, NULL);

// Start the node, enabling the waku protocols
waku_start(on_error, NULL);
waku_start(ctx, on_error, NULL);

// Obtain the node's peerID
char *peerID = NULL;
waku_peerid(on_response, (void *)&peerID);
waku_peerid(ctx, on_response, (void *)&peerID);
printf("PeerID: %s\n", peerID);

// Obtain the node's multiaddresses
char *addresses = NULL;
waku_listen_addresses(on_response, (void *)&addresses);
waku_listen_addresses(ctx, on_response, (void *)&addresses);
printf("Addresses: %s\n", addresses);

// Build a content topic
Expand All @@ -154,12 +156,12 @@ int main(int argc, char *argv[])

// To use dns discovery, and retrieve nodes from a enrtree url
char *discoveredNodes = NULL;
waku_dns_discovery("enrtree://AO47IDOLBKH72HIZZOXQP6NMRESAN7CHYWIBNXDXWRJRZWLODKII6@test.wakuv2.nodes.status.im",
waku_dns_discovery(ctx, "enrtree://AO47IDOLBKH72HIZZOXQP6NMRESAN7CHYWIBNXDXWRJRZWLODKII6@test.wakuv2.nodes.status.im",
"", 0, on_response, (void *)&discoveredNodes);
printf("Discovered nodes: %s\n", discoveredNodes);

// Connect to a node
waku_connect("/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/30303/"
waku_connect(ctx, "/dns4/node-01.do-ams3.wakuv2.test.statusim.net/tcp/30303/"
"p2p/16Uiu2HAmPLe7Mzm8TsYUubgCAW1aJoeFScxrLj8ppHFivPo97bUZ",
0, on_response, NULL);

Expand All @@ -176,7 +178,7 @@ int main(int argc, char *argv[])
sprintf(contentFilter,
"{\"pubsubTopic\":\"%s\",\"contentTopics\":[\"%s\"]}",
defaultPubsubTopic, contentTopic);
waku_relay_subscribe(contentFilter, on_error, NULL);
waku_relay_subscribe(ctx, contentFilter, on_error, NULL);

int i = 0;
int version = 1;
Expand All @@ -202,7 +204,7 @@ int main(int argc, char *argv[])

// Broadcast via waku relay
char *messageID = NULL;
waku_relay_publish(encodedMessage, defaultPubsubTopic, 0, on_response,
waku_relay_publish(ctx, encodedMessage, defaultPubsubTopic, 0, on_response,
(void *)&messageID);
printf("MessageID: %s\n", messageID);

Expand All @@ -221,7 +223,10 @@ int main(int argc, char *argv[])
// printf("%s\n", local_result);

// Stop the node's execution
waku_stop(on_response, NULL);
waku_stop(ctx, on_response, NULL);

// Release resources allocated to waku
waku_free(ctx, on_response, NULL);

// TODO: free all char*

Expand Down
155 changes: 117 additions & 38 deletions library/c/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ import (

func main() {}

// Allocate memory for a waku node. The result of this function must be passed
// to all waku_* functions
//
//export waku_init
func waku_init() unsafe.Pointer {
cid := C.malloc(C.size_t(unsafe.Sizeof(uintptr(0))))
pid := (*uint)(cid)
instance := library.Init()
*pid = instance.ID
return cid
}

// Initialize a waku node. Receives a JSON string containing the configuration
// for the node. It can be NULL. Example configuration:
// ```
Expand Down Expand Up @@ -88,41 +100,88 @@ func main() {}
// - dns4DomainName: the domain name resolving to the node's public IPv4 address.
//
//export waku_new
func waku_new(configJSON *C.char, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
err := library.NewNode(C.GoString(configJSON))
func waku_new(ctx unsafe.Pointer, configJSON *C.char, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
onError(err, cb, userData)
}

err = library.NewNode(instance, C.GoString(configJSON))
return onError(err, cb, userData)
}

// Starts the waku node
//
//export waku_start
func waku_start(onErr C.WakuCallBack, userData unsafe.Pointer) C.int {
err := library.Start()
func waku_start(ctx unsafe.Pointer, onErr C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
onError(err, onErr, userData)
}

err = library.Start(instance)
return onError(err, onErr, userData)
}

// Stops a waku node
//
//export waku_stop
func waku_stop(onErr C.WakuCallBack, userData unsafe.Pointer) C.int {
err := library.Stop()
func waku_stop(ctx unsafe.Pointer, onErr C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
onError(err, onErr, userData)
}

err = library.Stop(instance)
return onError(err, onErr, userData)
}

// Release the resources allocated to a waku node (stopping the node first if necessary)
//
//export waku_free
func waku_free(ctx unsafe.Pointer, onErr C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
return onError(err, onErr, userData)
}

err = library.Stop(instance)
return onError(err, onErr, userData)
}

// Determine is a node is started or not
//
//export waku_is_started
func waku_is_started() C.int {
started := library.IsStarted()
func waku_is_started(ctx unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
return 0
}

started := library.IsStarted(instance)
if started {
return 1
}
return 0
}

type fn func() (string, error)
type fn func(instance *library.WakuInstance) (string, error)
type fnNoInstance func() (string, error)

func singleFnExec(f fn, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
func singleFnExec(f fn, ctx unsafe.Pointer, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
onError(err, cb, userData)
}

result, err := f(instance)
if err != nil {
return onError(err, cb, userData)
}
return onSuccesfulResponse(result, cb, userData)
}

func singleFnExecNoCtx(f fnNoInstance, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
result, err := f()
if err != nil {
return onError(err, cb, userData)
Expand All @@ -133,62 +192,77 @@ func singleFnExec(f fn, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
// Obtain the peer ID of the waku node
//
//export waku_peerid
func waku_peerid(cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func() (string, error) {
return library.PeerID()
}, cb, userData)
func waku_peerid(ctx unsafe.Pointer, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func(instance *library.WakuInstance) (string, error) {
return library.PeerID(instance)
}, ctx, cb, userData)
}

// Obtain the multiaddresses the wakunode is listening to
//
//export waku_listen_addresses
func waku_listen_addresses(cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func() (string, error) {
return library.ListenAddresses()
}, cb, userData)
func waku_listen_addresses(ctx unsafe.Pointer, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func(instance *library.WakuInstance) (string, error) {
return library.ListenAddresses(instance)
}, ctx, cb, userData)
}

// Add node multiaddress and protocol to the wakunode peerstore
//
//export waku_add_peer
func waku_add_peer(address *C.char, protocolID *C.char, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func() (string, error) {
return library.AddPeer(C.GoString(address), C.GoString(protocolID))
}, cb, userData)
func waku_add_peer(ctx unsafe.Pointer, address *C.char, protocolID *C.char, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func(instance *library.WakuInstance) (string, error) {
return library.AddPeer(instance, C.GoString(address), C.GoString(protocolID))
}, ctx, cb, userData)
}

// Connect to peer at multiaddress. if ms > 0, cancel the function execution if it takes longer than N milliseconds
//
//export waku_connect
func waku_connect(address *C.char, ms C.int, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
err := library.Connect(C.GoString(address), int(ms))
func waku_connect(ctx unsafe.Pointer, address *C.char, ms C.int, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
onError(err, cb, userData)
}

err = library.Connect(instance, C.GoString(address), int(ms))
return onError(err, cb, userData)
}

// Connect to known peer by peerID. if ms > 0, cancel the function execution if it takes longer than N milliseconds
//
//export waku_connect_peerid
func waku_connect_peerid(peerID *C.char, ms C.int, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
err := library.ConnectPeerID(C.GoString(peerID), int(ms))
func waku_connect_peerid(ctx unsafe.Pointer, peerID *C.char, ms C.int, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
onError(err, cb, userData)
}

err = library.ConnectPeerID(instance, C.GoString(peerID), int(ms))
return onError(err, cb, userData)
}

// Close connection to a known peer by peerID
//
//export waku_disconnect
func waku_disconnect(peerID *C.char, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
err := library.Disconnect(C.GoString(peerID))
func waku_disconnect(ctx unsafe.Pointer, peerID *C.char, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
onError(err, cb, userData)
}

err = library.Disconnect(instance, C.GoString(peerID))
return onError(err, cb, userData)
}

// Get number of connected peers
//
//export waku_peer_cnt
func waku_peer_cnt(cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func() (string, error) {
peerCnt, err := library.PeerCnt()
func waku_peer_cnt(ctx unsafe.Pointer, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func(instance *library.WakuInstance) (string, error) {
peerCnt, err := library.PeerCnt(instance)
return fmt.Sprintf("%d", peerCnt), err
}, cb, userData)
}, ctx, cb, userData)
}

// Create a content topic string according to RFC 23
Expand All @@ -211,15 +285,20 @@ func waku_default_pubsub_topic(cb C.WakuCallBack, userData unsafe.Pointer) C.int
// signature for the callback should be `void myCallback(char* signalJSON)`
//
//export waku_set_event_callback
func waku_set_event_callback(cb C.WakuCallBack) {
library.SetEventCallback(unsafe.Pointer(cb))
func waku_set_event_callback(ctx unsafe.Pointer, cb C.WakuCallBack) {
instance, err := getInstance(ctx)
if err != nil {
panic(err.Error()) // TODO: refactor to return an error instead of panic
}

library.SetEventCallback(instance, unsafe.Pointer(cb))
}

// Retrieve the list of peers known by the waku node
//
//export waku_peers
func waku_peers(cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func() (string, error) {
return library.Peers()
}, cb, userData)
func waku_peers(ctx unsafe.Pointer, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func(instance *library.WakuInstance) (string, error) {
return library.Peers(instance)
}, ctx, cb, userData)
}
15 changes: 10 additions & 5 deletions library/c/api_discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,22 @@ import (
// (in milliseconds) is reached, or an error will be returned
//
//export waku_dns_discovery
func waku_dns_discovery(url *C.char, nameserver *C.char, ms C.int, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func() (string, error) {
func waku_dns_discovery(ctx unsafe.Pointer, url *C.char, nameserver *C.char, ms C.int, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
return singleFnExec(func(instance *library.WakuInstance) (string, error) {
return library.DNSDiscovery(C.GoString(url), C.GoString(nameserver), int(ms))
}, cb, userData)
}, ctx, cb, userData)
}

// Update the bootnode list used for discovering new peers via DiscoveryV5
// The bootnodes param should contain a JSON array containing the bootnode ENRs i.e. `["enr:...", "enr:..."]`
//
//export waku_discv5_update_bootnodes
func waku_discv5_update_bootnodes(bootnodes *C.char, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
err := library.SetBootnodes(C.GoString(bootnodes))
func waku_discv5_update_bootnodes(ctx unsafe.Pointer, bootnodes *C.char, cb C.WakuCallBack, userData unsafe.Pointer) C.int {
instance, err := getInstance(ctx)
if err != nil {
onError(err, cb, userData)
}

err = library.SetBootnodes(instance, C.GoString(bootnodes))
return onError(err, cb, userData)
}
Loading

0 comments on commit 03e34b8

Please sign in to comment.