Three hashes of one message
A TON message travels as a bag of cells — a BoC — and three different hashes get called "the message hash" depending on who is talking. The TONNode mempool stream delivers all three for every pending message, under these names:
cell_hash— the representation hash of the root cell. This is what explorers call the message hash, and once the message is included in a block it equals thein_msghash of that transaction. It depends only on the cells, not on how they were serialised into bytes.norm_hash— the TEP-467 normalized hash: the hash of the same external-in message rewritten into a fixed form —ext_in_msg_info$10 src:addr_none dest:<unchanged> import_fee:0 init:nothing body:right$1 ^body. It stays stable when the message is re-serialised or when itssrc,import_feeorinitfields change. If the message already has that form,norm_hashequalscell_hash.sha256— SHA-256 of the BoC bytes exactly as delivered. The same cells can be serialised in several valid ways (with or without an index, with or without a CRC32C), and each serialisation has a different SHA-256 while the two cell hashes do not move.
The tool prints each one in hex and in base64, because APIs disagree: explorers and the liteserver protocol use hex, several HTTP APIs return base64.
Why the normalized hash exists
An external message is signed by a wallet and handed to the network by whoever relays it. On the way it can be re-encoded, and a relayer can add or strip the src address, the import_fee or an attached init — none of which the wallet signed. Each change gives a new root cell and a new cell_hash, so a node that de-duplicated by cell hash would accept the same signed payload several times. TEP-467 fixes the form that is hashed so that all those variants collapse into one identity, and the node's own get_ext_in_msg_hash_norm (in validator/impl/external-message.cpp) computes it that way.
The construction is small: take the destination bits exactly as they are, zero the source, zero the fee, drop the init, and put the body into a reference whether or not it was one. Only an external-in message has a normalized hash; for an internal or external-out message the tool shows the other two and says so.
What the mempool stream carries
Each msg frame of the TONNode mempool stream carries the boc itself, its size, and cell_hash, norm_hash and sha256 as defined above. De-duplicate your own handling by cell_hash: the same message can arrive twice, first as early (unverified) and then as checked, and again after a reconnect. sha256 is the key the node uses to de-duplicate the bytes it received, which is why it is hashed over the delivered bytes and not over a re-encoding.
Tested against real messages
The implementation here builds the normalized cell exactly the way the stream's own parser does, and the repository's test suite feeds 72 real mainnet external messages — the stream's fixture set, each with the cell_hash, norm_hash and sha256 the stream delivered — through it and asserts all three match. The example message from the mempool documentation is one of them; paste it to see the three values side by side.
