Skip to content

Transaction and block ids ​

VIZ inherited its id types from Graphene (BitShares, then Steem). In the source they are declared as fc::ripemd160, and older documentation and libraries call them "ripemd160 ids". That is only the name of the 20-byte container they are stored in: neither id is a ripemd160 digest. The node declares them through the alias id160_type to say so.

ValueHow it is computedSize
transaction_idfirst 20 bytes of sha256(packed transaction), signatures not included20 bytes
block_idfirst 20 bytes of sha224(packed block header), then bytes 0–3 replaced by the block number (big-endian)20 bytes
transaction_merkle_rootripemd160 of the root of a sha256 tree over the signed transactions20 bytes

The merkle root is the one value that really is a ripemd160.

Consequences for clients ​

  • To compute a transaction id, serialize the transaction without signatures, take sha256 and keep the first 20 bytes. Do not run ripemd160 — the result would never match the node.
  • The block number can be read straight from a block id: it is the first 4 bytes, big-endian. ref_block_num / ref_block_prefix (TaPoS) use the low 16 bits of that number and bytes 4–7 of the block id.
  • The ids are 20 bytes in every API answer, in the block log and in account history. Changing the hash (to a real ripemd160 or to a full 32-byte sha256) would be a hardfork that splits history in two id formats, for no security gain: 160 bits is plenty for an identifier, and signatures are made over the full sha256 digest, not over the id.

Source: transaction::id() in libraries/protocol/transaction.cpp, signed_block_header::id() and signed_block::calculate_merkle_root() in libraries/protocol/block.cpp, the types in libraries/protocol/include/graphene/protocol/types.hpp.