Announcing GNUnet++, experimental high level GNUnet C++ wrapper

Today I'm announcing GNUnet++. An experimentable yet sensable C++ wrapper for GNUnet services. You can find my introduction to GNUnet in my older articles [1][2]. TL;DR GNUnet is GNU's version of IPFS and libp2p. It has some pros and cons over libp2p. Like built-in trafic covering, firewall bypass, etc.. But has (much) lower adoption rate and stablity. In any case, it's a fun project and I see potential in it. The pure C API is a pain point I can help fix - with GNUnet++.

GNUnet++

GNUnet++ aims to provide a RAII-style API for GNUnet. At least make managing GNUnet object lifetime not as painful. And high level function calls for commonly used functions. Currently basic capablitites of the following subsystems are wrapped:

  • DHT
  • File Share
  • Identity
  • Crypto
  • GNS
  • Scheduler

And more is to come. I shall demonstrate what GNUnet++ can do with a simple example. Let's say we want to query the GNS system for the IP address of gnunet.org. We can do this with the following code.

auto gns = std::make_shared<gnunetpp::GNS>(cfg);
gns->lookup("gnunet.org", [](auto result) {
    if(result.empty())
        std::cout << "No result found" << std::endl;
    else {
        for(auto& record : result)
            std::cout << "Found record: " << record << std::endl;
    }
});

Likewise, to publish file to GNUnet's file share system, we can do this:

gnunetpp::FS::publish(cfg, "/path/to/the/file.txt", {}
    , [](auto status, const std::string& uri, const std::string& namespace_uri) {
    if(status == gnunetpp::FS::PublishResult::Success)
        std::cout << "Published " << filename << " at " << uri << std::endl;
    else
        std::cout << "Publish failed" << std::endl;
});

Contrast to what GNUnet's official thousand line utility to publich files. I think GNUnet++ is a lot more reasonable and easier to use. Sure, there's lots of functions missing. I'm adding them as I need. And more importantly, contributions are always welcome.

New CLI Tools

GNUnet++ replements a subset of GNUnet's CLI tools as examples of how to use GNUnet++. These tools are better organized with subcommands and with better help messages. It shoes what's required parameters and the default of optional parameters. For example, the help message of gnunetpp-dht put is:

❯ ./examples/gnunetpp-dht put --help
Put a key-value pair into the GNUnet DHT
Usage: ./examples/gnunetpp-dht put [OPTIONS]

Options:
  -h,--help                   Print this help message and exit
  -k,--key TEXT REQUIRED      Key of the key value pair
  -v,--value TEXT REQUIRED    Value of the key value pair
  -e,--expiration UINT [3600] 
                              Expiration time for the value in seconds
  -r,--replication UINT [5]   Estimation of how many nearest peer this request reaches (not data replication count)

While the oginal gnunet-dht-put is more cryptic:

❯ gnunet-dht-put -h
gnunet-dht-put
Issue a PUT request to the GNUnet DHT insert DATA under KEY.
Arguments mandatory for long options are also mandatory for short options.
  -c, --config=FILENAME      use configuration file FILENAME
  -d, --data=DATA            the data to insert under the key
  -e, --expiration=EXPIRATIONhow long to store this entry in the dht (in
                               seconds)
  -h, --help                 print this help
  -k, --key=KEY              the query key
  -L, --log=LOGLEVEL         configure logging to use LOGLEVEL
  -l, --logfile=FILENAME     configure logging to write logs to FILENAME
  -R, --record               use DHT's record route option
  -r, --replication=LEVEL    how many replicas to create
  -t, --type=TYPE            the type to insert data as
  -V, --verbose              be verbose
  -v, --version              print the version number
  -x, --demultiplex          use DHT's demultiplex everywhere option

Limitations

Like I said above, GNUnet++ is a high level library. This means that it's not as flexible as the original GNUnet API. For example, GNUnet++ doesn't expose the routing information along with the DHT query result. However, I think this is a reasonable tradeoff as that information is rarely used. And it's not hard to add it back if you really need it. Also, GNunet++ is not as "efficient" as using the raw C API as it has to do some extra work to manage object lifetime. Again, I think this is a reasonable tradeoff for very obvious reasons.

Near future work

I decide to announce GNUnet++ at it's current form for a few reasons. 1. The callback hell is getting out of hands and I'll be turning them into coroutines. But that will take some serious problem solving for lifetime management. 2. It's good enough to be slightly useful and I want to get some feedback if anyone happens to find it.

The main thing missing now is CADET and namestore support. Looking at their APIs. I rather implement them after coroutines are done. I'm also debating myself if I should support the chat and auction subsystems. They are such high level that I'm not sure if anyone would use them programmatically.

Pratical Use

I haven't come up with a good pratical use case for GNUnet++ yet (at least something I'd spend time to build). Decentralized p2p is very attractive. But is also hard. In the sense that the traditional client-server archicture is easy to build, low latency and low noise. Decentralizing means you gave up all that and have to deal with a lot of new problems. Like easy DoS attacks, unreliable network, and so on.

I'm working hard on it though. I truely believe in the power of p2p networks.


Find the source code at the following link.

Author's profile. Photo taken in VRChat by my friend Tast+
Martin Chang
Systems software, HPC, GPGPU and AI. I mostly write stupid C++ code. Sometimes does AI research. Chronic VRChat addict

I run TLGS, a major search engine on Gemini. Used by Buran by default.


  • marty1885 \at protonmail.com
  • Matrix: @clehaxze:matrix.clehaxze.tw
  • Jami: a72b62ac04a958ca57739247aa1ed4fe0d11d2df