End block's header hash is unconstrained for a subchain proof
Description
The output for prove_subchain
circuit contains the following values.
MapReduceSubchainVariable {
is_enabled: is_batch_enabled,
start_block: batch_start_block,
start_header: batch_start_header_hash,
end_block: batch_end_block,
end_header: batch_end_header_hash,
data_merkle_root,
}
The end_header
is the header hash of the last block in the subchain. However, the value it contains (i.e., batch_end_header_hash
) is taken directly from the input and left completely unconstrained.
Impact
A malicous prover can supply an arbitrary value for the end header, which allows for the Blobstream state to differ from the on-chain state.
Recommendations
The loop in prove_subchain
calculates the header for each block in the chain and verifies chain integrity using Merkle proofs.
curr_header = last_block_id_proof_root;
At the end of the loop, curr_header
will contain the hash of the last block, and it should be asserted that this hash is equal to the value being returned.
let end_hash_matches_calculated_hash = self.is_equal(batch_end_header_hash, curr_header);
self.assert_is_equal(end_hash_matches_calculated_hash, true_bool);
Remediation
This issue has been acknowledged by Succinct, and a fix was implemented in the following commit: .