logos-messaging-go-bindings/pkg/kernel/relay_send_receive_test.go
Igor Sirotin 97b7601c41
refactor: build against the single liblogosdelivery library (#116)
* refactor: build against the single liblogosdelivery library

logos-delivery#4012 unifies the node lifecycle on logosdelivery_* and ships the
full API — Messaging, Reliable Channels, and the low-level Kernel (waku_*) tier
— in one liblogosdelivery library, retiring the separate libwaku. Migrate the
bindings onto it.

- internal/ffi: collapse the two bridges into one internal/ffi/liblogosdelivery
  over the single library. The kernel wrappers keep calling waku_* (kernel
  header), the lifecycle now uses logosdelivery_create_node/start_node/
  stop_node/destroy (waku_new/start/stop/destroy are gone), events use
  logosdelivery_set_event_callback. Include liblogosdelivery_kernel.h (which
  re-exports the stable header) and link -llogosdelivery. Remove
  internal/ffi/libwaku.
- pkg/kernel: repoint at internal/ffi/liblogosdelivery; the Makefile links
  -llogosdelivery.
- CI: build only `make liblogosdelivery`, headers from library/, single
  -llogosdelivery. Pinned to logos-delivery#4012 until it merges (see the
  TODO/LOGOS_DELIVERY_REF in pr.yml).

The high-level MessagingClient (pkg/messaging) lands in a follow-up PR on top.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(kernel): drop removed config keys + add a runnable example

Running against the single liblogosdelivery library surfaced a config drift:
the consolidated WakuNodeConf strictly rejects unknown keys, and
common.WakuConfig still emitted `legacyStore` (removed upstream, and sent
unconditionally) and `host` (renamed to `listenAddress`). That failed node
creation, so every kernel test that starts a node failed. Reconcile the config:

- drop the LegacyStore field (and its two obsolete test references; the
  already-skipped TestCheckLegacyStore keeps compiling);
- map Host to the `listenAddress` JSON key.

Add examples/kernel: a small runnable check that drives the unified lifecycle
(logosdelivery_create_node/start/stop/destroy) and a few kernel ops (version,
listen addresses, is-online, relay subscribe/unsubscribe) over the single
library. `go run ./examples/kernel` prints OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(kernel): correct RelayPublish wire format; verify send/receive

RelayPublish marshalled the generated protobuf (content_topic), but the library
requires the WakuMessage wire format (contentTopic) and rejected it with
"Missing required field: contentTopic". Marshal the wire shape explicitly.

Turn examples/kernel into an end-to-end send/receive check: two relay nodes on
cluster 16 / shard 64, connected via the listen multiaddr, one publishes and the
other receives it off MsgChan. Verified against a locally-built liblogosdelivery:
prints "send/receive OK".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ci: build against logos-delivery master (unpin #4012)

logos-delivery#4012 merged, so drop the LOGOS_DELIVERY_REF pin and clone the
default branch; the cache key tracks master's HEAD again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(ffi): flatten internal/ffi/liblogosdelivery into internal/ffi

With a single library there is no bridge to choose between, so the extra
subpackage adds nothing: move the bridge to internal/ffi (package ffi) and drop
the umbrella doc. pkg/kernel now imports internal/ffi and calls ffi.*.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test(kernel): add a two-node relay send/receive integration test

TestRelaySendReceive connects two relay nodes, publishes a random payload from
one and requires the other to receive it — end-to-end over the single library.
Compiled by the PR gate; run under `make test` / locally against a built
liblogosdelivery. (Replaces the former examples/kernel, removed alongside the
ffi flatten.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(ffi): keep two files in package ffi, one per header

The single library still ships two headers — liblogosdelivery.h (stable
Messaging/lifecycle) and liblogosdelivery_kernel.h (low-level waku_*). Mirror
that with two files in one package instead of one merged ffi.go: keep the
messaging bridge and all the shared plumbing (Handle/RetOK/lifecycle/callbacks/
call) in liblogosdelivery.go, and keep the kernel wrappers in libwaku.go, which
reuses the shared call + logosGoCallback. This is the same two files we had,
moved into package ffi — a much smaller diff than the merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Apply suggestions from code review

Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ivan FB <128452529+Ivansete-status@users.noreply.github.com>
2026-07-20 17:55:30 +01:00

91 lines
2.8 KiB
Go

package kernel
import (
"context"
"crypto/rand"
"testing"
"time"
"github.com/logos-messaging/logos-delivery-go-bindings/pkg/kernel/common"
"github.com/logos-messaging/logos-delivery-go-bindings/pkg/kernel/pb"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
// TestRelaySendReceive is an end-to-end check against the single liblogosdelivery
// library: two relay nodes are connected, one publishes a random payload and the
// other must receive it. It exercises the unified node lifecycle and the Kernel
// relay ops (waku_relay_subscribe/publish) over the one library.
func TestRelaySendReceive(t *testing.T) {
const clusterID, shardID = 16, 64
newNode := func(name string) *WakuNode {
node, err := StartWakuNode(name, &common.WakuConfig{
Relay: true,
LogLevel: "ERROR",
Discv5Discovery: false,
ClusterID: clusterID,
Shards: []uint16{shardID},
})
require.NoError(t, err)
t.Cleanup(func() { _ = node.StopAndDestroy() })
return node
}
sender := newNode("sender")
receiver := newNode("receiver")
topic := FormatWakuRelayTopic(clusterID, shardID)
require.NoError(t, sender.RelaySubscribe(topic))
require.NoError(t, receiver.RelaySubscribe(topic))
// Dial the receiver from the sender using the receiver's listen multiaddr
// (it already embeds the peer id).
addrs, err := receiver.ListenAddresses()
require.NoError(t, err)
require.NotEmpty(t, addrs)
connCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
require.NoError(t, sender.Connect(connCtx, addrs[0]))
require.Eventually(t, func() bool {
n, _ := sender.GetNumConnectedPeers()
return n >= 1
}, 15*time.Second, time.Second, "sender never connected to the receiver")
payload := make([]byte, 16)
_, err = rand.Read(payload)
require.NoError(t, err)
publish := func() {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
// May fail with NoPeersToPublish until gossipsub grafts the mesh; retried.
_, _ = sender.RelayPublish(ctx, &pb.WakuMessage{
Payload: payload,
ContentTopic: "/kernel-test/1/relay/proto",
Version: proto.Uint32(0),
Timestamp: proto.Int64(time.Now().UnixNano()),
}, topic)
}
// Publish immediately, then retry each second while waiting for delivery —
// the relay mesh takes a moment to form after the connection is established.
publish()
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
deadline := time.After(10 * time.Second)
for {
select {
case env := <-receiver.MsgChan:
if string(env.Message().GetPayload()) == string(payload) {
return // received our exact message — success
}
case <-ticker.C:
publish()
case <-deadline:
t.Fatal("timed out waiting for the receiver to get the message")
}
}
}