feat(transport): add TestAnnounceHopCount to validate hop count registration and update path handling logic
This commit is contained in:
@@ -577,8 +577,11 @@ func (t *Transport) RequestPath(destinationHash []byte, onInterface string, tag
|
||||
pathRequestData = append(destinationHash, tag...)
|
||||
}
|
||||
|
||||
destHashFull := sha256.Sum256([]byte("rnstransport.path.request"))
|
||||
pathRequestDestHash := destHashFull[:common.SIZE_16]
|
||||
pathRequestName := "rnstransport.path.request"
|
||||
nameHashFull := sha256.Sum256([]byte(pathRequestName))
|
||||
nameHash10 := nameHashFull[:10]
|
||||
finalHashFull := sha256.Sum256(nameHash10)
|
||||
pathRequestDestHash := finalHashFull[:16]
|
||||
|
||||
pkt := packet.NewPacket(
|
||||
packet.DestinationPlain,
|
||||
@@ -586,11 +589,12 @@ func (t *Transport) RequestPath(destinationHash []byte, onInterface string, tag
|
||||
0x00,
|
||||
0x00,
|
||||
packet.PropagationBroadcast,
|
||||
0x01,
|
||||
pathRequestDestHash,
|
||||
0x00, // Header Type 1
|
||||
nil,
|
||||
false,
|
||||
0x00,
|
||||
)
|
||||
pkt.DestinationHash = pathRequestDestHash
|
||||
|
||||
if err := pkt.Pack(); err != nil {
|
||||
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
|
||||
// The destination is reachable via the interface that received this announce
|
||||
if iface != nil {
|
||||
// Use unlocked version since we may be called in a locked context
|
||||
t.mutex.Lock()
|
||||
t.updatePathUnlocked(destinationHash, nil, iface.GetName(), hopCount)
|
||||
t.updatePathUnlocked(destinationHash, nil, iface.GetName(), hopCount+1)
|
||||
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
|
||||
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")
|
||||
|
||||
// Don't forward if max hops reached
|
||||
@@ -1376,7 +1379,7 @@ func (t *Transport) InitializePathRequestHandler() error {
|
||||
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 {
|
||||
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 {
|
||||
t.mutex.Lock()
|
||||
defer t.mutex.Unlock()
|
||||
|
||||
@@ -3,8 +3,12 @@ package transport
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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 {
|
||||
@@ -118,3 +122,68 @@ func TestTransportStatus(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user