# Transactions Root Hash
transactionsRoot field in the block header contains the root hash of the Merkle tree of transactions of the block. The root hash is the proof that the block contains all the transactions in the proper order.
The transactions root hash in the block header has the following purposes:
- To prove the integrity of transactions in the block without presenting all transactions.
- To sign the block header only, separately from its transactions.
transactionsRootis enabled by feature #15 “Ride V4, VRF, Protobuf, Failed transactions”.
# transactionsRoot Calculation
The hash of each transaction in the block is calculated. For example:
HA = hash(TA)
HB = hash(TB)
Each pair of adjacent hashes is concatenated, and the hash is calculated for each resulting concatenation:
HAB = hash(HA + HB)
If the last hash does not have a pair, it is concatenated with the zero byte hash:
HGH = hash(HG + hash(0))
Step 2 is repeated until the root hash is obtained:
The root hash is written in the
If the block is empty, then
Waves blockchain uses BLAKE2b-256 hashing function.
# Proof of Transaction in Block
Let's suppose that side 1 stores the full blockchain data and side 2 stores the block headers only.
To prove that the block contains a given transaction, side 1 provides the following data:
T: Transaction to check.
merkleProofs: Array of sibling hashes of the Merkle tree, bottom-to-top.
index: Index of the transaction in the block.
For example, for the TD transaction:
merkleProofs= [ HC, HAB, HEFGH ]
Side 2 checks the proof:
It calculates the hash of the transaction being checked (all the transaction data is hashed, including the signature):
HD = hash(TD)
It concatenates the current hash with the corresponding hash of the
merkleProofsarray and calculates the hash of concatenation.
indexdetermines in which order to concatenate the hashes:
• If the
nth bit of
indexfrom the end is 0, then the order is: the current hash + the
nth hash of the
merkleProofsarray (proof hash is on the right). • If the
nth bit is 1, the order is: the
nth hash of the
merkleProofsarray + the current hash (proof hash is on the left).
index= 310 = 112 , thus:
merkleProofs = HC is on the left, •
merkleProofs = HAB is on the left, •
merkleProofs = HEFGH is on the right.
It repeates Step 2 until the root hash is obtained:
It compares the root hash obtained with the already known
transactionsRootfrom the block header. If the hashes match, then the transaction exists in the block.
The following Node API methods accept transaction IDs and provide the proof that the transaction is in a block for each transaction:
The methods are described in the Transaction article.
You can check a transaction on the same blockchain without using a root hash, since the Waves node stores the full blockchain data, including all transactions. Use the following built-in Ride function:
transactionHeightById(id: ByteVector): Int|Unit
The function returns the block height if the transaction with the specified
id exists. Otherwise, it returns
unit. See the function description in the Blockchain functions article.
To check a transaction in a block on the external blockchain you can use the following built-in Ride function:
createMerkleRoot(merkleProofs: List[ByteVector], valueBytes: ByteVector, index: Int): ByteVector
This function is applicable if the external blockchain uses the same algorithm for calculating the root hash of transactions (for instance, external blockchain is Waves-based). The
createMerkleRoot function calculates the root hash from the transaction hash and sibling hashes of the Merkle tree (see Steps 1–3). To check a transaction in a block, compare the calculated root hash with the
transactionsRoot value in the block header. See the function description in the Verification functions article.