feat: add TODOs for path table persistence and improve packet handling logic in transport

This commit is contained in:
2025-12-29 23:27:32 -06:00
parent 474bb2ff33
commit e3d65525b8

View File

@@ -160,7 +160,7 @@ type Path struct {
func NewTransport(cfg *common.ReticulumConfig) *Transport { func NewTransport(cfg *common.ReticulumConfig) *Transport {
t := &Transport{ t := &Transport{
interfaces: make(map[string]common.NetworkInterface), interfaces: make(map[string]common.NetworkInterface),
paths: make(map[string]*common.Path), paths: make(map[string]*common.Path), // TODO: Load persisted path table from storage/destination_table for faster startup
seenAnnounces: make(map[string]bool), seenAnnounces: make(map[string]bool),
announceRate: rate.NewLimiter(PROPAGATION_RATE, common.ONE), announceRate: rate.NewLimiter(PROPAGATION_RATE, common.ONE),
mutex: sync.RWMutex{}, mutex: sync.RWMutex{},
@@ -178,6 +178,13 @@ func NewTransport(cfg *common.ReticulumConfig) *Transport {
heldAnnounces: make(map[string]*PathAnnounceEntry), heldAnnounces: make(map[string]*PathAnnounceEntry),
} }
// TODO: Path table persistence - Python Reticulum persists the path table to disk at
// storage/destination_table containing [timestamp, received_from, hops, expires, random_blobs, interface, packet_hash]
// for each known destination. This allows faster startup by not needing to re-discover all paths.
// However, NOT persisting provides better privacy as paths are ephemeral and forgotten on restart.
// Decision: Keep paths in-memory only for now (better privacy). If persistence is needed in the future,
// implement loadPathTable() and persistPathTable() methods with periodic saves.
transportIdent, err := identity.LoadOrCreateTransportIdentity() transportIdent, err := identity.LoadOrCreateTransportIdentity()
if err == nil { if err == nil {
t.transportIdentity = transportIdent t.transportIdentity = transportIdent
@@ -1231,19 +1238,59 @@ func (t *Transport) handleTransportPacket(data []byte, iface common.NetworkInter
return return
} }
pkt := &packet.Packet{Raw: append([]byte{0x00}, data...)}
if err := pkt.Unpack(); err != nil {
debug.Log(debug.DEBUG_INFO, "Failed to unpack transport packet", "error", err)
return
}
headerByte := data[0] headerByte := data[0]
packetType := headerByte & 0x03 packetType := headerByte & 0x03
destType := (headerByte & 0x0C) >> 2 destType := (headerByte & 0x0C) >> 2
if packetType == packet.PacketTypeData && destType == DEST_TYPE_PLAIN { if packetType == packet.PacketTypeData {
if len(data) < 19 { if destType == DEST_TYPE_PLAIN {
return if len(data) < 19 {
return
}
context := data[18]
if context == packet.ContextPathResponse {
t.handlePathResponse(data[19:], iface)
return
}
} }
context := data[18] destHash := pkt.DestinationHash
if len(destHash) > 16 {
destHash = destHash[:16]
}
if context == packet.ContextPathResponse { debug.Log(debug.DEBUG_VERBOSE, "Looking up destination for data packet", "hash", fmt.Sprintf("%x", destHash))
t.handlePathResponse(data[19:], iface)
t.mutex.RLock()
destIface, exists := t.destinations[string(destHash)]
t.mutex.RUnlock()
if exists {
debug.Log(debug.DEBUG_INFO, "Routing data packet to destination", "hash", fmt.Sprintf("%x", destHash))
destValue := reflect.ValueOf(destIface)
if destValue.IsValid() && !destValue.IsNil() {
method := destValue.MethodByName("Receive")
if method.IsValid() {
args := []reflect.Value{
reflect.ValueOf(pkt),
reflect.ValueOf(iface),
}
method.Call(args)
} else {
debug.Log(debug.DEBUG_VERBOSE, "Destination does not have Receive method")
}
}
} else {
debug.Log(debug.DEBUG_VERBOSE, "No destination registered for hash", "hash", fmt.Sprintf("%x", destHash))
} }
} }
} }