From 73d4f197467fe3e58fc49e3264f73e00a79004f9 Mon Sep 17 00:00:00 2001 From: Danish Arora <35004822+danisharora099@users.noreply.github.com> Date: Mon, 29 Apr 2024 15:35:23 +0530 Subject: [PATCH] fix(message-hash): account for `timestamp` (#1986) * fix: message-hash algo + tests * rm: only * chore: move numberToBytes to utils package * chore: shorten implementation --- packages/message-hash/src/index.spec.ts | 40 +++++++++++++++---------- packages/message-hash/src/index.ts | 27 ++++++++++------- packages/utils/src/bytes/index.ts | 13 ++++++++ 3 files changed, 55 insertions(+), 25 deletions(-) diff --git a/packages/message-hash/src/index.spec.ts b/packages/message-hash/src/index.spec.ts index 11b32dad78..e650dfbdff 100644 --- a/packages/message-hash/src/index.spec.ts +++ b/packages/message-hash/src/index.spec.ts @@ -6,63 +6,73 @@ import { messageHash } from "./index.js"; // https://rfc.vac.dev/spec/14/#test-vectors describe("RFC Test Vectors", () => { - it("Waku message hash computation", () => { + it("Waku message hash computation (meta size of 12 bytes)", () => { const expectedHash = - "4fdde1099c9f77f6dae8147b6b3179aba1fc8e14a7bf35203fc253ee479f135f"; - + "64cce733fed134e83da02b02c6f689814872b1a0ac97ea56b76095c3c72bfe05"; const pubsubTopic = "/waku/2/default-waku/proto"; const message: IProtoMessage = { payload: hexToBytes("0x010203045445535405060708"), contentTopic: "/waku/2/default-content/proto", meta: hexToBytes("0x73757065722d736563726574"), + timestamp: BigInt("0x175789bfa23f8400"), ephemeral: undefined, rateLimitProof: undefined, - timestamp: undefined, version: undefined }; - const hash = messageHash(pubsubTopic, message); + expect(bytesToHex(hash)).to.equal(expectedHash); + }); + it("Waku message hash computation (meta size of 64 bytes)", () => { + const expectedHash = + "7158b6498753313368b9af8f6e0a0a05104f68f972981da42a43bc53fb0c1b27"; + const pubsubTopic = "/waku/2/default-waku/proto"; + const message: IProtoMessage = { + payload: hexToBytes("0x010203045445535405060708"), + contentTopic: "/waku/2/default-content/proto", + meta: hexToBytes( + "0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f" + ), + timestamp: BigInt("0x175789bfa23f8400"), + ephemeral: undefined, + rateLimitProof: undefined, + version: undefined + }; + const hash = messageHash(pubsubTopic, message); expect(bytesToHex(hash)).to.equal(expectedHash); }); it("Waku message hash computation (meta attribute not present)", () => { const expectedHash = - "87619d05e563521d9126749b45bd4cc2430df0607e77e23572d874ed9c1aaa62"; - + "a2554498b31f5bcdfcbf7fa58ad1c2d45f0254f3f8110a85588ec3cf10720fd8"; const pubsubTopic = "/waku/2/default-waku/proto"; const message: IProtoMessage = { payload: hexToBytes("0x010203045445535405060708"), contentTopic: "/waku/2/default-content/proto", meta: undefined, + timestamp: BigInt("0x175789bfa23f8400"), ephemeral: undefined, rateLimitProof: undefined, - timestamp: undefined, version: undefined }; - const hash = messageHash(pubsubTopic, message); - expect(bytesToHex(hash)).to.equal(expectedHash); }); it("Waku message hash computation (payload length 0)", () => { const expectedHash = - "e1a9596237dbe2cc8aaf4b838c46a7052df6bc0d42ba214b998a8bfdbe8487d6"; - + "483ea950cb63f9b9d6926b262bb36194d3f40a0463ce8446228350bd44e96de4"; const pubsubTopic = "/waku/2/default-waku/proto"; const message: IProtoMessage = { payload: new Uint8Array(), contentTopic: "/waku/2/default-content/proto", meta: hexToBytes("0x73757065722d736563726574"), + timestamp: BigInt("0x175789bfa23f8400"), ephemeral: undefined, rateLimitProof: undefined, - timestamp: undefined, version: undefined }; - const hash = messageHash(pubsubTopic, message); - expect(bytesToHex(hash)).to.equal(expectedHash); }); }); diff --git a/packages/message-hash/src/index.ts b/packages/message-hash/src/index.ts index 4bf9842e19..37f9c80c60 100644 --- a/packages/message-hash/src/index.ts +++ b/packages/message-hash/src/index.ts @@ -1,6 +1,12 @@ import { sha256 } from "@noble/hashes/sha256"; import type { IProtoMessage } from "@waku/interfaces"; -import { bytesToUtf8, concat, utf8ToBytes } from "@waku/utils/bytes"; +import { isDefined } from "@waku/utils"; +import { + bytesToUtf8, + concat, + numberToBytes, + utf8ToBytes +} from "@waku/utils/bytes"; /** * Deterministic Message Hashing as defined in @@ -12,19 +18,20 @@ export function messageHash( ): Uint8Array { const pubsubTopicBytes = utf8ToBytes(pubsubTopic); const contentTopicBytes = utf8ToBytes(message.contentTopic); + const timestampBytes = message.timestamp + ? numberToBytes(message.timestamp) + : undefined; - let bytes; - - if (message.meta) { - bytes = concat([ + const bytes = concat( + [ pubsubTopicBytes, message.payload, contentTopicBytes, - message.meta - ]); - } else { - bytes = concat([pubsubTopicBytes, message.payload, contentTopicBytes]); - } + message.meta, + timestampBytes + ].filter(isDefined) + ); + return sha256(bytes); } diff --git a/packages/utils/src/bytes/index.ts b/packages/utils/src/bytes/index.ts index 5c2048d413..74adbc7890 100644 --- a/packages/utils/src/bytes/index.ts +++ b/packages/utils/src/bytes/index.ts @@ -14,6 +14,19 @@ export function hexToBytes(hex: string | Uint8Array): Uint8Array { return hex; } +export function numberToBytes(value: number | bigint): Uint8Array { + const buffer = new ArrayBuffer(8); + const view = new DataView(buffer); + + if (typeof value === "number") { + view.setFloat64(0, value, false); + } else { + view.setBigInt64(0, value, false); + } + + return new Uint8Array(buffer); +} + /** * Convert byte array to hex string (no `0x` prefix). */