Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
4540602
Create cmake-multi-platform.yml
adozenlines May 10, 2025
e7060e1
Delete .github/workflows/cmake-multi-platform.yml
adozenlines May 10, 2025
b89733e
macOS support removes the use of auto -> char *
adozenlines May 21, 2025
3eecfb6
macOS support static declaration of portable_mutex_timedlock
adozenlines May 21, 2025
c0bbf8d
Add pthreads includes to linux cross-platform io header
adozenlines May 21, 2025
c3a8b0c
Updates the .gitignore file
adozenlines May 21, 2025
967f7bb
Delete cmake-build-debug directory
adozenlines May 21, 2025
eae901c
Delete .idea directory
adozenlines May 21, 2025
50c5417
Local ide changes
adozenlines May 21, 2025
a295bec
pthread include file location
adozenlines May 21, 2025
a9b7180
pthread include file location
adozenlines May 21, 2025
a077ff7
Merge remote-tracking branch 'origin/master_libevent' into master_lib…
adozenlines May 21, 2025
ba5718e
nullptr -> NULL
adozenlines May 21, 2025
c450a32
Adds support for status page refresh
adozenlines May 21, 2025
741c819
Delete cmake-build-debug directory
adozenlines May 21, 2025
32d9d43
Delete .idea directory
adozenlines May 21, 2025
4f61ce3
Update .gitignore
adozenlines May 21, 2025
d922697
[STRATUM] Code cleanup, branching optimizations and redundant return …
adozenlines May 21, 2025
95d4ba9
[STRATUM] Code cleanup, branching optimizations and redundant return …
adozenlines May 21, 2025
f065e70
[STRATUM] Code cleanup, branching optimizations and redundant return …
adozenlines May 21, 2025
104ba04
[PORTABILITY] Adds detection for BSD platforms into the build process.
adozenlines May 22, 2025
54d9770
[PORTABILITY] Adds detection for BSD platforms into the build process.
adozenlines May 22, 2025
d3f2af9
[PORTABILITY] Adds detection for BSD platforms into the build process.
adozenlines May 22, 2025
1a035bb
[PORTABILITY] Adds detection for BSD platforms into the build process.
adozenlines May 22, 2025
b6f0d04
[PORTABILITY] dead code: break statements will never be called after …
adozenlines May 22, 2025
196db32
[PORTABILITY] Align CMake version with github
adozenlines May 22, 2025
f50d021
[PORTABILITY] Align CMake version-lowered expectations
adozenlines May 22, 2025
ea90064
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
dd1551d
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
45fab7a
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
4088c8d
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
b0abf0d
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
c21cd9c
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
fe2c9ad
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
f111000
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
fc9b0ea
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
c114726
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
91b2de6
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
7e96b53
[PORTABILITY] Align CMake version-lowered expectations 3.11
adozenlines May 22, 2025
c2d975b
Merge pull request #1 from adozenlines/master_libevent
adozenlines Oct 14, 2025
dc5aa74
Merge branch 'ocean_master'
adozenlines Oct 14, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
CMakeCache.txt
CMakeFiles
Makefile
/Testing/
/.idea/
/cmake-build-debug/
/cmake-build-release/
cmake_install.cmake

git_version.h
web_resources.h
workspace.xml
.ninja_deps
.ninja_log
datum_gateway

*.conf
*.json
!doc/example_datum_gateway_config.json
Expand Down
19 changes: 18 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.11)

project(DATUM VERSION 0.4.0 LANGUAGES C)

Expand All @@ -17,6 +17,22 @@ option(ENABLE_API "Build API support." ON)

include(GNUInstallDirs)

if(CMAKE_SYSTEM_NAME STREQUAL "Darwin")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't CMake already provide platform detection without you having to define them here explicitly?

add_definitions(-D__APPLE__)
set(PLATFORM_SOURCES src/platform_macos.c)

elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR
CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR
CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR
CMAKE_SYSTEM_NAME STREQUAL "DragonFly")
add_definitions(-D__BSD__)
set(PLATFORM_SOURCES src/platform_bsd.c)

elseif(UNIX)
add_definitions(-D__LINUX__)
set(PLATFORM_SOURCES src/platform_linux.c)
endif()

add_executable(datum_gateway
src/datum_blocktemplates.c
src/datum_coinbaser.c
Expand All @@ -37,6 +53,7 @@ add_executable(datum_gateway
src/thirdparty_base58.c
src/thirdparty_segwit_addr.c
${CMAKE_CURRENT_BINARY_DIR}/web_resources.h
src/datum_cross_platform_io.h
)
install(TARGETS datum_gateway DESTINATION bin)

Expand Down
27 changes: 24 additions & 3 deletions src/datum_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ void datum_api_var_DATUM_MINER_TAG(char *buffer, size_t buffer_size, const T_DAT
buffer[i+1] = '"';
buffer[i+2] = 0;
}
void datum_api_var_PAGE_REFRESH_TIMED_OUT(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) {
if (datum_config.datum_page_refresh_seconds != 0)
{
snprintf(buffer, buffer_size, "%d", datum_config.datum_page_refresh_seconds);
}
}
void datum_api_var_DATUM_POOL_DIFF(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) {
snprintf(buffer, buffer_size, "%llu", (unsigned long long)datum_config.override_vardiff_min);
}
Expand Down Expand Up @@ -256,7 +262,9 @@ DATUM_API_VarEntry var_entries[] = {
{"STRATUM_JOB_WEIGHT", datum_api_var_STRATUM_JOB_WEIGHT},
{"STRATUM_JOB_SIGOPS", datum_api_var_STRATUM_JOB_SIGOPS},
{"STRATUM_JOB_TXNCOUNT", datum_api_var_STRATUM_JOB_TXNCOUNT},


{"PAGE_REFRESH_TIMED_OUT", datum_api_var_PAGE_REFRESH_TIMED_OUT},

{NULL, NULL} // Mark the end of the array
};

Expand Down Expand Up @@ -955,7 +963,10 @@ size_t datum_api_fill_config_var(const char *var_start, const size_t var_name_le
const size_t var_name_len_2 = var_end - var_start_2;
const char * const underscore_pos = memchr(var_start_2, '_', var_name_len_2);
int val;
if (var_name_len_2 == 3 && 0 == strncmp(var_start_2, "*ro", 3)) {
if (var_name_len_2 == 16 && 0 == strncmp(var_start_2, "time_out_in_secs", 16))
{

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing else-if chaining could cause unnecessary string comparisons.

val = datum_config.datum_page_refresh_seconds;
} else if (var_name_len_2 == 3 && 0 == strncmp(var_start_2, "*ro", 3)) {
val = !(datum_config.api_modify_conf && datum_config.api_admin_password_len);
if (!colon_pos) {
var_start = "readonly:";
Expand Down Expand Up @@ -1147,7 +1158,17 @@ struct datum_api_config_set_status {
// If anything fails (including validation), errors is appended and false is returned
bool datum_api_config_set(const char * const key, const char * const val, struct datum_api_config_set_status * const status) {
json_t * const errors = status->errors;
if (0 == strcmp(key, "mining_pool_address")) {
if (0 == strcmp(key, "time_out_in_secs")) {
const int val_int = datum_atoi_strict(val, strlen(val));
if (val_int == datum_config.datum_page_refresh_seconds) return true;
if (val_int < 0) {
json_t * const j = json_pack("{s:i}", key, val_int);
json_array_append_new(errors, j);
return false;
}
datum_config.datum_page_refresh_seconds = val_int;
datum_api_json_modify_new("page_refresh", key, json_integer(val_int));
} else if (0 == strcmp(key, "mining_pool_address")) {
if (0 == strcmp(val, datum_config.mining_pool_address)) return true;
unsigned char dummy[64];
if (!addr_2_output_script(val, &dummy[0], 64)) {
Expand Down
4 changes: 4 additions & 0 deletions src/datum_conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,10 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = {
.required = false, .ptr = &datum_config.datum_pooled_mining_only, .default_bool = true },
{ .var_type = DATUM_CONF_INT, .category = "datum", .name = "protocol_global_timeout", .description = "If no valid messages are received from the DATUM server in this many seconds, give up and try to reconnect",
.required = false, .ptr = &datum_config.datum_protocol_global_timeout, .default_int = 60 },

// status page refresh
{ .var_type = DATUM_CONF_INT, .category = "page_refresh", .name = "time_out_in_secs", .description = "Refresh status page interval",
.required = false, .ptr = &datum_config.datum_page_refresh_seconds, .default_int = 5 },
};

#define NUM_CONFIG_ITEMS (sizeof(datum_config_options) / sizeof(datum_config_options[0]))
Expand Down
4 changes: 3 additions & 1 deletion src/datum_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,9 @@ typedef struct {
bool clog_to_stderr;
bool clog_rotate_daily;
char clog_file[1024];


int datum_page_refresh_seconds;

char datum_pool_host[1024];
int datum_pool_port;
bool datum_pool_pass_workers;
Expand Down
107 changes: 107 additions & 0 deletions src/datum_cross_platform_io.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/* datum_cross_platform_io.h - Cross-platform I/O abstraction */

#ifndef DATUM_CROSS_PLATFORM_IO_H
#define DATUM_CROSS_PLATFORM_IO_H

#if defined(__APPLE__) || defined(__BSD__)
#include <sys/event.h>
#include <sys/time.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#define IO_HANDLE int
#define IO_EVENT_READ EVFILT_READ
#define IO_EVENT_ERROR EVFILT_EXCEPT
#define IO_MAX_EVENTS 32

static inline int datum_io_create() {
return kqueue();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing a return value check for kqueue()

}

static inline int datum_io_add(IO_HANDLE kq, uintptr_t fd, struct kevent *evSet) {
evSet->ident = fd;
EV_SET(evSet, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
return kevent(kq, evSet, 1, NULL, 0, NULL);
}

static inline int datum_io_delete(IO_HANDLE kq, uintptr_t fd, struct kevent *evSet)
{
if (evSet) {
evSet->ident = fd;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For datum_io_delete and datum_io_modify, what happens if the parameter is NULL? Seems like this just sets it without checking...

}
EV_SET(evSet, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
return kevent(kq, evSet, 1, NULL, 0, NULL);
}

static inline int datum_io_modify(IO_HANDLE kq, uintptr_t fd, struct kevent *evSet)
{
if (evSet) {
evSet->ident = fd;
}
EV_SET(evSet, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL);
return kevent(kq, evSet, 1, NULL, 0, NULL);
}

static inline int datum_io_wait(IO_HANDLE kq, struct kevent* events, int max_events, int timeout_ms) {
struct timespec ts = {
.tv_sec = timeout_ms / 1000,
.tv_nsec = (timeout_ms % 1000) * 1000000
};
return kevent(kq, NULL, 0, events, max_events, &ts);
}

static inline int portable_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout) {
while (nanosleep(timeout, NULL) == -1 && errno == EINTR) continue;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this use of portable_mutex_timedlock() will work for Apple/BSD since this is using a sleep loop instead of proper timeout handling.

return pthread_mutex_trylock(mutex);
}

#elif defined(__linux__)
#include <sys/epoll.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <pthread.h>
#define IO_HANDLE int
#define IO_EVENT_READ EPOLLIN
#define IO_EVENT_ERROR (EPOLLERR | EPOLLHUP)
#define IO_MAX_EVENTS 32

static inline int datum_io_create(int flags) {
return epoll_create1(flags);
}

static inline int datum_io_add(IO_HANDLE epfd, uintptr_t fd, struct epoll_event *ev) {
if (ev) {
ev->events = EPOLLIN | EPOLLERR | EPOLLHUP;
ev->data.fd = fd;
}
return epoll_ctl(epfd, EPOLL_CTL_ADD, fd, ev);
}

static inline int datum_io_delete(IO_HANDLE epfd, uintptr_t fd, struct epoll_event *ev) {
return epoll_ctl(epfd, EPOLL_CTL_DEL, fd, ev);
}

static inline int datum_io_modify(IO_HANDLE epfd, uintptr_t fd, struct epoll_event *ev) {
return epoll_ctl(epfd, EPOLL_CTL_MOD, fd, ev);
}

static inline int datum_io_wait(IO_HANDLE epfd, struct epoll_event* events, int max_events, int timeout_ms) {
return epoll_wait(epfd, events, max_events, timeout_ms);
}

static inline int portable_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout) {
return pthread_mutex_timedlock(mutex, timeout);
}

#else
#error Platform not supported
#endif

#endif // DATUM_CROSS_PLATFORM_IO_H

71 changes: 51 additions & 20 deletions src/datum_protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,17 @@
*/

// DATUM Client protocol implementation
// Encrypted and on the wire has 7.999 bits of entropy per byte in testing. completely uncompressable.
// Encrypted and on the wire has 7.999 bits of entropy per byte in testing. completely uncompressed.

// TODO: Clean this up and break up various functions
// TODO: Generalize encryption related operations vs repeated code
// TODO: Implement versioning on the protocol for feature lists
// TODO: Add pool-side assistance with startup to ensure that the client's node is fully sync'd with the network
// TODO: Add pool-side assistance with startup to ensure that the client's node is fully synced with the network
// TODO: Optionally allow pool to suggest node peers
// TODO: Implement graceful negotiation of chain forks
// TODO: Implement preciousblock for pool blocks not found by the client
// TODO: Implement precious block for pool blocks not found by the client
// TODO: Handle network failures that aren't immediately obvious more gracefully (like not receiving responses to server commands)
// TODO: Implement resuiming of work without allowing one client to cause duplicate work for another
// TODO: Implement resuming of work without allowing one client to cause duplicate work for another

#include <sodium.h>
#include <stdatomic.h>
Expand All @@ -58,7 +58,13 @@
#include <netdb.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include "datum_cross_platform_io.h"
#if defined(__APPLE__) || defined(__BSD__)
#include <sys/event.h> // macOS uses kqueue instead of epoll
#include <mach/mach_time.h>
#else
#include <sys/epoll.h> // Linux-specific
#endif
#include <time.h>
#include <sys/time.h>
#include <pthread.h>
Expand Down Expand Up @@ -294,7 +300,7 @@ int datum_protocol_coinbaser_fetch_response(int len, unsigned char *data) {
return 0;
}

rc = pthread_mutex_timedlock(&datum_protocol_coinbaser_fetch_mutex, &ts);
rc = portable_mutex_timedlock(&datum_protocol_coinbaser_fetch_mutex, &ts);
if (rc != 0) {
DLOG_DEBUG("Could not get a lock on the coinbaser reception mutex after 5 seconds... bug?");
return 0;
Expand Down Expand Up @@ -1467,7 +1473,12 @@ void *datum_protocol_client(void *args) {
int sockfd = -1;
int epollfd, nfds;
int flag = 1;
#ifdef __linux__
struct epoll_event ev, events[MAX_DATUM_CLIENT_EVENTS];
#elif defined(__APPLE__) || defined(__BSD__)
struct kevent ev, events[MAX_DATUM_CLIENT_EVENTS];
#endif

struct timeval start, now;
int ret,i,n;
datum_protocol_client_active = 1;
Expand Down Expand Up @@ -1580,23 +1591,26 @@ void *datum_protocol_client(void *args) {
}

// Set up epoll
if ((epollfd = epoll_create1(0)) == -1) {
#ifdef __linux__
if ((epollfd = datum_io_create(0)) == -1) {
#elif defined(__APPLE__) || defined(__BSD__)
if ((epollfd = datum_io_create()) == -1) {
#endif

DLOG_FATAL("epoll_create1(...) error: %s",strerror(errno));
close(sockfd);
datum_protocol_client_active = 0;
return NULL;
}

ev.events = EPOLLIN | EPOLLERR | EPOLLHUP;
ev.data.fd = sockfd;

if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) {

if (datum_io_add(epollfd, sockfd, &ev) == -1) {
DLOG_FATAL("epoll_ctl(...) error: %s",strerror(errno));
close(sockfd);
close(epollfd);
datum_protocol_client_active = 0;
return NULL;
}

i = 0;
datum_last_accepted_share_tsms = 0;
datum_last_accepted_share_local_tsms = 0;
Expand Down Expand Up @@ -1687,9 +1701,14 @@ void *datum_protocol_client(void *args) {
}

if (break_again) break;

nfds = epoll_wait(epollfd, events, MAX_DATUM_CLIENT_EVENTS, 5); // Wait for 5ms


#ifdef __linux__
int wait_ms = 5;
#elif defined(__APPLE__) || defined(__BSD__)
int wait_ms = 5000;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is there a massive difference between Linux and Apple/BSD behavior for wait_ms?

#endif
nfds = datum_io_wait(epollfd, events, MAX_DATUM_CLIENT_EVENTS, wait_ms); // Wait for 5ms

if (nfds == -1 && errno != EINTR) {
DLOG_FATAL("epoll_wait(...) error: %s",strerror(errno));
break;
Expand All @@ -1698,12 +1717,20 @@ void *datum_protocol_client(void *args) {
if (nfds <= 0) {
continue; // Timeout, nothing happened
}
#ifdef __linux__
if (events[0].events & (EPOLLERR | EPOLLHUP)) {
#elif defined(__APPLE__) || defined(__BSD__)
if (events[0].flags & EVFILT_EXCEPT) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be using EV_ERROR?

#endif

int err = 0;
socklen_t errlen = sizeof(err);

if (getsockopt(events[0].data.fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == 0) {
#ifdef __linux__
int fd = events[0].data.fd;
#elif defined(__APPLE__) || defined(__BSD__)
int fd = events[0].ident;
#endif
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == 0) {
if (err != 0) {
DLOG_ERROR("Socket error: %s", strerror(err));
} else {
Expand All @@ -1714,8 +1741,12 @@ void *datum_protocol_client(void *args) {
}
break;
}


#ifdef __linux__
if (events[0].events & EPOLLIN) {
#elif defined(__APPLE__) || defined(__BSD__)
if (events[0].flags & EV_ADD) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be using EVFILT_READ?

#endif
// data to receive
break_again = false;
// Receive the header, followed by any data specified by the header
Expand Down Expand Up @@ -1958,4 +1989,4 @@ int datum_encrypt_generate_keys(DATUM_ENC_KEYS *keys) {
keys->is_remote = false;

return 0;
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing newline at EOF?

Loading
Loading