2026-07-06 14:49:45 +01:00
|
|
|
// Command kernel is a small runnable check that the bindings work against the
|
2026-07-06 15:07:16 +01:00
|
|
|
// single liblogosdelivery library. It brings up two relay nodes, connects them,
|
|
|
|
|
// publishes a message from one, and confirms the other receives it — end-to-end
|
|
|
|
|
// send/receive over the unified lifecycle (logosdelivery_create_node/start/
|
|
|
|
|
// stop/destroy) and the Kernel relay ops (waku_relay_publish, plus the mesh the
|
|
|
|
|
// node forms for its configured shard).
|
2026-07-06 14:49:45 +01:00
|
|
|
//
|
|
|
|
|
// Build/run requires liblogosdelivery at link time, e.g.:
|
|
|
|
|
//
|
|
|
|
|
// export LOGOS_DELIVERY_DIR=/abs/path/to/logos-delivery # built with `make liblogosdelivery`
|
|
|
|
|
// export CGO_CFLAGS="-I$LOGOS_DELIVERY_DIR/library"
|
|
|
|
|
// export CGO_LDFLAGS="-L$LOGOS_DELIVERY_DIR/build -Wl,-rpath,$LOGOS_DELIVERY_DIR/build"
|
|
|
|
|
// go run ./examples/kernel
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
2026-07-06 15:07:16 +01:00
|
|
|
"context"
|
2026-07-06 14:49:45 +01:00
|
|
|
"log"
|
2026-07-06 15:07:16 +01:00
|
|
|
"time"
|
2026-07-06 14:49:45 +01:00
|
|
|
|
|
|
|
|
"github.com/logos-messaging/logos-delivery-go-bindings/pkg/kernel"
|
|
|
|
|
"github.com/logos-messaging/logos-delivery-go-bindings/pkg/kernel/common"
|
2026-07-06 15:07:16 +01:00
|
|
|
"github.com/logos-messaging/logos-delivery-go-bindings/pkg/kernel/pb"
|
|
|
|
|
"google.golang.org/protobuf/proto"
|
2026-07-06 14:49:45 +01:00
|
|
|
)
|
|
|
|
|
|
2026-07-06 15:07:16 +01:00
|
|
|
const (
|
|
|
|
|
clusterID = 16
|
|
|
|
|
shardID = 64
|
|
|
|
|
)
|
2026-07-06 14:49:45 +01:00
|
|
|
|
2026-07-06 15:07:16 +01:00
|
|
|
func newNode(name string) *kernel.WakuNode {
|
|
|
|
|
// A relay node on cluster 16, static shard 0 — the node auto-subscribes to
|
|
|
|
|
// its configured shard, so no explicit RelaySubscribe or discovery is needed.
|
|
|
|
|
node, err := kernel.StartWakuNode(name, &common.WakuConfig{
|
|
|
|
|
Relay: true,
|
|
|
|
|
LogLevel: "ERROR",
|
|
|
|
|
Discv5Discovery: false,
|
|
|
|
|
ClusterID: clusterID,
|
|
|
|
|
Shards: []uint16{shardID},
|
|
|
|
|
})
|
2026-07-06 14:49:45 +01:00
|
|
|
if err != nil {
|
2026-07-06 15:07:16 +01:00
|
|
|
log.Fatalf("start %s: %v", name, err)
|
2026-07-06 14:49:45 +01:00
|
|
|
}
|
2026-07-06 15:07:16 +01:00
|
|
|
return node
|
|
|
|
|
}
|
2026-07-06 14:49:45 +01:00
|
|
|
|
2026-07-06 15:07:16 +01:00
|
|
|
func stopAndDestroy(node *kernel.WakuNode) {
|
|
|
|
|
if err := node.StopAndDestroy(); err != nil {
|
|
|
|
|
log.Printf("stop/destroy: %v", err)
|
2026-07-06 14:49:45 +01:00
|
|
|
}
|
2026-07-06 15:07:16 +01:00
|
|
|
}
|
2026-07-06 14:49:45 +01:00
|
|
|
|
2026-07-06 15:07:16 +01:00
|
|
|
func main() {
|
|
|
|
|
sender := newNode("sender")
|
|
|
|
|
defer stopAndDestroy(sender)
|
|
|
|
|
receiver := newNode("receiver")
|
|
|
|
|
defer stopAndDestroy(receiver)
|
2026-07-06 14:49:45 +01:00
|
|
|
|
2026-07-06 15:07:16 +01:00
|
|
|
topic := kernel.FormatWakuRelayTopic(clusterID, shardID)
|
|
|
|
|
if err := sender.RelaySubscribe(topic); err != nil {
|
|
|
|
|
log.Fatalf("sender subscribe: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if err := receiver.RelaySubscribe(topic); err != nil {
|
|
|
|
|
log.Fatalf("receiver subscribe: %v", err)
|
2026-07-06 14:49:45 +01:00
|
|
|
}
|
|
|
|
|
|
2026-07-06 15:07:16 +01:00
|
|
|
// Dial the receiver from the sender using the receiver's listen multiaddr
|
|
|
|
|
// (it already embeds the peer id).
|
|
|
|
|
addrs, err := receiver.ListenAddresses()
|
|
|
|
|
if err != nil || len(addrs) == 0 {
|
|
|
|
|
log.Fatalf("receiver listen addresses: %v", err)
|
|
|
|
|
}
|
|
|
|
|
connCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
|
|
|
|
defer cancel()
|
|
|
|
|
if err := sender.Connect(connCtx, addrs[0]); err != nil {
|
|
|
|
|
log.Fatalf("connect sender -> receiver: %v", err)
|
|
|
|
|
}
|
|
|
|
|
// The dial is asynchronous; wait for the peer to actually connect.
|
|
|
|
|
for i := 0; ; i++ {
|
|
|
|
|
if n, _ := sender.GetNumConnectedPeers(); n >= 1 {
|
|
|
|
|
log.Printf("nodes connected (%d peer)", n)
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
if i >= 15 {
|
|
|
|
|
log.Fatal("no peer connected after 15s")
|
|
|
|
|
}
|
|
|
|
|
time.Sleep(1 * time.Second)
|
2026-07-06 14:49:45 +01:00
|
|
|
}
|
|
|
|
|
|
2026-07-06 15:07:16 +01:00
|
|
|
payload := []byte("hello over relay")
|
|
|
|
|
publish := func() {
|
|
|
|
|
msg := &pb.WakuMessage{
|
|
|
|
|
Payload: payload,
|
|
|
|
|
ContentTopic: "/kernel-example/1/relay/proto",
|
|
|
|
|
Version: proto.Uint32(0),
|
|
|
|
|
Timestamp: proto.Int64(time.Now().UnixNano()),
|
|
|
|
|
}
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
|
|
|
defer cancel()
|
|
|
|
|
if hash, err := sender.RelayPublish(ctx, msg, topic); err != nil {
|
|
|
|
|
log.Printf("publish (retrying): %v", err)
|
|
|
|
|
} else {
|
|
|
|
|
log.Printf("sender published, hash: %s", hash.String())
|
|
|
|
|
}
|
2026-07-06 14:49:45 +01:00
|
|
|
}
|
|
|
|
|
|
2026-07-06 15:07:16 +01:00
|
|
|
// Publish immediately, then retry each second — gossipsub needs a moment to
|
|
|
|
|
// graft the mesh after the connection is established.
|
|
|
|
|
publish()
|
|
|
|
|
deadline := time.After(30 * time.Second)
|
|
|
|
|
ticker := time.NewTicker(1 * time.Second)
|
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case env := <-receiver.MsgChan:
|
|
|
|
|
if got := env.Message().GetPayload(); string(got) == string(payload) {
|
|
|
|
|
log.Printf("receiver got %q on %s — send/receive OK over the single library", got, env.PubsubTopic())
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
case <-ticker.C:
|
|
|
|
|
publish()
|
|
|
|
|
case <-deadline:
|
|
|
|
|
log.Fatal("timed out waiting for the receiver to get the message")
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-06 14:49:45 +01:00
|
|
|
}
|