diff --git a/multicodec/code.py b/multicodec/code.py index b115701..fbf9838 100644 --- a/multicodec/code.py +++ b/multicodec/code.py @@ -9,7 +9,7 @@ from __future__ import annotations -from .constants import CODE_TABLE, CODECS, NAME_TABLE +from .constants import CODE_TABLE, CODECS, NAME_TABLE, TAG_TABLE # ReservedStart is the (inclusive) start of the reserved range of codes that # are safe to use for internal purposes. @@ -188,260 +188,8 @@ def tag(self) -> str: def _get_tag(code: int) -> str: - """Get the tag for a codec code (mirrors go-multicodec's Tag() method).""" - name = CODE_TABLE.get(code, "") - - # CID - if name in ("cidv1", "cidv2", "cidv3"): - return "cid" - - # Encryption - if name in ("aes-gcm-256",): - return "encryption" - - # Filecoin - if name in ("fil-commitment-unsealed", "fil-commitment-sealed"): - return "filecoin" - - # Hash (non-multihash) - if name in ( - "murmur3-x64-64", - "murmur3-32", - "murmur3-128", - "crc32", - "crc64-ecma", - "crc64-nvme", - "murmur3-x64-128", - "sha256a", - "xxh-32", - "xxh-64", - "xxh3-64", - "xxh3-128", - ): - return "hash" - - # Holochain - if "holochain" in name: - return "holochain" - - # IPLD - if name in ( - "cbor", - "raw", - "dag-pb", - "dag-cbor", - "libp2p-key", - "git-raw", - "torrent-info", - "torrent-file", - "blake3-hashseq", - "leofcoin-block", - "leofcoin-tx", - "leofcoin-pr", - "dag-jose", - "dag-cose", - "eth-block", - "eth-block-list", - "eth-tx-trie", - "eth-tx", - "eth-tx-receipt-trie", - "eth-tx-receipt", - "eth-state-trie", - "eth-account-snapshot", - "eth-storage-trie", - "eth-receipt-log-trie", - "eth-receipt-log", - "bitcoin-block", - "bitcoin-tx", - "bitcoin-witness-commitment", - "zcash-block", - "zcash-tx", - "stellar-block", - "stellar-tx", - "decred-block", - "decred-tx", - "dash-block", - "dash-tx", - "swarm-manifest", - "swarm-feed", - "beeson", - "dag-json", - "swhid-1-snp", - "json", - "rdfc-1", - "json-jcs", - ): - return "ipld" - - # Key - if any( - k in name - for k in ( - "-pub", - "-priv", - "secp256k1", - "bls12_381", - "x25519", - "ed25519", - "p256", - "p384", - "p521", - "ed448", - "x448", - "sr25519", - "rsa", - "sm2", - "mlkem", - "jwk", - ) - ): - return "key" - - # Libp2p - if name in ("libp2p-peer-record", "libp2p-relay-rsvp"): - return "libp2p" - - # Multiaddr - if name in ( - "ip4", - "tcp", - "dccp", - "ip6", - "ip6zone", - "ipcidr", - "dns", - "dns4", - "dns6", - "dnsaddr", - "sctp", - "udp", - "p2p-webrtc-star", - "p2p-webrtc-direct", - "p2p-stardust", - "p2p-circuit", - "udt", - "utp", - "unix", - "thread", - "p2p", - "https", - "onion", - "onion3", - "garlic64", - "garlic32", - "tls", - "sni", - "noise", - "shs", - "quic", - "quic-v1", - "webtransport", - "certhash", - "ws", - "wss", - "p2p-websocket-star", - "http", - "http-path", - "webrtc-direct", - "webrtc", - "plaintextv2", - "scion", - "memory", - ): - return "multiaddr" - - # Multiformat - if name in ("multicodec", "multihash", "multiaddr", "multibase", "varsig"): - return "multiformat" - - # Multihash - if any( - h in name - for h in ( - "identity", - "sha1", - "sha2-", - "sha3-", - "shake-", - "keccak-", - "blake2b-", - "blake2s-", - "blake3", - "dbl-sha2-", - "md4", - "md5", - "bmt", - "ripemd-", - "x11", - "kangarootwelve", - "sm3-", - "poseidon-", - "skein", - ) - ): - return "multihash" - - # Namespace - if name in ( - "path", - "lbry", - "streamid", - "ipld-ns", - "ipfs-ns", - "swarm-ns", - "ipns-ns", - "zeronet", - "dnslink", - "skynet-ns", - "arweave-ns", - "subspace-ns", - "kumandra-ns", - ): - return "namespace" - - # Serialization - if name in ( - "protobuf", - "rlp", - "bencode", - "messagepack", - "car", - "car-index-sorted", - "car-multihash-index-sorted", - "ipns-record", - "x509-certificate", - "ssz", - ): - return "serialization" - - # Transport - if name in ( - "transport-bitswap", - "transport-graphsync-filecoinv1", - "transport-ipfs-gateway-http", - ): - return "transport" - - # Varsig - if name in ( - "nonstandard-sig", - "es256k", - "bls12_381-g1-sig", - "bls12_381-g2-sig", - "eddsa", - "eip-191", - "es256", - "es384", - "es512", - "rs256", - ): - return "varsig" - - # Zeroxcert - if name in ("zeroxcert-imprint-256",): - return "zeroxcert" - - return "" + """Get the tag for a codec code from the generated TAG_TABLE.""" + return TAG_TABLE.get(code, "") # Cache for known codes list diff --git a/multicodec/constants.py b/multicodec/constants.py index 33217bf..ebc2771 100644 --- a/multicodec/constants.py +++ b/multicodec/constants.py @@ -3938,3 +3938,4 @@ NAME_TABLE: dict[str, int] = {name: cast(int, value["prefix"]) for name, value in CODECS.items()} CODE_TABLE: dict[int, str] = {cast(int, value["prefix"]): name for name, value in CODECS.items()} +TAG_TABLE: dict[int, str] = {cast(int, value["prefix"]): cast(str, value["tag"]) for name, value in CODECS.items()} diff --git a/newsfragments/37.bugfix.rst b/newsfragments/37.bugfix.rst new file mode 100644 index 0000000..558db21 --- /dev/null +++ b/newsfragments/37.bugfix.rst @@ -0,0 +1 @@ +Return the correct ``multikey``, ``multisig``, ``nonce``, ``shelter``, ``softhash``, and ``vlad`` tags from ``Code.tag()``. diff --git a/newsfragments/40.bugfix.rst b/newsfragments/40.bugfix.rst new file mode 100644 index 0000000..147baa1 --- /dev/null +++ b/newsfragments/40.bugfix.rst @@ -0,0 +1 @@ +Derive ``Code.tag()`` from the multicodec table so every codec returns its CSV tag. diff --git a/newsfragments/42.misc.rst b/newsfragments/42.misc.rst new file mode 100644 index 0000000..d163f84 --- /dev/null +++ b/newsfragments/42.misc.rst @@ -0,0 +1 @@ +Add tests verifying ``Code.tag()`` matches the multicodec table for every registered codec. diff --git a/tests/test_code.py b/tests/test_code.py index 8987c2c..ab43a0e 100644 --- a/tests/test_code.py +++ b/tests/test_code.py @@ -117,6 +117,25 @@ def test_code_tag(self): code = Code(0x55) # raw assert code.tag() == "ipld" + # Previously missing categories (issue #37) + assert Code(0xA000).tag() == "multikey" # chacha20-poly1305 + assert Code(0xD01300).tag() == "multisig" # es256k-msig + assert Code(0xD01301).tag() == "multisig" # bls12_381-g1-msig + assert Code(0x123B).tag() == "nonce" + assert Code(0x511E00).tag() == "shelter" # shelter-contract-manifest + assert Code(0xCC01).tag() == "softhash" # iscc + assert Code(0x1207).tag() == "vlad" + + # Unknown code + assert Code(0x999999).tag() == "" + + def test_code_tag_matches_codecs_for_all(self): + """Every registered codec's tag() matches CODECS metadata (issue #42).""" + from multicodec.constants import CODECS + + for name, info in CODECS.items(): + assert Code(info["prefix"]).tag() == info["tag"], name + def test_code_status(self): """Test Code.status property.""" # Permanent codec diff --git a/tests/test_constants_metadata.py b/tests/test_constants_metadata.py index 5da39b3..952b34d 100644 --- a/tests/test_constants_metadata.py +++ b/tests/test_constants_metadata.py @@ -1,6 +1,6 @@ """Smoke tests for enriched CODECS metadata from update-table.py.""" -from multicodec.constants import CODE_TABLE, CODECS, NAME_TABLE +from multicodec.constants import CODE_TABLE, CODECS, NAME_TABLE, TAG_TABLE def test_codecs_include_csv_metadata_keys(): @@ -22,3 +22,10 @@ def test_identity_preserves_description(): def test_name_and_code_tables_still_use_prefix(): assert NAME_TABLE["sha2-256"] == 0x12 assert CODE_TABLE[0x12] == "sha2-256" + + +def test_tag_table_matches_codecs(): + assert TAG_TABLE[0x12] == "multihash" + assert len(TAG_TABLE) == len(CODECS) + for name, info in CODECS.items(): + assert TAG_TABLE[info["prefix"]] == info["tag"], name diff --git a/tools/update-table.py b/tools/update-table.py index 49ee444..d9db6cf 100644 --- a/tools/update-table.py +++ b/tools/update-table.py @@ -29,6 +29,9 @@ CODE_TABLE: dict[int, str] = { cast(int, value["prefix"]): name for name, value in CODECS.items() } +TAG_TABLE: dict[int, str] = { + cast(int, value["prefix"]): cast(str, value["tag"]) for name, value in CODECS.items() +} """