Files
logos-libp2p-module/tutorial/docs/tutorial_0_introduction.md

6.7 KiB

Tutorial 0: Introduction and Common Patterns

Welcome to the first logos-libp2p-module tutorial!

Before creating nodes or connecting peers, it helps to understand the conventions used by every tutorial in this series.

The C++ wrapper (logos-libp2p-module) exposes a single main class, Libp2pModuleImpl. Most methods on that class return the Logos result type. In C++ module code that type is named StdLogosResult.

StdLogosResult has three fields:

Field Meaning
success true when the call completed successfully
value a nlohmann::json value returned by the call
error a diagnostic string when success == false

Always Check success First

Every tutorial follows the same rule: check unsuccessful outcomes before using returned data.

StdLogosResult info = node.peerInfo();
if (!info.success) {
    fprintf(stderr, "Failed to get peer info: %s\n",
            info.error.c_str());
    return 1;
}

This keeps the code predictable: errors are handled immediately, and the rest of the step can assume the operation succeeded.

Tutorial Executables Fail Fast

These programs are examples, not long-running services. When a required operation fails, they print a useful error message to stderr and return 1. Successful tutorials print progress to stdout and return 0.

Controlling libp2p Logs

The wrapped libp2p binding can emit runtime logs. Tutorials set the log level to "fatal" by default so stdout only shows the tutorial's own progress messages during normal runs. Choose another level such as "info", "debug", or "trace" when you need more detail while debugging:

setLogLevel("debug");
// ... or
Libp2pModuleImpl::setLogLevel("debug");

The level names are "none", "trace", "debug", "info", "notice", "warn", "error" and "fatal"; any other name returns a failed result and leaves the level as it was.

Log levels are inclusive minimum thresholds: "trace" emits trace and above, "debug" emits debug and above, and so on. "none" is the lowest threshold, so it emits all logs; it does not disable logging. Use "fatal" for the quietest built-in threshold.

In your own application, "error" is often a useful default. It keeps normal output quiet while still surfacing conditions that may indicate libp2p is misbehaving or that your integration code needs an adjustment. Some error logs describe remote-peer behavior, retries, or recoverable internal state, so they may not require any action from your side.

Convert JSON Values Explicitly

The value field is JSON. Convert it to the C++ type you need at the point where you use it:

std::string peerId = info.value["peerId"].get<std::string>();

for (const nlohmann::json& addr : info.value["addrs"]) {
    printf("  %s\n", addr.get<std::string>().c_str());
}

Scalar values use get<T>(). Objects are accessed by key. Arrays are iterated with range-for loops.

Tutorial code assumes returned JSON values have the documented type, so it uses and converts values directly instead of checking the JSON type first.

Binary Data May Be Encoded

Some APIs carry arbitrary bytes, such as stream reads, DHT values, public keys, or service discovery records. Those values may be returned as strings encoded for JSON transport. The tutorials decode them before comparing or printing human-readable payloads.


#include <cstdio>
#include <string>
#include "plugin.h"

int main()
{
    printf("=== Tutorial 0: Introduction and Common Patterns ===\n\n");

    // Silence logs from wrapped library.
    setLogLevel("fatal");

Step 1: Create and start a node

Even the first real operation returns a result. Check it before moving on.

    Libp2pModuleImpl node;

    StdLogosResult startRes = node.start();
    if (!startRes.success) {
        fprintf(stderr, "Failed to start node: %s\n",
                startRes.error.c_str());
        return 1;
    }
    printf("Node started\n");

Step 2: Use scalar JSON values

Some calls return a single string, number, or boolean in value.

    StdLogosResult version = node.getNodeInfo("Version");
    if (!version.success) {
        fprintf(stderr, "Failed to get module version: %s\n",
                version.error.c_str());
        return 1;
    }
    printf("Module version: %s\n",
           version.value.get<std::string>().c_str());

Step 3: Use JSON objects and arrays

peerInfo() returns a JSON object. The peerId field is a string, while addrs is an array because a node may listen on multiple addresses.

    StdLogosResult info = node.peerInfo();
    if (!info.success) {
        fprintf(stderr, "Failed to get peer info: %s\n",
                info.error.c_str());
        return 1;
    }

    std::string peerId = info.value["peerId"].get<std::string>();
    printf("Peer ID: %s\n", peerId.c_str());

    printf("Listening addresses:\n");
    for (const nlohmann::json& addr : info.value["addrs"]) {
        printf("  %s\n", addr.get<std::string>().c_str());
    }

Step 4: Stop cleanly

Production code should always attempt to close resources gracefully first.

The tutorials do not always process every cleanup outcome, such as failures while stopping a node or closing streams, in order to keep the example code focused and easy to follow.

    StdLogosResult stopRes = node.stop();
    if (!stopRes.success) {
        fprintf(stderr, "Failed to stop node: %s\n",
                stopRes.error.c_str());
        return 1;
    }

    printf("\n=== Tutorial 0 Complete ===\n");

    return 0;
}

Summary

In this tutorial you learned the conventions used everywhere else:

  • Most calls return StdLogosResult
  • Check success before reading value
  • Print error when a call fails
  • Convert JSON values with get<T>(), object keys, or array iteration
  • Return 1 from tutorial executables when required operations fail

Run tutorial

Run this tutorial now to check that your environment is set up and the tutorial executable has already been built:

./build/tutorial/tutorial_0_introduction

If that command fails because the tutorial has not been built yet, build the tutorials with this command and then run it again:

nix --extra-experimental-features 'nix-command flakes' develop --command ./tutorial/build_tutorials.sh

Exercise: Enable libp2p debug logs

Change this tutorial's log level from "fatal" to "debug":

setLogLevel("debug");

Compile the tutorial binaries again, using the same command from above.

Run tutorial 0 again. You should now see libp2p debug logs in stdout alongside the tutorial's own progress messages.

Creating and Starting a libp2p Node →