Skip to main

AK#Notes

Understanding Hashgraph

I made my own simple implementation of Hashgraph in C, because I couldn’t find any simple implementation of it.

Most of the projects that I found on Google and GitHub were using very super high-level library made by Hedera (company that made Hashgraph).

Problem with using these high level library is, it's very hard to understand whats really happening inside data structure.

Plus, you need to create a Hedera account to connect to the network for the library to work. It is too much when you just want to make a simple implantation of protocol.

NOTE: I am still learning about hashgraph. so following information can contain silly mistakes.

Why there is so little work done on Hashgraph?

There are 2 reasons:

Technical Info

Instead of using block like blockchain Hashgraph uses event.

Here what is contain inside event:

.c
typedef struct Event {
    U64 sig;
    time_t timestamp;
    char payload[100]; // also called as tranaction like blockchain
    U64 self_parent;
    U64 other_parent;
} Event;

I use djb2 hash for simplicity:

.c
U64 hash_djb2(const U8 *data, size_t len, U64 seed) {
    U64 hash = seed > 0 ? seed : 5381;
    for (size_t i = 0; i < len; i++) {
        int c = data[i];
        hash = ((hash << 5) + hash) + c; // hash * 33 + c
    }
    return hash;
}

Hashgraph uses gossip protocol instead of PoW & PoS:

.c
void node_gossip(Node* from_node, Node* to_node) {
    if (from_node->events_count == 0) return;

    Event* from_last_event = &from_node->events[from_node->events_count - 1];
    U64 other_hash = hash_djb2((const U8*)from_last_event, sizeof(Event), 0);
    add_event(to_node, "Received gossip", other_hash);

    printf("+ %s gossiped to %s\n", from_node->name, to_node->name);
}

Full Implementation

.c
#include <stdint.h>
#include <time.h>
#include <string.h>
#include <stdio.h>

typedef uint64_t U64;
typedef unsigned char U8;

typedef struct Event {
    U64 sig;
    time_t timestamp;
    char payload[100];
    U64 self_parent;
    U64 other_parent;
} Event;

typedef struct Node {
    char name[20];
    char pub_key[20];
    Event events[10];
    int events_count;
    struct Node* next;
} Node;

typedef struct Hashgraph {
    Node nodes[10];
    uint32_t nodes_count;
} Hashgraph;

U64 hash_djb2(const U8 *data, size_t len, U64 seed) {
    U64 hash = seed > 0 ? seed : 5381;
    for (size_t i = 0; i < len; i++) {
        int c = data[i];
        hash = ((hash << 5) + hash) + c; // hash * 33 + c
    }
    return hash;
}

void add_event(Node* node, const char* payload, U64 other_parent) {
    printf("- Added Event '%s' to Node '%s'\n", payload, node->name);

    if (node->events_count >= 10) return;

    Event* event = &node->events[node->events_count];
    event->timestamp = time(NULL);
    strncpy(event->payload, payload, 100);
    event->payload[99] = '\0';

    if (node->events_count > 0) {
        Event* last_event = &node->events[node->events_count - 1];
        event->self_parent = hash_djb2((const U8*)last_event, sizeof(Event), 0);
    } else {
        event->self_parent = 0;
    }

    event->other_parent = other_parent;

    event->sig = hash_djb2((const U8*)event, sizeof(Event), (U64)node->pub_key);

    node->events_count++;
}

Node* create_node(Hashgraph* hashgraph, const char* name, const char* pub_key) {
    printf("Created node: %s\n", name);

    if (hashgraph->nodes_count >= 10) return NULL;

    Node* node = &hashgraph->nodes[hashgraph->nodes_count];
    strncpy(node->name, name, sizeof(node->name) - 1);
    node->name[sizeof(node->name) - 1] = '\0';
    strncpy(node->pub_key, pub_key, sizeof(node->pub_key) - 1);
    node->pub_key[sizeof(node->pub_key) - 1] = '\0';

    memset(node->events, 0, sizeof(node->events));
    node->events_count = 0;
    add_event(node, "Genesises Event", 0);
    node->next = NULL;

    hashgraph->nodes_count++;
    return node;
}

void node_gossip(Node* from_node, Node* to_node) {
    if (from_node->events_count == 0) return;

    Event* from_last_event = &from_node->events[from_node->events_count - 1];
    U64 other_hash = hash_djb2((const U8*)from_last_event, sizeof(Event), 0);
    add_event(to_node, "Received gossip", other_hash);

    printf("+ %s gossiped to %s\n", from_node->name, to_node->name);
}

int main(void) {
    Hashgraph hashgraph = {0};

    Node* node1 = create_node(&hashgraph, "Node 1", "1234567890");
    Node* node2 = create_node(&hashgraph, "Node 2", "1234234344");

    add_event(node1, "Data 1", 0);
    add_event(node1, "Data 2", 0);
    node_gossip(node1, node2);
    add_event(node2, "hello 2", 0);
    node_gossip(node2, node1);

    printf("\n# End data in each node:\n");
    for (uint32_t i = 0; i < hashgraph.nodes_count; i++) {
        Node node = hashgraph.nodes[i];
        printf("%s\n", node.name);
        for (int j = 0; j < node.events_count; j++) {
            Event* event = &node.events[j];
            char* head_symbol = "┣";
            char* child_symbol = "┃";
            if (j == node.events_count - 1) {
                head_symbol = "┗";
                child_symbol = " ";
            }
            printf(" %s━ Event %d:\n", head_symbol, j);
            printf(" %s      Signature: %llu\n", child_symbol, (unsigned long long)event->sig);
            printf(" %s      Timestamp: %ld\n", child_symbol, event->timestamp);
            printf(" %s      Payload: %s\n", child_symbol, event->payload);
            printf(" %s      Self Parent: %llu\n", child_symbol, (unsigned long long)event->self_parent);
            printf(" %s      Other Parent: %llu\n", child_symbol, (unsigned long long)event->other_parent);
        }
        printf("\n");
    }

    return 0;
}

Output:

.shell
Created node: Node 1
- Added Event 'Genesises Event' to Node 'Node 1'
Created node: Node 2
- Added Event 'Genesises Event' to Node 'Node 2'
- Added Event 'Data 1' to Node 'Node 1'
- Added Event 'Data 2' to Node 'Node 1'
- Added Event 'Received gossip' to Node 'Node 2'
+ Node 1 gossiped to Node 2
- Added Event 'hello 2' to Node 'Node 2'
- Added Event 'Received gossip' to Node 'Node 1'
+ Node 2 gossiped to Node 1

# End data in each node:

Node 1
 ┣━ Event 0:
 ┃      Signature: 78809854168950575
 ┃      Timestamp: 1742162985
 ┃      Payload: Genesises Event
 ┃      Self Parent: 0
 ┃      Other Parent: 0
 ┣━ Event 1:
 ┃      Signature: 10450772725946122193
 ┃      Timestamp: 1742162985
 ┃      Payload: Data 1
 ┃      Self Parent: 818364513241060300
 ┃      Other Parent: 0
 ┣━ Event 2:
 ┃      Signature: 18203920123613473163
 ┃      Timestamp: 1742162985
 ┃      Payload: Data 2
 ┃      Self Parent: 17721513724882639522
 ┃      Other Parent: 0
 ┗━ Event 3:
        Signature: 3117094728498925458
        Timestamp: 1742162985
        Payload: Received gossip
        Self Parent: 11123344966174739451
        Other Parent: 9047142429543685273

Node 2
 ┣━ Event 0:
 ┃      Signature: 13421062288126506167
 ┃      Timestamp: 1742162985
 ┃      Payload: Genesises Event
 ┃      Self Parent: 0
 ┃      Other Parent: 0
 ┣━ Event 1:
 ┃      Signature: 5178182153957979585
 ┃      Timestamp: 1742162985
 ┃      Payload: Received gossip
 ┃      Self Parent: 5323264896040275293
 ┃      Other Parent: 11123344966174739451
 ┗━ Event 2:
        Signature: 4818854448937497713
        Timestamp: 1742162985
        Payload: hello 2
        Self Parent: 1617674139949262145
        Other Parent: 0

Resources

Table of Content