package main /* #cgo LDFLAGS: -L../../build/ -lwaku -lnegentropy -Wl,--allow-multiple-definition #cgo LDFLAGS: -L../../ -Wl,-rpath,../../ #include "../../library/libwaku.h" #include #include extern void globalEventCallback(int ret, char* msg, size_t len, void* userData); typedef struct { int ret; char* msg; size_t len; } Resp; void* allocResp() { return calloc(1, sizeof(Resp)); } void freeResp(void* resp) { if (resp != NULL) { free(resp); } } char* getMyCharPtr(void* resp) { if (resp == NULL) { return NULL; } Resp* m = (Resp*) resp; return m->msg; } size_t getMyCharLen(void* resp) { if (resp == NULL) { return 0; } Resp* m = (Resp*) resp; return m->len; } int getRet(void* resp) { if (resp == NULL) { return 0; } Resp* m = (Resp*) resp; return m->ret; } // resp must be set != NULL in case interest on retrieving data from the callback void callback(int ret, char* msg, size_t len, void* resp) { if (resp != NULL) { Resp* m = (Resp*) resp; m->ret = ret; m->msg = msg; m->len = len; } } #define WAKU_CALL(call) \ do { \ int ret = call; \ if (ret != 0) { \ printf("Failed the call to: %s. Returned code: %d\n", #call, ret); \ exit(1); \ } \ } while (0) void* cGoWakuNew(const char* configJson, void* resp) { // We pass NULL because we are not interested in retrieving data from this callback return waku_new(configJson, (WakuCallBack) callback, resp); } void cGoWakuStart(void* ctx, void* resp) { WAKU_CALL(waku_start(ctx, (WakuCallBack) callback, resp)); } void cGoWakuStop(void* ctx, void* resp) { WAKU_CALL(waku_stop(ctx, (WakuCallBack) callback, resp)); } void cGoWakuDestroy(void* ctx, void* resp) { WAKU_CALL(waku_destroy(ctx, (WakuCallBack) callback, resp)); } void cGoWakuVersion(void* ctx, void* resp) { WAKU_CALL(waku_version(ctx, (WakuCallBack) callback, resp)); } void cGoWakuSetEventCallback(void* ctx) { // The 'globalEventCallback' Go function is shared amongst all possible WakuNode instances. // Given that the 'globalEventCallback' is shared, we pass again the // ctx instance but in this case is needed to pick up the correct method // that will handle the event. // In other words, for every call the libwaku makes to globalEventCallback, // the 'userData' parameter will bring the context of the node that registered // that globalEventCallback. // This technique is needed because cgo only allows to export Go functions and not methods. waku_set_event_callback(ctx, (WakuCallBack) globalEventCallback, ctx); } void cGoWakuContentTopic(void* ctx, char* appName, int appVersion, char* contentTopicName, char* encoding, void* resp) { WAKU_CALL( waku_content_topic(ctx, appName, appVersion, contentTopicName, encoding, (WakuCallBack) callback, resp) ); } void cGoWakuPubsubTopic(void* ctx, char* topicName, void* resp) { WAKU_CALL( waku_pubsub_topic(ctx, topicName, (WakuCallBack) callback, resp) ); } void cGoWakuDefaultPubsubTopic(void* ctx, void* resp) { WAKU_CALL (waku_default_pubsub_topic(ctx, (WakuCallBack) callback, resp)); } void cGoWakuRelayPublish(void* ctx, const char* pubSubTopic, const char* jsonWakuMessage, int timeoutMs, void* resp) { WAKU_CALL (waku_relay_publish(ctx, pubSubTopic, jsonWakuMessage, timeoutMs, (WakuCallBack) callback, resp)); } void cGoWakuRelaySubscribe(void* ctx, char* pubSubTopic, void* resp) { WAKU_CALL ( waku_relay_subscribe(ctx, pubSubTopic, (WakuCallBack) callback, resp) ); } void cGoWakuRelayUnsubscribe(void* ctx, char* pubSubTopic, void* resp) { WAKU_CALL ( waku_relay_unsubscribe(ctx, pubSubTopic, (WakuCallBack) callback, resp) ); } void cGoWakuConnect(void* ctx, char* peerMultiAddr, int timeoutMs, void* resp) { WAKU_CALL( waku_connect(ctx, peerMultiAddr, timeoutMs, (WakuCallBack) callback, resp) ); } void cGoWakuListenAddresses(void* ctx, void* resp) { WAKU_CALL (waku_listen_addresses(ctx, (WakuCallBack) callback, resp) ); } void cGoWakuGetMyENR(void* ctx, void* resp) { WAKU_CALL (waku_get_my_enr(ctx, (WakuCallBack) callback, resp) ); } */ import "C" import ( "encoding/json" "errors" "fmt" "os" "os/signal" "syscall" "unsafe" ) type WakuMessageHash = string type WakuPubsubTopic = string type WakuContentTopic = string type WakuConfig struct { Host string `json:"host,omitempty"` Port int `json:"port,omitempty"` NodeKey string `json:"key,omitempty"` EnableRelay bool `json:"relay"` LogLevel string `json:"logLevel"` } type WakuNode struct { ctx unsafe.Pointer } func WakuSetup() { C.waku_setup() } func WakuNew(config WakuConfig) (*WakuNode, error) { jsonConfig, err := json.Marshal(config) if err != nil { return nil, err } var cJsonConfig = C.CString(string(jsonConfig)) var resp = C.allocResp() defer C.free(unsafe.Pointer(cJsonConfig)) defer C.freeResp(resp) ctx := C.cGoWakuNew(cJsonConfig, resp) if C.getRet(resp) == C.RET_OK { return &WakuNode{ctx: ctx}, nil } errMsg := "error WakuNew: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return nil, errors.New(errMsg) } func (self *WakuNode) WakuStart() error { var resp = C.allocResp() defer C.freeResp(resp) C.cGoWakuStart(self.ctx, resp) if C.getRet(resp) == C.RET_OK { return nil } errMsg := "error WakuStart: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return errors.New(errMsg) } func (self *WakuNode) WakuStop() error { var resp = C.allocResp() defer C.freeResp(resp) C.cGoWakuStop(self.ctx, resp) if C.getRet(resp) == C.RET_OK { return nil } errMsg := "error WakuStop: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return errors.New(errMsg) } func (self *WakuNode) WakuDestroy() error { var resp = C.allocResp() defer C.freeResp(resp) C.cGoWakuDestroy(self.ctx, resp) if C.getRet(resp) == C.RET_OK { return nil } errMsg := "error WakuDestroy: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return errors.New(errMsg) } func (self *WakuNode) WakuVersion() (string, error) { var resp = C.allocResp() defer C.freeResp(resp) C.cGoWakuVersion(self.ctx, resp) if C.getRet(resp) == C.RET_OK { var version = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return version, nil } errMsg := "error WakuVersion: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return "", errors.New(errMsg) } //export globalEventCallback func globalEventCallback(callerRet C.int, msg *C.char, len C.size_t, userData unsafe.Pointer) { // This is shared among all Golang instances self := WakuNode{ctx: userData} self.MyEventCallback(callerRet, msg, len) } func (self *WakuNode) MyEventCallback(callerRet C.int, msg *C.char, len C.size_t) { fmt.Println("Event received:", C.GoStringN(msg, C.int(len))) } func (self *WakuNode) WakuSetEventCallback() { // Notice that the events for self node are handled by the 'MyEventCallback' method C.cGoWakuSetEventCallback(self.ctx) } func (self *WakuNode) FormatContentTopic( appName string, appVersion int, contentTopicName string, encoding string) (WakuContentTopic, error) { var cAppName = C.CString(appName) var cContentTopicName = C.CString(contentTopicName) var cEncoding = C.CString(encoding) var resp = C.allocResp() defer C.free(unsafe.Pointer(cAppName)) defer C.free(unsafe.Pointer(cContentTopicName)) defer C.free(unsafe.Pointer(cEncoding)) defer C.freeResp(resp) C.cGoWakuContentTopic(self.ctx, cAppName, C.int(appVersion), cContentTopicName, cEncoding, resp) if C.getRet(resp) == C.RET_OK { var contentTopic = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return contentTopic, nil } errMsg := "error FormatContentTopic: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return "", errors.New(errMsg) } func (self *WakuNode) FormatPubsubTopic(topicName string) (WakuPubsubTopic, error) { var cTopicName = C.CString(topicName) var resp = C.allocResp() defer C.free(unsafe.Pointer(cTopicName)) defer C.freeResp(resp) C.cGoWakuPubsubTopic(self.ctx, cTopicName, resp) if C.getRet(resp) == C.RET_OK { var pubsubTopic = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return pubsubTopic, nil } errMsg := "error FormatPubsubTopic: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return "", errors.New(errMsg) } func (self *WakuNode) WakuDefaultPubsubTopic() (WakuPubsubTopic, error) { var resp = C.allocResp() defer C.freeResp(resp) C.cGoWakuDefaultPubsubTopic(self.ctx, resp) if C.getRet(resp) == C.RET_OK { var defaultPubsubTopic = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return defaultPubsubTopic, nil } errMsg := "error WakuDefaultPubsubTopic: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return "", errors.New(errMsg) } func (self *WakuNode) WakuRelayPublish( pubsubTopic string, message string, timeoutMs int) (WakuMessageHash, error) { var cPubsubTopic = C.CString(pubsubTopic) var msg = C.CString(message) var resp = C.allocResp() defer C.freeResp(resp) defer C.free(unsafe.Pointer(cPubsubTopic)) defer C.free(unsafe.Pointer(msg)) C.cGoWakuRelayPublish(self.ctx, cPubsubTopic, msg, C.int(timeoutMs), resp) if C.getRet(resp) == C.RET_OK { msgHash := C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return msgHash, nil } errMsg := "error WakuRelayPublish: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return "", errors.New(errMsg) } func (self *WakuNode) WakuRelaySubscribe(pubsubTopic string) error { var resp = C.allocResp() var cPubsubTopic = C.CString(pubsubTopic) defer C.freeResp(resp) defer C.free(unsafe.Pointer(cPubsubTopic)) C.cGoWakuRelaySubscribe(self.ctx, cPubsubTopic, resp) if C.getRet(resp) == C.RET_OK { return nil } errMsg := "error WakuRelaySubscribe: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return errors.New(errMsg) } func (self *WakuNode) WakuRelayUnsubscribe(pubsubTopic string) error { var resp = C.allocResp() var cPubsubTopic = C.CString(pubsubTopic) defer C.freeResp(resp) defer C.free(unsafe.Pointer(cPubsubTopic)) C.cGoWakuRelayUnsubscribe(self.ctx, cPubsubTopic, resp) if C.getRet(resp) == C.RET_OK { return nil } errMsg := "error WakuRelayUnsubscribe: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return errors.New(errMsg) } func (self *WakuNode) WakuConnect(peerMultiAddr string, timeoutMs int) error { var resp = C.allocResp() var cPeerMultiAddr = C.CString(peerMultiAddr) defer C.freeResp(resp) defer C.free(unsafe.Pointer(cPeerMultiAddr)) C.cGoWakuConnect(self.ctx, cPeerMultiAddr, C.int(timeoutMs), resp) if C.getRet(resp) == C.RET_OK { return nil } errMsg := "error WakuConnect: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return errors.New(errMsg) } func (self *WakuNode) WakuListenAddresses() (string, error) { var resp = C.allocResp() defer C.freeResp(resp) C.cGoWakuListenAddresses(self.ctx, resp) if C.getRet(resp) == C.RET_OK { var listenAddresses = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return listenAddresses, nil } errMsg := "error WakuListenAddresses: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return "", errors.New(errMsg) } func (self *WakuNode) WakuGetMyENR() (string, error) { var resp = C.allocResp() defer C.freeResp(resp) C.cGoWakuGetMyENR(self.ctx, resp) if C.getRet(resp) == C.RET_OK { var myENR = C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return myENR, nil } errMsg := "error WakuGetMyENR: " + C.GoStringN(C.getMyCharPtr(resp), C.int(C.getMyCharLen(resp))) return "", errors.New(errMsg) } func main() { WakuSetup() config := WakuConfig{ Host: "0.0.0.0", Port: 30304, NodeKey: "11d0dcea28e86f81937a3bd1163473c7fbc0a0db54fd72914849bc47bdf78710", EnableRelay: true, LogLevel: "DEBUG", } node, err := WakuNew(config) if err != nil { fmt.Println("Error happened:", err.Error()) return } node.WakuSetEventCallback() defaultPubsubTopic, err := node.WakuDefaultPubsubTopic() if err != nil { fmt.Println("Error happened:", err.Error()) return } err = node.WakuRelaySubscribe(defaultPubsubTopic) if err != nil { fmt.Println("Error happened:", err.Error()) return } err = node.WakuConnect( // tries to connect to a localhost node with key: 0d714a1fada214dead6dc9c7274585eca0ff292451866e7d6d677dc818e8ccd2 "/ip4/0.0.0.0/tcp/60000/p2p/16Uiu2HAmVFXtAfSj4EiR7mL2KvL4EE2wztuQgUSBoj2Jx2KeXFLN", 10000) if err != nil { fmt.Println("Error happened:", err.Error()) return } err = node.WakuStart() if err != nil { fmt.Println("Error happened:", err.Error()) return } version, err := node.WakuVersion() if err != nil { fmt.Println("Error happened:", err.Error()) return } formattedContentTopic, err := node.FormatContentTopic("appName", 1, "cTopicName", "enc") if err != nil { fmt.Println("Error happened:", err.Error()) return } formattedPubsubTopic, err := node.FormatPubsubTopic("my-ctopic") if err != nil { fmt.Println("Error happened:", err.Error()) return } listenAddresses, err := node.WakuListenAddresses() if err != nil { fmt.Println("Error happened:", err.Error()) return } myENR, err := node.WakuGetMyENR() if err != nil { fmt.Println("Error happened:", err.Error()) return } fmt.Println("Version:", version) fmt.Println("Custom content topic:", formattedContentTopic) fmt.Println("Custom pubsub topic:", formattedPubsubTopic) fmt.Println("Default pubsub topic:", defaultPubsubTopic) fmt.Println("Listen addresses:", listenAddresses) fmt.Println("My ENR:", myENR) // Wait for a SIGINT or SIGTERM signal ch := make(chan os.Signal, 1) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) <-ch err = node.WakuStop() if err != nil { fmt.Println("Error happened:", err.Error()) return } err = node.WakuDestroy() if err != nil { fmt.Println("Error happened:", err.Error()) return } }