From 68705947c7191b3534d03ec91bf68378bebfb6c7 Mon Sep 17 00:00:00 2001 From: Glenn Gore Date: Wed, 16 Sep 2026 15:45:33 +0200 Subject: [PATCH] fix(tsp-js): raise MAX_HOPS to 64 so longer routes interoperate Signed-off-by: Glenn Gore --- packages/tsp-js/CHANGELOG.md | 3 +++ packages/tsp-js/src/cesr/wire.ts | 7 +++++-- packages/tsp-js/tests/message.routed.mjs | 11 +++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/packages/tsp-js/CHANGELOG.md b/packages/tsp-js/CHANGELOG.md index 4e38074..2aa569f 100644 --- a/packages/tsp-js/CHANGELOG.md +++ b/packages/tsp-js/CHANGELOG.md @@ -17,6 +17,9 @@ For history before this file, see `git log` on `packages/tsp-js`. ### Fixed +- `MAX_HOPS` is 64 (was 10). The specification sets no maximum, and 12-hop + routes packed by every other implementation were refused on decode. The same + bound applies when packing a route and when decoding a hop list or reply path. - An XSCS/XCTL body that is not exactly one Bytes primitive is refused. It was read as its first primitive, silently dropping the rest of the `-A##` stream, and data after the stream was ignored. See diff --git a/packages/tsp-js/src/cesr/wire.ts b/packages/tsp-js/src/cesr/wire.ts index cd0981d..1004b5b 100644 --- a/packages/tsp-js/src/cesr/wire.ts +++ b/packages/tsp-js/src/cesr/wire.ts @@ -388,8 +388,11 @@ export function decodeVariableData( return stream.slice(range.begin, range.end); } -/** Max hops accepted in a routed message's hop list (bounds a hostile count). */ -export const MAX_HOPS = 10; +/** Max hops accepted in a routed message's hop list or reply path (bounds a + * hostile count). The spec sets no maximum, so this is a local choice that + * caps interoperability: 64 matches the other affinidi TSP implementations. + * It was 10, which refused 12-hop routes every other implementation opens. */ +export const MAX_HOPS = 64; /** Read the `YTSP` genus marker and its version count code. Advances `cur`. * diff --git a/packages/tsp-js/tests/message.routed.mjs b/packages/tsp-js/tests/message.routed.mjs index 8530ddb..71eed4f 100644 --- a/packages/tsp-js/tests/message.routed.mjs +++ b/packages/tsp-js/tests/message.routed.mjs @@ -48,6 +48,17 @@ test("packRouted rejects empty and over-long routes", async () => { await assert.rejects(packRouted(enc.encode("x"), tooMany, alice.vid, hop1.vid, packKeys(alice, hop1))); }); +test("a route at MAX_HOPS packs and opens with its hops intact", async () => { + // 12 hops was refused when the limit was 10. + const alice = party("did:web:alice"); + const hop1 = party("did:web:hop1"); + const route = Array.from({ length: MAX_HOPS }, (_, i) => `did:web:h${i}`); + const packed = await packRouted(enc.encode("abc"), route, alice.vid, hop1.vid, packKeys(alice, hop1)); + const opened = await unpack(packed.bytes, unpackKeys(hop1, alice)); + assert.equal(opened.messageType, "routed"); + assert.deepEqual(opened.hops, route); +}); + test("routed multi-hop round-trip: alice → hop1 → hop2 → final (inner opaque)", async () => { const alice = party("did:web:alice"); const hop1 = party("did:web:hop1");