nym-cpp-ffi 1.21.1

C++ FFI bindings for Nym Rust SDK
Documentation
// Copyright 2023-2024 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0

#include <iostream>
#include <thread>
#include <boost/chrono.hpp>
#include <boost/thread.hpp>

//Rust function & type signatures
extern "C" {
    struct ReceivedMessage {
       const uint8_t* message;
       size_t size;
       const char* sender_tag;
    };

    void* init_logging();
    char init_ephemeral();
    char get_self_address(void (*callback)(const char*));
    char send_message(const char*, const char*);
    char listen_for_incoming(void (*callback)(ReceivedMessage));
    char reply(const char*, const char*);
}

//return code for error handling
char return_code = 0;
// bytes for sender tag
char sender_tag[22];
// nym address
char addr[134];
// test messages
char message[14] = "Hello World";
char reply_message[14] = "Reply World";

void string_callback_function(const char* c_string) {
    std::cout << "(c++)  callback received: " << c_string << std::endl;
    strcpy(addr, c_string);
}

void incoming_message_callback(ReceivedMessage received) {
    // this is where you deal with the incoming message -
    // in this case we'll just log it and save sender_tag to a pre-allocated
    // buffer to reply to the message further down in main()
    std::cout << "(c++) sender tag: " << received.sender_tag << std::endl;
    std::cout << "(c++) message: " << received.message << std::endl;
    std::cout << "(c++) message length : " << received.size << std::endl;
    const char* incoming_sender_tag = received.sender_tag;
    strcpy(sender_tag, incoming_sender_tag);
}

// an overly simplified example - handle the error however you wish
int handle(char return_code) {
    if (return_code == 0) {
        return 0;
    } else {
        return -1;
    }
}

int main() {
    // initialise Nym client logging - this is quite verbose but very informative
    init_logging();

    // blocking thread example with error return code:
    // - package fn
    // - obtain as future
    // - execute
    // - get() returned val
    // - handle val
    // initialise an ephemeral client - aka one without specified keystore
    boost::packaged_task<char> init(boost::bind(init_ephemeral));
    boost::unique_future<char> init_future = init.get_future();
    init();
    return_code = init_future.get();
    handle(return_code);

    // get_self_addr is sync so no thread required: this is the only exposed rust fn that isn't async
    return_code = get_self_address(string_callback_function);
    handle(return_code);

    // send a message through the mixnet - in this case to ourselves using the value from get_self_address
    std::cout << "(c++)  message to send through mixnet: " << message << std::endl;
    boost::packaged_task<char> send(boost::bind(send_message, addr, message));
    boost::unique_future<char> send_future = send.get_future();
    send();
    return_code = send_future.get();
    handle(return_code);

    // listen out for incoming messages: in the future the client can be split into a listening and a sending client,
    // allowing for this to run as a persistent process in its own thread and not have to block but instead be running
    // concurrently
    boost::packaged_task<char> listen(boost::bind(listen_for_incoming, incoming_message_callback));
    boost::unique_future<char> listen_future = listen.get_future();
    listen();
    return_code = listen_future.get();
    handle(return_code);

    // replying to incoming message (from ourselves) with SURBs - note that sending a message to a recipient and
    // replying to an incoming are different functions: replying relies on parsing the incoming sender_tag on the Rust
    // side and creating an AnonymousSenderTag type, instead of the Recipient type which relies on a nym address
    boost::packaged_task<char> reply_fn(boost::bind(reply, sender_tag, reply_message));
    boost::unique_future<char> reply_future = reply_fn.get_future();
    reply_fn();
    return_code = reply_future.get();
    handle(return_code);

    // sleep so that the client processes can catch up - in reality you'd have another process running to keep logging
    // going, so this is only necessary for this reference
    std::this_thread::sleep_for(std::chrono::seconds(40));

    return 0;
}