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:
- Very new protocol.
- Hashgraph is patented by Hedera, so most project don't want to get sued by company in future. This hasn’t happened yet, but we’ve already seen similar things with Java and Android.
Technical Info
Instead of using block like blockchain Hashgraph uses event.
Here what is contain inside event:
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:
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:
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
#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:
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