Skip to content
Joshua Dahl edited this page Jul 27, 2023 · 8 revisions

src/simplep2p.hpp

p2p Namespace

Classes

Name
struct p2p::PeerID
Alias equating a PeerID and a string.
class p2p::Key
Represents a P2P cryptographic private key.
struct p2p::Topic
Represents a P2P topic.
class p2p::Network
Represents the P2P network.
struct p2p::Message
Represents a P2P message.

Attributes

Name
constexpr bool do_not_initialize
Constant to indicate that initialization should not be performed automatically.
constexpr std::string_view default_listen_address
Default listen address used for network initialization.
constexpr std::string_view default_discovery_topic
Default discovery topic used for network initialization.

Attributes Documentation

variable do_not_initialize

constexpr bool do_not_initialize = false;

Constant to indicate that initialization should not be performed automatically.

variable default_listen_address

constexpr std::string_view default_listen_address = "/ip4/0.0.0.0/udp/0/quic-v1";

Default listen address used for network initialization.

variable default_discovery_topic

constexpr std::string_view default_discovery_topic = "simpleP2P";

Default discovery topic used for network initialization.

p2p::PeerID

Alias equating a PeerID and a string.

Inherits from std::string

Public Types

Name
using std::string_view view

Public Functions

Name
PeerID(const std::string & o)
PeerID(std::string && o)
PeerID(const PeerID & ) =default
PeerID(PeerID && ) =default
PeerID & operator=(const PeerID & ) =default
PeerID & operator=(PeerID && ) =default

Public Types Documentation

using view

using p2p::PeerID::view =  std::string_view;

Public Functions Documentation

function PeerID

inline PeerID(
    const std::string & o
)

function PeerID

inline PeerID(
    std::string && o
)

function PeerID

PeerID(
    const PeerID & 
) =default

function PeerID

PeerID(
    PeerID && 
) =default

function operator=

PeerID & operator=(
    const PeerID & 
) =default

function operator=

PeerID & operator=(
    PeerID && 
) =default

p2p::Key

Represents a P2P cryptographic private key.

Public Functions

Name
Key() =default
Default constructor.
Key(P2PKey && o)
Move constructor.
Key(const Key & ) =default
Copy constructor.
Key(Key && ) =default
Move constructor.
Key & operator=(P2PKey && o)
Move assignment operator.
Key & operator=(const Key & ) =default
Copy assignment operator.
Key & operator=(Key && ) =default
Move assignment operator.
operator P2PKey()
Conversion operator to P2PKey.
operator P2PKey() const
Conversion operator to P2PKey (const version).
std::ostream & save(std::ostream & out) const
Saves the Key object to an output stream.
std::istream & load(std::istream & in)
Loads the Key object from an input stream.
Key generate()
Generates a new P2P key.

Public Functions Documentation

function Key

Key() =default

Default constructor.

function Key

inline Key(
    P2PKey && o
)

Move constructor.

Parameters:

  • o The Key object to move from.

function Key

Key(
    const Key & 
) =default

Copy constructor.

Parameters:

  • o The Key object to copy from.

function Key

Key(
    Key && 
) =default

Move constructor.

Parameters:

  • o The Key object to move from.

function operator=

inline Key & operator=(
    P2PKey && o
)

Move assignment operator.

Parameters:

  • o The Key object to move from.

Return: Reference to the assigned Key object.

function operator=

Key & operator=(
    const Key & 
) =default

Copy assignment operator.

Parameters:

  • o The Key object to copy from.

Return: Reference to the assigned Key object.

function operator=

Key & operator=(
    Key && 
) =default

Move assignment operator.

Parameters:

  • o The Key object to move from.

Return: Reference to the assigned Key object.

function operator P2PKey

inline operator P2PKey()

Conversion operator to P2PKey.

Return: The underlying P2PKey structure.

function operator P2PKey

inline operator P2PKey() const

Conversion operator to P2PKey (const version).

Return: The underlying P2PKey structure.

function save

inline std::ostream & save(
    std::ostream & out
) const

Saves the Key object to an output stream.

Parameters:

  • out The output stream to save to.

Return: Reference to the output stream.

function load

inline std::istream & load(
    std::istream & in
)

Loads the Key object from an input stream.

Parameters:

  • in The input stream to load from.

Return: Reference to the input stream.

function generate

static inline Key generate()

Generates a new P2P key.

Return: The generated Key object.

p2p::Topic

Represents a P2P topic.

Public Functions

Name
Topic find(std::string_view name)
Finds a topic by name.
bool valid() const
Checks if the Topic object is valid.
operator bool() const
std::string name() const
Gets the name of the topic.
bool leave()
Leaves the topic.

Public Attributes

Name
P2PTopic id

Public Functions Documentation

function find

static inline Topic find(
    std::string_view name
)

Finds a topic by name.

Parameters:

  • name The name of the topic to find.

Return: The Topic object representing the found topic.

function valid

inline bool valid() const

Checks if the Topic object is valid.

Return: True if the topic is valid, false otherwise.

function operator bool

inline operator bool() const

function name

inline std::string name() const

Gets the name of the topic.

Return: The name of the topic.

function leave

inline bool leave()

Leaves the topic.

Return: True if successfully left the topic, false otherwise.

Public Attributes Documentation

variable id

P2PTopic id;

p2p::Network

Represents the P2P network.

Public Functions

Name
Network & get_singleton()
Gets the singleton instance of the Network class.
Network(std::string_view listenAddress =default_listen_address, std::string_view discoveryTopic =default_discovery_topic, const Key & identityKey ={}, delegate_function< void()> do_on_connected =nullptr, std::chrono::milliseconds connectionTimeout =std::chrono::seconds(60), bool verbose =false) =default
Constructor that initializes the P2P network connection.
Network(bool dontInit)
Constructor for not automatically initializing the network.
~Network()
Destructor.
Network(const Network & ) =delete
Copy constructor (deleted).
Network(Network && ) =delete
Move constructor (deleted).
void initialize(std::string_view listenAddress =default_listen_address, std::string_view discoveryTopic =default_discovery_topic, const Key & identityKey ={}, std::chrono::milliseconds connectionTimeout =std::chrono::seconds(60), bool verbose =false) =default
Initializes the P2P network connection.
void shutdown()
Shuts down the network connection.
PeerID local_id() const
Gets the local hashed ID for the P2P network.
Topic subscribe_to_topic(std::string_view name)
Subscribes to a topic with the provided name.
bool broadcast_message(std::string_view message, Topic topic) const
Broadcasts a message to a topic.
bool broadcast_message(std::string_view message) const
Broadcasts a message to the default topic.
bool broadcast_message(std::span< std::byte > message, Topic topic) const
Broadcasts a byte-span message to a topic.
bool broadcast_message(std::span< std::byte > message) const
Broadcasts a byte-span message to the default topic.

Public Attributes

Name
Topic defaultTopic
the default topic that the network originally joined
delegate< void(struct Message &)> on_message
delegate< void(PeerID::view)> on_peer_connected
delegate< void(PeerID::view)> on_peer_disconnected
delegate< void(Topic)> on_topic_subscribed
delegate< void(Topic)> on_topic_unsubscribed
delegate< void()> on_connected
delegate< void()> on_disconnected

Protected Functions

Name
void override_message_callback(P2PMsgCallback callback)
Overrides the message callback with the provided function pointer.
void override_peer_connected_callback(P2PPeerCallback callback)
Overrides the peer connected callback with the provided function pointer.
void override_peer_disconnected_callback(P2PPeerCallback callback)
Overrides the peer disconnected callback with the provided function pointer.
void override_topic_subscribed_callback(P2PTopicCallback callback)
Overrides the topic subscribed callback with the provided function pointer.
void override_topic_unsubscribed_callback(P2PTopicCallback callback)
Overrides the topic unsubscribed callback with the provided function pointer.
void override_connected_callback(P2PVoidCallback callback)
Overrides the connected callback with the provided function pointer.
void override_disconnected_callback(P2PVoidCallback callback)
Overrides the disconnected callback with the provided function pointer.

Public Functions Documentation

function get_singleton

static inline Network & get_singleton()

Gets the singleton instance of the Network class.

Return: The singleton instance.

function Network

inline Network(
    std::string_view listenAddress =default_listen_address,
    std::string_view discoveryTopic =default_discovery_topic,
    const Key & identityKey ={},
    delegate_function< void()> do_on_connected =nullptr,
    std::chrono::milliseconds connectionTimeout =std::chrono::seconds(60),
    bool verbose =false
) =default

Constructor that initializes the P2P network connection.

Parameters:

  • listenAddress The multiaddress we should listen for connections on.
  • discoveryTopic The discovery topic for network initialization.
  • identityKey The identity key for network initialization.
  • do_on_connected Callback function to register in on_connected before initializing the connection
  • connectionTimeout The time to wait for a connection before giving up.
  • verbose Flag indicating if the GO library should spew some more verbose messages.

function Network

inline Network(
    bool dontInit
)

Constructor for not automatically initializing the network.

Parameters:

  • dontInit Placeholder argument to differentiate from the other constructor.

function ~Network

inline ~Network()

Destructor.

function Network

Network(
    const Network & 
) =delete

Copy constructor (deleted).

function Network

Network(
    Network && 
) =delete

Move constructor (deleted).

function initialize

inline void initialize(
    std::string_view listenAddress =default_listen_address,
    std::string_view discoveryTopic =default_discovery_topic,
    const Key & identityKey ={},
    std::chrono::milliseconds connectionTimeout =std::chrono::seconds(60),
    bool verbose =false
) =default

Initializes the P2P network connection.

Parameters:

  • listenAddress The multiaddress we should listen for connections on.
  • discoveryTopic The discovery topic for network initialization.
  • identityKey The identity key for network initialization.
  • connectionTimeout The time to wait for a connection before giving up.
  • verbose Flag indicating if the GO library should spew some more verbose messages.

function shutdown

inline void shutdown()

Shuts down the network connection.

function local_id

inline PeerID local_id() const

Gets the local hashed ID for the P2P network.

Return: The local hashed ID.

function subscribe_to_topic

inline Topic subscribe_to_topic(
    std::string_view name
)

Subscribes to a topic with the provided name.

Parameters:

  • name The name of the topic to subscribe to.

Return: The Topic object representing the subscribed topic.

function broadcast_message

inline bool broadcast_message(
    std::string_view message,
    Topic topic
) const

Broadcasts a message to a topic.

Parameters:

  • message The message to broadcast.
  • topic The Topic object representing the target topic.

Return: True if the message was successfully broadcasted, false otherwise.

function broadcast_message

inline bool broadcast_message(
    std::string_view message
) const

Broadcasts a message to the default topic.

Parameters:

  • message The message to broadcast.

Return: True if the message was successfully broadcasted, false otherwise.

function broadcast_message

inline bool broadcast_message(
    std::span< std::byte > message,
    Topic topic
) const

Broadcasts a byte-span message to a topic.

Parameters:

  • message The byte-span message to broadcast.
  • topic The Topic object representing the target topic.

Return: True if the message was successfully broadcasted, false otherwise.

function broadcast_message

inline bool broadcast_message(
    std::span< std::byte > message
) const

Broadcasts a byte-span message to the default topic.

Parameters:

  • message The byte-span message to broadcast.

Return: True if the message was successfully broadcasted, false otherwise.

Public Attributes Documentation

variable defaultTopic

Topic defaultTopic;

the default topic that the network originally joined

variable on_message

delegate< void(struct Message &)> on_message;

variable on_peer_connected

delegate< void(PeerID::view)> on_peer_connected;

variable on_peer_disconnected

delegate< void(PeerID::view)> on_peer_disconnected;

variable on_topic_subscribed

delegate< void(Topic)> on_topic_subscribed;

variable on_topic_unsubscribed

delegate< void(Topic)> on_topic_unsubscribed;

variable on_connected

delegate< void()> on_connected;

variable on_disconnected

delegate< void()> on_disconnected;

Protected Functions Documentation

function override_message_callback

inline void override_message_callback(
    P2PMsgCallback callback
)

Overrides the message callback with the provided function pointer.

Parameters:

  • callback The function pointer to the message callback.

function override_peer_connected_callback

inline void override_peer_connected_callback(
    P2PPeerCallback callback
)

Overrides the peer connected callback with the provided function pointer.

Parameters:

  • callback The function pointer to the peer connected callback.

function override_peer_disconnected_callback

inline void override_peer_disconnected_callback(
    P2PPeerCallback callback
)

Overrides the peer disconnected callback with the provided function pointer.

Parameters:

  • callback The function pointer to the peer disconnected callback.

function override_topic_subscribed_callback

inline void override_topic_subscribed_callback(
    P2PTopicCallback callback
)

Overrides the topic subscribed callback with the provided function pointer.

Parameters:

  • callback The function pointer to the topic subscribed callback.

function override_topic_unsubscribed_callback

inline void override_topic_unsubscribed_callback(
    P2PTopicCallback callback
)

Overrides the topic unsubscribed callback with the provided function pointer.

Parameters:

  • callback The function pointer to the topic unsubscribed callback.

function override_connected_callback

inline void override_connected_callback(
    P2PVoidCallback callback
)

Overrides the connected callback with the provided function pointer.

Parameters:

  • callback The function pointer to the connected callback.

function override_disconnected_callback

inline void override_disconnected_callback(
    P2PVoidCallback callback
)

Overrides the disconnected callback with the provided function pointer.

Parameters:

  • callback The function pointer to the disconnected callback.

p2p::Message

Represents a P2P message.

Inherits from P2PMessage

Public Functions

Name
PeerID::view sender()
Gets the sender of the message.
std::string_view data_string()
Gets the message data as a string view.
std::span< std::byte > data()
Gets the message data as a byte span.
bool is_local()
Checks if the message was sent by the local node.

Additional inherited members

Public Attributes inherited from P2PMessage

Name
char * from
???
char * seqno
The sequence number of the message.
char * topic
The topic of the message.
char * signature
???
char * key
The key of the message.
char * id
The ID of the message.
char * received_from
The sender of the message.

Public Functions Documentation

function sender

inline PeerID::view sender()

Gets the sender of the message.

Return: The sender's ID.

function data_string

inline std::string_view data_string()

Gets the message data as a string view.

Return: The message data as a string view.

function data

inline std::span< std::byte > data()

Gets the message data as a byte span.

Return: The message data as a byte span.

function is_local

inline bool is_local()

Checks if the message was sent by the local node.

Return: True if the message was sent by the local node, false otherwise.

Source code

#ifndef SIMPLE_P2P_NETWORKING_HPP
#define SIMPLE_P2P_NETWORKING_HPP

#include "delegate.hpp"

#include <string_view>
#include <span>
#include <iostream>
#include <vector>
#include <optional>
#include <chrono>


namespace p2p {
    #include "simplep2p.h"

    struct PeerID: public std::string {
        using std::string::string;
        using std::string::operator=;
        PeerID(const std::string& o) : std::string(o) {}
        PeerID(std::string&& o) : std::string(std::move(o)) {}
        PeerID(const PeerID&) = default;
        PeerID(PeerID&&) = default;
        PeerID& operator=(const PeerID&) = default;
        PeerID& operator=(PeerID&&) = default;

        using view = std::string_view;
    };

    constexpr bool do_not_initialize = false;

    constexpr std::string_view default_listen_address = "/ip4/0.0.0.0/udp/0/quic-v1";

    constexpr std::string_view default_discovery_topic = "simpleP2P";

    class Key {
        std::vector<std::byte> data;

        static void freeBase(P2PKey& base) { free((void*)base.data); base.data = nullptr; base.size = 0; }
    public:
        Key() = default;

        Key(P2PKey&& o) : data((std::byte*)o.data, ((std::byte*)o.data) + o.size) { freeBase(o); }

        Key(const Key&) = default;

        Key(Key&&) = default;

        Key& operator=(P2PKey&& o) { data = std::vector<std::byte>((std::byte*)o.data, ((std::byte*)o.data) + o.size); freeBase(o); return *this; }

        Key& operator=(const Key&) = default;

        Key& operator=(Key&&) = default;

        operator P2PKey() { return {(char*)data.data(), (int)data.size()}; }

        operator P2PKey() const { return {(char*)data.data(), (int)data.size()}; }

        std::ostream& save(std::ostream& out) const {
            auto size = data.size();
            out.write(reinterpret_cast<char*>(&size), sizeof(size));
            out.write((char*)(data.data()), size);
            return out;
        }

        std::istream& load(std::istream& in) {
            auto size = data.size();
            in.read(reinterpret_cast<char*>(&size), sizeof(size));
            data.resize(size);
            in.read(reinterpret_cast<char*>(data.data()), size);
            return in;
        }

        static Key generate() { return p2p_generate_key(); }
    };

    struct Topic {
        P2PTopic id;

        static Topic find(std::string_view name) { return { p2p_find_topicn(name.data(), name.size()) }; }

        bool valid() const { return id >= 0; }
        operator bool() const { return valid(); }

        std::string name() const {
            auto raw = p2p_topic_name(id);
            std::string out = raw;
            free((char*)raw);
            return out;
        }

        bool leave() { return p2p_leave_topic(id); }
    };

    class Network {
        static Network* singleton;
    public:
        static Network& get_singleton() { return *singleton; }

        Topic defaultTopic;

        // Multicast delegates representing the different network events
        delegate<void(struct Message&)> on_message;
        delegate<void(PeerID::view)> on_peer_connected; // Note: only called for directly connected peers... if you need all peers work at a higher level!
        delegate<void(PeerID::view)> on_peer_disconnected;
        delegate<void(Topic)> on_topic_subscribed;
        delegate<void(Topic)> on_topic_unsubscribed;
        delegate<void()> on_connected;
        delegate<void()> on_disconnected;

        Network(
            std::string_view listenAddress = default_listen_address,
            std::string_view discoveryTopic = default_discovery_topic,
            const Key& identityKey = {},
            delegate_function<void()> do_on_connected = nullptr,
            std::chrono::milliseconds connectionTimeout = std::chrono::seconds(60),
            bool verbose = false
        ) {
            singleton = this;

            if(do_on_connected != nullptr)
                on_connected = do_on_connected;

            initialize(listenAddress, discoveryTopic, identityKey, connectionTimeout, verbose);
        }

        Network(bool dontInit) { singleton = this; }

        ~Network() { shutdown(); }

        Network(const Network&) = delete;

        Network(Network&&) = delete;

        void initialize(
            std::string_view listenAddress = default_listen_address,
            std::string_view discoveryTopic = default_discovery_topic,
            const Key& identityKey = {},
            std::chrono::milliseconds connectionTimeout = std::chrono::seconds(60),
            bool verbose = false
        ) {
            // Connect the delegates to the callbacks
            override_message_callback(on_mesage_impl);
            override_connected_callback(on_connected_impl);
            override_disconnected_callback(on_disconnected_impl);
            override_peer_connected_callback(on_peer_connected_impl);
            override_peer_disconnected_callback(on_peer_disconnected_impl);
            override_topic_subscribed_callback(on_topic_subscribed_impl);
            override_topic_unsubscribed_callback(on_topic_unsubscribed_impl);

            // Initialize the GO library!
            defaultTopic = { p2p_initialize({
                .listenAddress = listenAddress.data(),
                .listenAddressSize = (long long)listenAddress.size(),
                .discoveryTopic = discoveryTopic.data(),
                .discoveryTopicSize = (long long)discoveryTopic.size(),
                .identity = identityKey,
                .connectionTimeout = std::chrono::duration_cast<std::chrono::duration<double>>(connectionTimeout).count(),
                .verbose = verbose
            })};
        }

        void shutdown() { p2p_shutdown(); }

        PeerID local_id() const {
            auto raw = p2p_local_id();
            std::string out = raw;
            free((char*)raw);
            return out;
        }

        Topic subscribe_to_topic(std::string_view name) { return { p2p_subscribe_to_topicn(name.data(), name.size()) }; }

        bool broadcast_message(std::string_view message, Topic topic) const { return p2p_broadcast_messagen(message.data(), message.size(), topic.id); }

        bool broadcast_message(std::string_view message) const { return broadcast_message(message, defaultTopic); }

        bool broadcast_message(std::span<std::byte> message, Topic topic) const { return p2p_broadcast_messagen((char*)message.data(), message.size(), topic.id); }

        bool broadcast_message(std::span<std::byte> message) const { return broadcast_message(message, defaultTopic); }

    protected:
        void override_message_callback(P2PMsgCallback callback) { p2p_set_message_callback(callback); }

        void override_peer_connected_callback(P2PPeerCallback callback) { p2p_set_peer_connected_callback(callback); }

        void override_peer_disconnected_callback(P2PPeerCallback callback) { p2p_set_peer_disconnected_callback(callback); }

        void override_topic_subscribed_callback(P2PTopicCallback callback) { p2p_set_topic_subscribed_callback(callback); }

        void override_topic_unsubscribed_callback(P2PTopicCallback callback) { p2p_set_topic_unsubscribed_callback(callback); }

        void override_connected_callback(P2PVoidCallback callback) { p2p_set_connected_callback(callback); }

        void override_disconnected_callback(P2PVoidCallback callback) { p2p_set_disconnected_callback(callback); }

    private:
        static bool on_mesage_impl(P2PMessage* msg) {
            if(!get_singleton().on_message.empty())
                get_singleton().on_message(*reinterpret_cast<struct Message*>(msg));
            return true; // Go should never panic!
        }

        static bool on_peer_connected_impl(char* peerID) {
            if(!get_singleton().on_peer_connected.empty())
                get_singleton().on_peer_connected(peerID);
            return true; // Go should never panic!
        }

        static bool on_peer_disconnected_impl(char* peerID) {
            if(!get_singleton().on_peer_disconnected.empty())
                get_singleton().on_peer_disconnected(peerID);
            return true; // Go should never panic!
        }

        static bool on_topic_subscribed_impl(P2PTopic topicID) {
            if(!get_singleton().on_topic_subscribed.empty())
                get_singleton().on_topic_subscribed({topicID});
            return true; // Go should never panic!
        }

        static bool on_topic_unsubscribed_impl(P2PTopic topicID) {
            if(!get_singleton().on_topic_unsubscribed.empty())
                get_singleton().on_topic_unsubscribed({topicID});
            return true; // Go should never panic!
        }

        static bool on_connected_impl() {
            if(!get_singleton().on_connected.empty())
                get_singleton().on_connected();
            return true; // Go should never panic!
        }

        static bool on_disconnected_impl() {
            if(!get_singleton().on_disconnected.empty())
                get_singleton().on_disconnected();
            return true; // Go should never panic!
        }
    };

#ifdef SIMPLE_P2P_IMPLEMENTATION
    Network* Network::singleton = nullptr;
#endif

    struct Message: private P2PMessage {
        PeerID::view sender() { return received_from; }

        std::string_view data_string() { return P2PMessage::data; }

        std::span<std::byte> data() { auto view = data_string(); return { (std::byte*)view.data(), view.size() }; }

        bool is_local() { return Network::get_singleton().local_id() == sender(); }
    };
}

#endif // SIMPLE_P2P_NETWORKING_HPP
Clone this wiki locally