Skip to content

Commit fe57461

Browse files
committed
feat: implement deterministic message hash logic
1 parent 76d1d4f commit fe57461

7 files changed

+123
-3
lines changed

.release-please-manifest.json

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
"packages/utils": "0.0.4",
33
"packages/proto": "0.0.4",
44
"packages/interfaces": "0.0.11",
5+
"packages/message-hash": "0.1.0",
56
"packages/enr": "0.0.10",
67
"packages/peer-exchange": "0.0.9",
78
"packages/core": "0.0.16",

.size-limit.cjs

+5
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,9 @@ module.exports = [
4343
path: "packages/core/bundle/index.js",
4444
import: "{ wakuStore }",
4545
},
46+
{
47+
name: "Deterministic Message Hashing",
48+
path: "packages/message-hash/bundle/index.js",
49+
import: "{ messageHash }",
50+
},
4651
];

package-lock.json

+9-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/message-hash/package.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@waku/message-hash",
3-
"version": "0.0.10",
3+
"version": "0.1.0",
44
"description": "TypeScript implementation of the Deterministic Message Hashing as specified in 14/WAKU2-MESSAGE",
55
"types": "./dist/index.d.ts",
66
"module": "./dist/index.js",
@@ -51,6 +51,10 @@
5151
"engines": {
5252
"node": ">=16"
5353
},
54+
"dependencies": {
55+
"@noble/hashes": "^1.2.0",
56+
"@waku/utils": "*"
57+
},
5458
"devDependencies": {
5559
"@rollup/plugin-commonjs": "^24.0.1",
5660
"@rollup/plugin-json": "^6.0.0",
@@ -61,6 +65,7 @@
6165
"@typescript-eslint/eslint-plugin": "^5.54.1",
6266
"@typescript-eslint/parser": "^5.51.0",
6367
"@waku/build-utils": "*",
68+
"@waku/interfaces": "*",
6469
"chai": "^4.3.7",
6570
"cspell": "^6.28.0",
6671
"eslint": "^8.35.0",
+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import type { IProtoMessage } from "@waku/interfaces";
2+
import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
3+
import { expect } from "chai";
4+
5+
import { messageHash } from "./index.js";
6+
7+
// https://rfc.vac.dev/spec/14/#test-vectors
8+
describe("RFC Test Vectors", () => {
9+
it("Waku message hash computation", () => {
10+
const expectedHash =
11+
"4fdde1099c9f77f6dae8147b6b3179aba1fc8e14a7bf35203fc253ee479f135f";
12+
13+
const pubSubTopic = "/waku/2/default-waku/proto";
14+
const message: IProtoMessage = {
15+
payload: hexToBytes("0x010203045445535405060708"),
16+
contentTopic: "/waku/2/default-content/proto",
17+
meta: hexToBytes("0x73757065722d736563726574"),
18+
ephemeral: undefined,
19+
rateLimitProof: undefined,
20+
timestamp: undefined,
21+
version: undefined,
22+
};
23+
24+
const hash = messageHash(pubSubTopic, message);
25+
26+
expect(bytesToHex(hash)).to.equal(expectedHash);
27+
});
28+
29+
it("Waku message hash computation (meta attribute not present)", () => {
30+
const expectedHash =
31+
"87619d05e563521d9126749b45bd4cc2430df0607e77e23572d874ed9c1aaa62";
32+
33+
const pubSubTopic = "/waku/2/default-waku/proto";
34+
const message: IProtoMessage = {
35+
payload: hexToBytes("0x010203045445535405060708"),
36+
contentTopic: "/waku/2/default-content/proto",
37+
meta: undefined,
38+
ephemeral: undefined,
39+
rateLimitProof: undefined,
40+
timestamp: undefined,
41+
version: undefined,
42+
};
43+
44+
const hash = messageHash(pubSubTopic, message);
45+
46+
expect(bytesToHex(hash)).to.equal(expectedHash);
47+
});
48+
49+
it("Waku message hash computation (payload length 0)", () => {
50+
const expectedHash =
51+
"e1a9596237dbe2cc8aaf4b838c46a7052df6bc0d42ba214b998a8bfdbe8487d6";
52+
53+
const pubSubTopic = "/waku/2/default-waku/proto";
54+
const message: IProtoMessage = {
55+
payload: new Uint8Array(),
56+
contentTopic: "/waku/2/default-content/proto",
57+
meta: hexToBytes("0x73757065722d736563726574"),
58+
ephemeral: undefined,
59+
rateLimitProof: undefined,
60+
timestamp: undefined,
61+
version: undefined,
62+
};
63+
64+
const hash = messageHash(pubSubTopic, message);
65+
66+
expect(bytesToHex(hash)).to.equal(expectedHash);
67+
});
68+
});

packages/message-hash/src/index.ts

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { sha256 } from "@noble/hashes/sha256";
2+
import type { IProtoMessage } from "@waku/interfaces";
3+
import { concat, utf8ToBytes } from "@waku/utils/bytes";
4+
5+
/**
6+
* Deterministic Message Hashing as defined in
7+
* [14/WAKU2-MESSAGE](https://rfc.vac.dev/spec/14/#deterministic-message-hashing)
8+
*/
9+
export function messageHash(
10+
pubsubTopic: string,
11+
message: IProtoMessage
12+
): Uint8Array {
13+
const pubsubTopicBytes = utf8ToBytes(pubsubTopic);
14+
const contentTopicBytes = utf8ToBytes(message.contentTopic);
15+
16+
let bytesToHash;
17+
18+
if (message.meta) {
19+
bytesToHash = concat([
20+
pubsubTopicBytes,
21+
message.payload,
22+
contentTopicBytes,
23+
message.meta,
24+
]);
25+
} else {
26+
bytesToHash = concat([
27+
pubsubTopicBytes,
28+
message.payload,
29+
contentTopicBytes,
30+
]);
31+
}
32+
return sha256(bytesToHash);
33+
}

release-please-config.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
"packages/utils": {},
1010
"packages/proto": {},
1111
"packages/interfaces": {},
12+
"packages/message-hash": {},
1213
"packages/enr": {},
1314
"packages/peer-exchange": {},
14-
"packages/message-hash": {},
1515
"packages/core": {},
1616
"packages/dns-discovery": {},
1717
"packages/message-encryption": {},

0 commit comments

Comments
 (0)