feat(transport): add TestAnnounceHopCount to validate hop count registration and update path handling logic

This commit is contained in:
2026-01-02 15:42:21 -06:00
parent b972d87e91
commit c26c50cc3a
2 changed files with 89 additions and 9 deletions

View File

@@ -577,8 +577,11 @@ func (t *Transport) RequestPath(destinationHash []byte, onInterface string, tag
pathRequestData = append(destinationHash, tag...) pathRequestData = append(destinationHash, tag...)
} }
destHashFull := sha256.Sum256([]byte("rnstransport.path.request")) pathRequestName := "rnstransport.path.request"
pathRequestDestHash := destHashFull[:common.SIZE_16] nameHashFull := sha256.Sum256([]byte(pathRequestName))
nameHash10 := nameHashFull[:10]
finalHashFull := sha256.Sum256(nameHash10)
pathRequestDestHash := finalHashFull[:16]
pkt := packet.NewPacket( pkt := packet.NewPacket(
packet.DestinationPlain, packet.DestinationPlain,
@@ -586,11 +589,12 @@ func (t *Transport) RequestPath(destinationHash []byte, onInterface string, tag
0x00, 0x00,
0x00, 0x00,
packet.PropagationBroadcast, packet.PropagationBroadcast,
0x01, 0x00, // Header Type 1
pathRequestDestHash, nil,
false, false,
0x00, 0x00,
) )
pkt.DestinationHash = pathRequestDestHash
if err := pkt.Pack(); err != nil { if err := pkt.Pack(); err != nil {
return fmt.Errorf("failed to pack path request: %w", err) return fmt.Errorf("failed to pack path request: %w", err)
@@ -1110,16 +1114,15 @@ func (t *Transport) handleAnnouncePacket(data []byte, iface common.NetworkInterf
// Register the path from this announce // Register the path from this announce
// The destination is reachable via the interface that received this announce // The destination is reachable via the interface that received this announce
if iface != nil { if iface != nil {
// Use unlocked version since we may be called in a locked context
t.mutex.Lock() t.mutex.Lock()
t.updatePathUnlocked(destinationHash, nil, iface.GetName(), hopCount) t.updatePathUnlocked(destinationHash, nil, iface.GetName(), hopCount+1)
t.mutex.Unlock() t.mutex.Unlock()
debug.Log(debug.DEBUG_INFO, "Registered path", "hash", fmt.Sprintf("%x", destinationHash), "interface", iface.GetName(), "hops", hopCount) debug.Log(debug.DEBUG_INFO, "Registered path", "hash", fmt.Sprintf("%x", destinationHash), "interface", iface.GetName(), "hops", hopCount+1)
} }
// Notify handlers first, regardless of forwarding limits // Notify handlers first, regardless of forwarding limits
debug.Log(debug.DEBUG_INFO, "Notifying announce handlers", "destHash", fmt.Sprintf("%x", destinationHash), "appDataLen", len(appData)) debug.Log(debug.DEBUG_INFO, "Notifying announce handlers", "destHash", fmt.Sprintf("%x", destinationHash), "appDataLen", len(appData))
t.notifyAnnounceHandlers(destinationHash, id, appData, hopCount) t.notifyAnnounceHandlers(destinationHash, id, appData, hopCount+1)
debug.Log(debug.DEBUG_INFO, "Announce handlers notified") debug.Log(debug.DEBUG_INFO, "Announce handlers notified")
// Don't forward if max hops reached // Don't forward if max hops reached
@@ -1376,7 +1379,7 @@ func (t *Transport) InitializePathRequestHandler() error {
return errors.New("transport identity not initialized") return errors.New("transport identity not initialized")
} }
pathRequestDest, err := destination.New(t.transportIdentity, destination.IN, destination.PLAIN, "rnstransport", t, "path", "request") pathRequestDest, err := destination.New(nil, destination.IN, destination.PLAIN, "rnstransport", t, "path", "request")
if err != nil { if err != nil {
return fmt.Errorf("failed to create path request destination: %w", err) return fmt.Errorf("failed to create path request destination: %w", err)
} }
@@ -1692,6 +1695,14 @@ func (l *Link) HandleResource(resource interface{}) bool {
} }
} }
// SetIdentity sets the identity for the Transport.
func (t *Transport) SetIdentity(id *identity.Identity) {
t.mutex.Lock()
defer t.mutex.Unlock()
t.transportIdentity = id
}
// Start initializes the Transport.
func (t *Transport) Start() error { func (t *Transport) Start() error {
t.mutex.Lock() t.mutex.Lock()
defer t.mutex.Unlock() defer t.mutex.Unlock()

View File

@@ -3,8 +3,12 @@ package transport
import ( import (
"bytes" "bytes"
"testing" "testing"
"time"
"git.quad4.io/Networks/Reticulum-Go/pkg/common" "git.quad4.io/Networks/Reticulum-Go/pkg/common"
"git.quad4.io/Networks/Reticulum-Go/pkg/destination"
"git.quad4.io/Networks/Reticulum-Go/pkg/identity"
"git.quad4.io/Networks/Reticulum-Go/pkg/packet"
) )
type mockInterface struct { type mockInterface struct {
@@ -118,3 +122,68 @@ func TestTransportStatus(t *testing.T) {
t.Error("Path should be responsive again") t.Error("Path should be responsive again")
} }
} }
func TestAnnounceHopCount(t *testing.T) {
config := common.DefaultConfig()
tr := NewTransport(config)
defer tr.Close()
iface := &mockInterface{}
iface.Name = "wasm0"
iface.Enabled = true
_ = tr.RegisterInterface("wasm0", iface)
// Create an identity for the announce
id, _ := identity.New()
// Create a destination to get a valid hash for this identity
// NewAnnouncePacket uses "reticulum-go.node" by default
dest, _ := destination.New(id, destination.IN, destination.SINGLE, "reticulum-go.node", tr)
destHash := dest.GetHash()
// Create a raw announce packet manually to control hop count
// Header(2) + DestHash(16) + Context(1) + Payload...
// Header: 0x21 (Announce, Header Type 1, Broadcast, Destination Type Single)
// Hop count: 0
raw := make([]byte, 2+16+1+148) // header + dest + context + min_announce_payload
raw[0] = 0x21
raw[1] = 0 // Initial hop count
copy(raw[2:18], destHash)
raw[18] = 0 // context
// Announce payload: pubKey(64) + nameHash(10) + randomHash(10) + signature(64)
payload := raw[19:]
copy(payload[0:64], id.GetPublicKey())
// Name hash, random hash, signature - filling with dummy data but valid length
// Normally we would sign it properly, but handleAnnouncePacket validates it.
// Actually, handleAnnouncePacket WILL fail if signature is invalid.
// Use NewAnnouncePacket to get a valid signed packet
transportID := make([]byte, 16)
annPkt, err := packet.NewAnnouncePacket(destHash, id, []byte("test"), transportID)
if err != nil {
t.Fatalf("NewAnnouncePacket failed: %v", err)
}
annRaw, err := annPkt.Serialize()
if err != nil {
t.Fatalf("Serialize failed: %v", err)
}
// Override hop count to 0 to simulate neighbor
annRaw[1] = 0
// Handle the packet
tr.HandlePacket(annRaw, iface)
// Wait a bit for the async processing
time.Sleep(100 * time.Millisecond)
// Check stored hops
if !tr.HasPath(destHash) {
t.Fatal("Path not registered from announce")
}
hops := tr.HopsTo(destHash)
if hops != 1 {
t.Errorf("Expected 1 hop for neighbor (received 0), got %d", hops)
}
}