Bring wire protocol into full YAW/2 spec compliance
Five interop issues fixed against PROTOCOL.md:
- Join signature now covers nonce_raw || net_ascii (64-char UTF-8 hex
string) as specified in §5.1, not net_raw_bytes. Both anchor server
and client updated to match.
- Chat wire fields renamed to spec names: text/ts (Unix ms int64)
replacing body/sent_at (ISO timestamp). Flat layout on PeerMessage
matches §8 exactly; store and TUI updated accordingly.
- Direct messages now use the spec "pm" type (flat {type,mid,text,ts})
instead of chat+to. Receiver reconstructs a ChatMessage with
dm:<short-id> room for IPC/storage. §8 compliant.
- File transfer message types changed to spec hyphenated names:
file-offer, file-accept, file-cancel, file-done with spec field
names (name/size not filename/size_bytes). §9 compliant.
- DataChannel open-race (§14 gotcha #3) fixed with sync.Once: doOpen
fires on OnOpen callback or immediately if the channel is already
open when WireDataChannel is called (answerer race).
Also fixes two bugs found during testing:
- mid was missing from outgoing wire messages, causing all received
messages to arrive with mid="" and collide on the UNIQUE DB
constraint. mid is now included on all sent chat/pm messages; a
random mid is generated for any received message that omits it.
- Test scripts hardened: kill -9 + active lsof polling replaces blind
sleep for port cleanup; join_network sent before peer_field queries
(local_peer is now network-scoped and nil until joined).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -57,10 +57,11 @@ func (s *Store) Close() error {
|
||||
// SaveMessage persists a chat message. Duplicate mids are silently ignored
|
||||
// (INSERT OR IGNORE), so calling this more than once is safe.
|
||||
func (s *Store) SaveMessage(msg *proto.ChatMessage) error {
|
||||
sentAt := time.UnixMilli(msg.Ts).UTC()
|
||||
_, err := s.db.Exec(
|
||||
`INSERT OR IGNORE INTO messages (mid, room, from_peer, body, sent_at)
|
||||
VALUES (?, ?, ?, ?, ?)`,
|
||||
msg.Mid, msg.Room, string(msg.From), msg.Body, msg.SentAt.UTC(),
|
||||
msg.Mid, msg.Room, string(msg.From), msg.Text, sentAt,
|
||||
)
|
||||
return err
|
||||
}
|
||||
@@ -96,12 +97,12 @@ func (s *Store) RecentMessages(room string, limit int) ([]proto.ChatMessage, err
|
||||
var m proto.ChatMessage
|
||||
var from string
|
||||
var sentAt time.Time
|
||||
if err := rows.Scan(&m.Mid, &from, &m.Body, &sentAt); err != nil {
|
||||
if err := rows.Scan(&m.Mid, &from, &m.Text, &sentAt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.From = proto.PeerID(from)
|
||||
m.Room = room
|
||||
m.SentAt = sentAt
|
||||
m.Ts = sentAt.UnixMilli()
|
||||
msgs = append(msgs, m)
|
||||
}
|
||||
// Reverse so oldest-first.
|
||||
|
||||
@@ -16,11 +16,11 @@ func TestRoundTrip(t *testing.T) {
|
||||
defer st.Close()
|
||||
|
||||
msg := &proto.ChatMessage{
|
||||
Mid: "aabbccdd00112233",
|
||||
From: proto.PeerID("deadbeef"),
|
||||
Room: "general",
|
||||
Body: "hello world",
|
||||
SentAt: time.Now().UTC().Truncate(time.Second),
|
||||
Mid: "aabbccdd00112233",
|
||||
From: proto.PeerID("deadbeef"),
|
||||
Room: "general",
|
||||
Text: "hello world",
|
||||
Ts: time.Now().UTC().Truncate(time.Second).UnixMilli(),
|
||||
}
|
||||
|
||||
if err := st.SaveMessage(msg); err != nil {
|
||||
@@ -40,7 +40,7 @@ func TestRoundTrip(t *testing.T) {
|
||||
t.Fatalf("got %d messages, want 1", len(msgs))
|
||||
}
|
||||
got := msgs[0]
|
||||
if got.Mid != msg.Mid || got.Body != msg.Body || got.Room != msg.Room {
|
||||
if got.Mid != msg.Mid || got.Text != msg.Text || got.Room != msg.Room {
|
||||
t.Fatalf("message mismatch: %+v", got)
|
||||
}
|
||||
}
|
||||
@@ -79,11 +79,11 @@ func TestRecentMessagesOrdering(t *testing.T) {
|
||||
base := time.Now().UTC().Truncate(time.Second)
|
||||
for i := range 5 {
|
||||
st.SaveMessage(&proto.ChatMessage{
|
||||
Mid: string(rune('a'+i)) + "000000000000000",
|
||||
From: "deadbeef",
|
||||
Room: "general",
|
||||
Body: string(rune('a' + i)),
|
||||
SentAt: base.Add(time.Duration(i) * time.Second),
|
||||
Mid: string(rune('a'+i)) + "000000000000000",
|
||||
From: "deadbeef",
|
||||
Room: "general",
|
||||
Text: string(rune('a' + i)),
|
||||
Ts: base.Add(time.Duration(i) * time.Second).UnixMilli(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ func TestRecentMessagesOrdering(t *testing.T) {
|
||||
}
|
||||
// Must be oldest-first.
|
||||
for i := 1; i < len(msgs); i++ {
|
||||
if msgs[i].SentAt.Before(msgs[i-1].SentAt) {
|
||||
if msgs[i].Ts < msgs[i-1].Ts {
|
||||
t.Fatalf("messages not in ascending order at index %d", i)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user