Skip to content

Commit

Permalink
Fix header.hash for height 1 (#438)
Browse files Browse the repository at this point in the history
* Add test-case

* add test in lite_impl too and fix code

* fmt & add comment

* fmt & add comment & add test for height 2 as well

* Deduplicate code for computing header hashes in light-client crate

* Fix formatting

Co-authored-by: Romain Ruetschi <romain@informal.systems>
  • Loading branch information
liamsi and romac committed Jul 13, 2020
1 parent 82628f7 commit 2ea453b
Show file tree
Hide file tree
Showing 4 changed files with 177 additions and 65 deletions.
58 changes: 2 additions & 56 deletions light-client/src/operations/hasher.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::types::{Header, ValidatorSet};

use tendermint::amino_types::{message::AminoMessage, BlockId, ConsensusVersion, TimeMsg};
use tendermint::lite::types::Header as _;
use tendermint::merkle;
use tendermint::Hash;

Expand All @@ -14,7 +14,7 @@ pub struct ProdHasher;

impl Hasher for ProdHasher {
fn hash_header(&self, header: &Header) -> Hash {
amino_hash(header)
header.hash()
}

/// Compute the Merkle root of the validator set
Expand All @@ -28,57 +28,3 @@ impl Hasher for ProdHasher {
Hash::Sha256(merkle::simple_hash_from_byte_vectors(validator_bytes))
}
}

fn amino_hash(header: &Header) -> Hash {
// Note that if there is an encoding problem this will
// panic (as the golang code would):
// https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/block.go#L393
// https://github.com/tendermint/tendermint/blob/134fe2896275bb926b49743c1e25493f6b24cc31/types/encoding_helper.go#L9:6

let mut fields_bytes: Vec<Vec<u8>> = Vec::with_capacity(16);
fields_bytes.push(AminoMessage::bytes_vec(&ConsensusVersion::from(
&header.version,
)));
fields_bytes.push(bytes_enc(header.chain_id.as_bytes()));
fields_bytes.push(encode_varint(header.height.into()));
fields_bytes.push(AminoMessage::bytes_vec(&TimeMsg::from(header.time)));
fields_bytes.push(
header
.last_block_id
.as_ref()
.map_or(vec![], |id| AminoMessage::bytes_vec(&BlockId::from(id))),
);
fields_bytes.push(header.last_commit_hash.as_ref().map_or(vec![], encode_hash));
fields_bytes.push(header.data_hash.as_ref().map_or(vec![], encode_hash));
fields_bytes.push(encode_hash(&header.validators_hash));
fields_bytes.push(encode_hash(&header.next_validators_hash));
fields_bytes.push(encode_hash(&header.consensus_hash));
fields_bytes.push(bytes_enc(&header.app_hash));
fields_bytes.push(
header
.last_results_hash
.as_ref()
.map_or(vec![], encode_hash),
);
fields_bytes.push(header.evidence_hash.as_ref().map_or(vec![], encode_hash));
fields_bytes.push(bytes_enc(header.proposer_address.as_bytes()));

Hash::Sha256(merkle::simple_hash_from_byte_vectors(fields_bytes))
}

fn bytes_enc(bytes: &[u8]) -> Vec<u8> {
let mut chain_id_enc = vec![];
prost_amino::encode_length_delimiter(bytes.len(), &mut chain_id_enc).unwrap();
chain_id_enc.append(&mut bytes.to_vec());
chain_id_enc
}

fn encode_hash(hash: &Hash) -> Vec<u8> {
bytes_enc(hash.as_bytes())
}

fn encode_varint(val: u64) -> Vec<u8> {
let mut val_enc = vec![];
prost_amino::encoding::encode_varint(val, &mut val_enc);
val_enc
}
10 changes: 10 additions & 0 deletions rpc/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,16 @@ mod endpoints {
assert_eq!(header.hash(), block_id.hash);
}

#[test]
fn commit_height_1() {
let response =
endpoint::commit::Response::from_string(&read_json_fixture("commit_1")).unwrap();
let header = response.signed_header.header;
let commit = response.signed_header.commit;
let block_id = commit.block_id;
assert_eq!(header.hash(), block_id.hash);
}

#[test]
fn genesis() {
let response =
Expand Down
53 changes: 53 additions & 0 deletions rpc/tests/support/commit_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"jsonrpc": "2.0",
"id": -1,
"result": {
"signed_header": {
"header": {
"version": {
"block": "10",
"app": "1"
},
"chain_id": "dockerchain",
"height": "1",
"time": "2020-07-09T14:24:44.7157258Z",
"last_block_id": {
"hash": "",
"parts": {
"total": "0",
"hash": ""
}
},
"last_commit_hash": "",
"data_hash": "",
"validators_hash": "74F2AC2B6622504D08DD2509E28CE731985CFE4D133C9DB0CB85763EDCA95AA3",
"next_validators_hash": "74F2AC2B6622504D08DD2509E28CE731985CFE4D133C9DB0CB85763EDCA95AA3",
"consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F",
"app_hash": "",
"last_results_hash": "",
"evidence_hash": "",
"proposer_address": "AD358F20C8CE80889E0F0248FDDC454595D632AE"
},
"commit": {
"height": "1",
"round": "0",
"block_id": {
"hash": "F008EACA817CF6A3918CF7A6FD44F1F2464BB24D25A7EDB45A03E8783E9AB438",
"parts": {
"total": "1",
"hash": "BF5130E879A02AC4BB83E392732ED4A37BE2F01304A615467EE7960858774E57"
}
},
"signatures": [
{
"block_id_flag": 2,
"validator_address": "AD358F20C8CE80889E0F0248FDDC454595D632AE",
"timestamp": "2020-07-10T23:47:42.8655562Z",
"signature": "HWADptT75yH6Y558j268oItSorBwngMsqfldKgXQaUT02Gd6OqpzFHZgPUzoaLuVa1rk6EM7W0Rx1dDdpOIoDg=="
}
]
}
},
"canonical": true
}
}
121 changes: 112 additions & 9 deletions tendermint/src/lite_impl/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl lite::Header for block::Header {
fields_bytes.push(AminoMessage::bytes_vec(&ConsensusVersion::from(
&self.version,
)));
fields_bytes.push(bytes_enc(self.chain_id.as_bytes()));
fields_bytes.push(encode_bytes(self.chain_id.as_bytes()));
fields_bytes.push(encode_varint(self.height.value()));
fields_bytes.push(AminoMessage::bytes_vec(&TimeMsg::from(self.time)));
fields_bytes.push(
Expand All @@ -48,28 +48,131 @@ impl lite::Header for block::Header {
fields_bytes.push(encode_hash(&self.validators_hash));
fields_bytes.push(encode_hash(&self.next_validators_hash));
fields_bytes.push(encode_hash(&self.consensus_hash));
fields_bytes.push(bytes_enc(&self.app_hash));
fields_bytes.push(encode_bytes(&self.app_hash));
fields_bytes.push(self.last_results_hash.as_ref().map_or(vec![], encode_hash));
fields_bytes.push(self.evidence_hash.as_ref().map_or(vec![], encode_hash));
fields_bytes.push(bytes_enc(self.proposer_address.as_bytes()));
fields_bytes.push(encode_bytes(self.proposer_address.as_bytes()));

Hash::Sha256(simple_hash_from_byte_vectors(fields_bytes))
}
}

fn bytes_enc(bytes: &[u8]) -> Vec<u8> {
let mut chain_id_enc = vec![];
prost_amino::encode_length_delimiter(bytes.len(), &mut chain_id_enc).unwrap();
chain_id_enc.append(&mut bytes.to_vec());
chain_id_enc
fn encode_bytes(bytes: &[u8]) -> Vec<u8> {
let bytes_len = bytes.len();
if bytes_len > 0 {
let mut encoded = vec![];
prost_amino::encode_length_delimiter(bytes_len, &mut encoded).unwrap();
encoded.append(&mut bytes.to_vec());
encoded
} else {
vec![]
}
}

fn encode_hash(hash: &Hash) -> Vec<u8> {
bytes_enc(hash.as_bytes())
encode_bytes(hash.as_bytes())
}

fn encode_varint(val: u64) -> Vec<u8> {
let mut val_enc = vec![];
prost_amino::encoding::encode_varint(val, &mut val_enc);
val_enc
}

#[cfg(test)]
mod test {
use crate::block::Header;
use crate::lite::Header as _;
use crate::Hash;
use std::str::FromStr;

#[test]
fn test_hash_height_1() {
// JSON extracted from https://github.com/tendermint/tendermint/tree/v0.33
// more precisely `curl`ed from locally build docker image of:
// git log --pretty=format:"%H" -1 15:35:44
// 606d0a89ccabbd3e59cff521f9f4d875cc366ac9
// via
// curl -X GET "http://localhost:26657/commit?height=1" -H "accept: application/json" | jq .result.signed_header.header
let json_data = r#"
{
"version": {
"block": "10",
"app": "1"
},
"chain_id": "dockerchain",
"height": "1",
"time": "2020-07-09T14:24:44.7157258Z",
"last_block_id": {
"hash": "",
"parts": {
"total": "0",
"hash": ""
}
},
"last_commit_hash": "",
"data_hash": "",
"validators_hash": "74F2AC2B6622504D08DD2509E28CE731985CFE4D133C9DB0CB85763EDCA95AA3",
"next_validators_hash": "74F2AC2B6622504D08DD2509E28CE731985CFE4D133C9DB0CB85763EDCA95AA3",
"consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F",
"app_hash": "",
"last_results_hash": "",
"evidence_hash": "",
"proposer_address": "AD358F20C8CE80889E0F0248FDDC454595D632AE"
}"#;
// extracted expected hash from a commit via
// curl -X GET "http://localhost:26657/commit?height=1" -H "accept: application/json" | jq .result.signed_header.commit.block_id.hash
let header: Header = serde_json::from_str(json_data).unwrap();
let got_hash = header.hash();
let want_hash =
Hash::from_str("F008EACA817CF6A3918CF7A6FD44F1F2464BB24D25A7EDB45A03E8783E9AB438")
.unwrap();

assert_eq!(got_hash, want_hash);
}

#[test]
fn test_hash_height_2() {
// JSON test-vector extracted from https://github.com/tendermint/tendermint/tree/v0.33
// more precisely `curl`ed from locally build docker image of:
// git log --pretty=format:"%H" -1 15:35:44
// 606d0a89ccabbd3e59cff521f9f4d875cc366ac9
// via
// curl -X GET "http://localhost:26657/commit?height=2" -H "accept: application/json" | jq .result.signed_header.header
let json_data = r#"
{
"version": {
"block": "10",
"app": "1"
},
"chain_id": "dockerchain",
"height": "2",
"time": "2020-07-10T23:47:42.8655562Z",
"last_block_id": {
"hash": "F008EACA817CF6A3918CF7A6FD44F1F2464BB24D25A7EDB45A03E8783E9AB438",
"parts": {
"total": "1",
"hash": "BF5130E879A02AC4BB83E392732ED4A37BE2F01304A615467EE7960858774E57"
}
},
"last_commit_hash": "1527AF33311EB16EF9BB15B5570DE9664D6F3598BEE08231588CED89B2D8F8EA",
"data_hash": "",
"validators_hash": "74F2AC2B6622504D08DD2509E28CE731985CFE4D133C9DB0CB85763EDCA95AA3",
"next_validators_hash": "74F2AC2B6622504D08DD2509E28CE731985CFE4D133C9DB0CB85763EDCA95AA3",
"consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F",
"app_hash": "0000000000000000",
"last_results_hash": "",
"evidence_hash": "",
"proposer_address": "AD358F20C8CE80889E0F0248FDDC454595D632AE"
}"#;
// extracted expected hash from a commit via
// curl -X GET "http://localhost:26657/commit?height=2" -H "accept: application/json" | jq .result.signed_header.commit.block_id.hash
let header: Header = serde_json::from_str(json_data).unwrap();
let got_hash = header.hash();
let want_hash =
Hash::from_str("5CFF26AAECBEEA7CBD2181D5547BB087E4DC8004960D611ECA59CFEA724AD538")
.unwrap();

assert_eq!(got_hash, want_hash);
}
}

0 comments on commit 2ea453b

Please sign in to comment.