Multihash Interface ExampleΒΆ
This example focuses on hash function choice and CID outcomes:
hash the same payload with
sha2-256,build a CID from the resulting multihash,
build a second CID using the JS-example-compatible
sha3-512-style path,compare digest lengths and CID strings to documented JS expected values.
Run it with:
python -m examples.multihash_interface.multihash_interface
python -m examples.multihash_interface.multihash_interface --json
Expected result:
printed digest lengths for both hash paths,
printed CIDs,
booleans indicating whether output matches the JS reference comments.
1"""Equivalent of js-multiformats/examples/multihash-interface.js."""
2
3import argparse
4import hashlib
5import json
6
7import multihash
8from cid import make_cid
9
10JS_EXPECTED = {
11 "sha2_256_digest_len": 32,
12 "sha2_256_cid_base32": "bagaaierasords4njcts6vs7qvdjfcvgnume4hqohf65zsfguprqphs3icwea",
13 "sha3_512_digest_len": 64,
14 "sha3_512_cid_base32": (
15 "bagaaifca7d5wrebdi6rmqkgtrqyodq3bo6gitrqtemxtliymakwswbazbu7ai763747ljp7ycqfv7aqx4xlgiugcx62quo2te45pcgjbg4qjsvq"
16 ),
17}
18
19
20def _js_style_sha3_512_multihash(payload: bytes) -> bytes:
21 """Reproduce the JS example's explicit code=0x14 behavior.
22
23 The upstream JS example labels the second hasher as `sha3-512` (code `0x14`)
24 while using Node's `sha512` digest bytes. We mirror that encoded multihash
25 byte layout to compare with the documented JS comment output exactly.
26 """
27 digest = hashlib.sha512(payload).digest()
28 return bytes((0x14, len(digest))) + digest
29
30
31def build_report() -> dict[str, object]:
32 payload_obj = {"hello": "world"}
33 payload = json.dumps(payload_obj, separators=(",", ":"), sort_keys=True).encode("utf-8")
34
35 digest_256 = multihash.digest(payload, "sha2-256")
36 cid_256 = make_cid(1, "json", digest_256.encode())
37 cid_256_b32 = cid_256.encode("base32").decode("ascii")
38
39 js_style_512_mh = _js_style_sha3_512_multihash(payload)
40 cid_js_style_512 = make_cid(1, "json", js_style_512_mh)
41 cid_js_style_512_b32 = cid_js_style_512.encode("base32").decode("ascii")
42
43 matches = {
44 "sha2_256_digest_len": digest_256.length == JS_EXPECTED["sha2_256_digest_len"],
45 "sha2_256_cid_base32": cid_256_b32 == JS_EXPECTED["sha2_256_cid_base32"],
46 "sha3_512_digest_len": len(hashlib.sha512(payload).digest()) == JS_EXPECTED["sha3_512_digest_len"],
47 "sha3_512_cid_base32": cid_js_style_512_b32 == JS_EXPECTED["sha3_512_cid_base32"],
48 }
49
50 return {
51 "input": payload_obj,
52 "codec": "json",
53 "sha2_256": {
54 "digest_length": digest_256.length,
55 "cid_base32": cid_256_b32,
56 "cid_default_string": str(cid_256),
57 },
58 "sha3_512_js_style": {
59 "digest_length": len(hashlib.sha512(payload).digest()),
60 "multihash_code_hex": "0x14",
61 "cid_base32": cid_js_style_512_b32,
62 "cid_default_string": str(cid_js_style_512),
63 },
64 "js_expected": JS_EXPECTED,
65 "matches_js_comments": matches,
66 }
67
68
69def main() -> None:
70 parser = argparse.ArgumentParser(description="Multihash interface example")
71 parser.add_argument("--json", action="store_true", help="Print structured JSON report")
72 args = parser.parse_args()
73
74 report = build_report()
75 if args.json:
76 print(json.dumps(report, indent=2, sort_keys=True))
77 return
78
79 print(f"sha2-256 digest length: {report['sha2_256']['digest_length']} bytes")
80 print(f"sha2-256 CID: {report['sha2_256']['cid_default_string']}")
81 print(f"sha3-512 digest length: {report['sha3_512_js_style']['digest_length']} bytes")
82 print(f"sha3-512 CID: {report['sha3_512_js_style']['cid_default_string']}")
83 print(
84 "Matches js comments (sha2-256/sha3-512 CID): "
85 f"{report['matches_js_comments']['sha2_256_cid_base32']}/"
86 f"{report['matches_js_comments']['sha3_512_cid_base32']}"
87 )
88
89
90if __name__ == "__main__":
91 main()