waku-org/go-waku

View on GitHub
examples/c-bindings/main.c

Summary

Maintainability
Test Coverage

#include "main.h"
#include "base64.h"
#include "libgowaku.h"
#include "nxjson.c"
#include <inttypes.h>
#include <unistd.h>

char *alicePrivKey =
    "0x4f012057e1a1458ce34189cb27daedbbe434f3df0825c1949475dec786e2c64e";
char *alicePubKey =
    "0x0440f05847c4c7166f57ae8ecaaf72d31bddcbca345e26713ca9e26c93fb8362ddcd5ae7"
    "f4533ee956428ad08a89cd18b234c2911a3b1c7fbd1c0047610d987302";

char *bobPrivKey =
    "0xb91d6b2df8fb6ef8b53b51b2b30a408c49d5e2b530502d58ac8f94e5c5de1453";
char *bobPubKey =
    "0x045eef61a98ba1cf44a2736fac91183ea2bd86e67de20fe4bff467a71249a8a0c05f795d"
    "d7f28ced7c15eaa69c89d4212cc4f526ca5e9a62e88008f506d850cccd";

void on_error(int ret, const char *result, void *user_data)
{
  if (ret == 0)
  {
    return;
  }

  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, %s\n", ret, result);
    exit(1);
  }

  if (user_data == NULL)
    return;

  char **data_ref = (char **)user_data;
  size_t len = strlen(result);

  if (*data_ref != NULL)
    free(*data_ref);

  *data_ref = malloc(len * sizeof(char) + 1);
  strcpy(*data_ref, result);
}

void callBack(int ret, const char *signal, void *user_data)
{
  // This callback will be executed each time a new message is received

  // Example signal:
  /*{
      "nodeId":1,
      "type":"message",
      "event":{
        "pubsubTopic":"/waku/2/default-waku/proto",
        "messageID":"0x6496491e40dbe0b6c3a2198c2426b16301688a2daebc4f57ad7706115eac3ad1",
        "wakuMessage":{
          "payload":"BPABASUqWgRkgp73aW/FHIyGtJDYnStvaQvCoX9MdaNsOH39Vet0em6ipZc3lZ7kK9uFFtbJgIWfRaqTxSRjiFOPx88gXt1JeSm2SUwGSz+1gh2xTy0am8tXkc8OWSSjamdkEbXuVgAueLxHOnV3xlGwYt7nx2G5DWYqUu1BXv4yWHPOoiH2yx3fxX0OajgKGBwiMbadRNUuAUFPRM90f+bzG2y22ssHctDV/U6sXOa9ljNgpAx703Q3WIFleSRozto7ByNAdRFwWR0RGGV4l0btJXM7JpnrYcVC24dB0tJ3HVWuD0ZcwOM1zTL0wwc0hTezLHvI+f6bHSzsFGcCWIlc03KSoMjK1XENNL4dtDmSFI1DQCGgq09c2Bc3Je3Ci6XJHu+FP1F1pTnRzevv2WP8FSBJiTXpmJXdm6evB7V1Xxj4QlzQDvmHLRpBOL6PSttxf1Dc0IwC6BfZRN5g0dNmItNlS2pcY1MtZLxD5zpj",
          "contentTopic":"ABC",
          "version":1,
          "timestamp":1647826358000000000
        }
      }
    }*/

  if (ret != 0)
  {
    printf("function execution failed. Returned code: %d\n", ret);
    exit(1);
  }

  const nx_json *json = nx_json_parse((char *)signal, 0);
  const char *type = nx_json_get(json, "type")->text_value;

  if (strcmp(type, "message") == 0)
  {
    const nx_json *wakuMsgJson =
        nx_json_get(nx_json_get(json, "event"), "wakuMessage");
    const char *contentTopic =
        nx_json_get(wakuMsgJson, "contentTopic")->text_value;

    if (strcmp(contentTopic, "/example/1/default/rfc26") == 0)
    {
      char *msg = utils_extract_wakumessage_from_signal(wakuMsgJson);

      // Decode a message using asymmetric encryption
      char *decodedMsg = NULL;
      waku_decode_asymmetric(msg, bobPrivKey, on_response, (void *)&decodedMsg);

      const nx_json *dataJson = nx_json_parse(decodedMsg, 0);

      const char *pubkey = nx_json_get(dataJson, "pubkey")->text_value;
      const char *base64data = nx_json_get(dataJson, "data")->text_value;

      size_t data_len = b64_decoded_size(base64data);
      unsigned char *data = malloc(data_len);

      b64_decode(base64data, data, data_len);

      printf(">>> Received \"%s\" from %s\n", data, pubkey);

      free(msg);
      free(decodedMsg);
      free(data);

      fflush(stdout);
    }
  }

  nx_json_free(json);
}

int main(int argc, char *argv[])
{
  // 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}";
  void* ctx = waku_new(configJSON, on_error, NULL);

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

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

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

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

  // Build a content topic
  char *contentTopic = NULL;
  waku_content_topic("example", "1", "default", "rfc26", on_response,
                     (void *)&contentTopic);
  printf("Content Topic: %s\n", contentTopic);

  // Obtain the default pubsub topic
  char *defaultPubsubTopic = NULL;
  waku_default_pubsub_topic(on_response, (void *)&defaultPubsubTopic);
  printf("Default pubsub topic: %s\n", defaultPubsubTopic);

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

  // Connect to a node
  waku_connect(ctx, "/dns4/node-01.do-ams3.waku.test.status.im/tcp/30303/"
               "p2p/16Uiu2HAkykgaECHswi3YKJ5dMLbq2kPVCo89fcyTd38UcQD6ej5W",
               0, on_response, NULL);

  // To see a store query in action:
  // char query[1000];
  // sprintf(query,
  //        "{\"pubsubTopic\":\"%s\", \"pagingOptions\":{\"pageSize\": 40,  "
  //        "\"forward\":false}}",
  //        pubsubTopic);
  // char *query_result = NULL;
  // waku_store_query(query, NULL, 0, on_response, (void*)&query_result);
  // printf("%s\n", query_result);
  char contentFilter[1000];
  sprintf(contentFilter,
          "{\"pubsubTopic\":\"%s\",\"contentTopics\":[\"%s\"]}",
          defaultPubsubTopic, contentTopic);
  waku_relay_subscribe(ctx, contentFilter, on_error, NULL);

  int i = 0;
  int version = 1;
  while (i < 5)
  {
    i++;

    char wakuMsg[1000];
    unsigned char plain_text[] = "Hello World!";
    char *msgPayload = b64_encode(&plain_text[0], 12);

    // Build the waku message
    sprintf(wakuMsg,
            "{\"payload\":\"%s\",\"contentTopic\":\"%s\",\"timestamp\":%" PRIu64
            "}",
            msgPayload, contentTopic, nowInNanosecs());
    free(msgPayload);

    // Use asymmetric encryption to encrypt the waku message
    char *encodedMessage = NULL;
    waku_encode_asymmetric(wakuMsg, bobPubKey, alicePrivKey, on_response,
                           (void *)&encodedMessage);

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

    sleep(1);
  }

  // To retrieve messages from local store, set store:true in the node
  // config, and use waku_store_local_query
  // char query2[1000];
  // sprintf(query2,
  //        "{\"pubsubTopic\":\"%s\", \"pagingOptions\":{\"pageSize\":  40, "
  //        "\"forward\":false}}",
  //        pubsubTopic);
  // char *local_result = NULL;
  // waku_store_local_query(query2, on_response, (void*)&local_result);
  // printf("%s\n", local_result);

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

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

  // TODO: free all char*

  return 0;
}