From 394f16055451696c3aea915634c719ef927d6d1b Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 7 May 2020 18:10:42 +0900 Subject: [PATCH 1/5] Change mock's Header::default function to static function --- test/src/helper/mock/cHeader.ts | 37 +++++++++++++++++---------------- 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/test/src/helper/mock/cHeader.ts b/test/src/helper/mock/cHeader.ts index 0f12cf0fe3..c113db2705 100644 --- a/test/src/helper/mock/cHeader.ts +++ b/test/src/helper/mock/cHeader.ts @@ -52,6 +52,25 @@ export class Header { return header; } + + public static default(): Header { + return new Header( + new H256( + "0000000000000000000000000000000000000000000000000000000000000000" + ), + new U256(0), + new U256(0), + new H256( + "0000000000000000000000000000000000000000000000000000000000000000" + ), + Buffer.alloc(0), + BLAKE_NULL_RLP, + BLAKE_NULL_RLP, + BLAKE_NULL_RLP, + [] + ); + } + private parentHash: H256; private timestamp: U256; private number: U256; @@ -134,24 +153,6 @@ export class Header { return this.bareHash; } - public default(): Header { - return new Header( - new H256( - "0000000000000000000000000000000000000000000000000000000000000000" - ), - new U256(0), - new U256(0), - new H256( - "0000000000000000000000000000000000000000000000000000000000000000" - ), - Buffer.alloc(0), - BLAKE_NULL_RLP, - BLAKE_NULL_RLP, - BLAKE_NULL_RLP, - [] - ); - } - public toEncodeObject(): Array { return [ this.parentHash.toEncodeObject(), From 6729691bfaa87b0e23aaf177fdb5591c1fa83d40 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 7 May 2020 18:12:07 +0900 Subject: [PATCH 2/5] Change seal serialize code in mock The seal is an array of any RLP data. Wrapping them with Buffer makes invalid RLP encoding. --- test/src/helper/mock/cHeader.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/src/helper/mock/cHeader.ts b/test/src/helper/mock/cHeader.ts index c113db2705..9b0eabc5a9 100644 --- a/test/src/helper/mock/cHeader.ts +++ b/test/src/helper/mock/cHeader.ts @@ -79,7 +79,7 @@ export class Header { private transactionsRoot: H256; private stateRoot: H256; private nextValidatorSetHash: H256; - private seal: number[][]; + private seal: any[]; private hash: null | H256; private bareHash: null | H256; @@ -92,7 +92,7 @@ export class Header { transactionsRoot: H256, stateRoot: H256, nextValidatorSetHash: H256, - seal: number[][], + seal: any[], hash?: H256, bareHash?: H256 ) { @@ -141,7 +141,7 @@ export class Header { this.nextValidatorSetHash = root; } - public setSeal(seal: number[][]) { + public setSeal(seal: any[]) { this.seal = seal; } @@ -163,7 +163,7 @@ export class Header { this.number.toEncodeObject(), this.timestamp.toEncodeObject(), this.extraData - ].concat(this.seal.map(seal => Buffer.of(...seal))); + ].concat(this.seal); } public rlpBytes(): Buffer { From a98a41506854e27cc09006f358f9fa231c80fb20 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 7 May 2020 18:16:01 +0900 Subject: [PATCH 3/5] Add Header RLP encoding test in mock --- test/src/helper/mock/cHeader.ts | 4 ++ test/src/helper/mock/test/header.test.ts | 62 ++++++++++++++++++++++++ 2 files changed, 66 insertions(+) create mode 100644 test/src/helper/mock/test/header.test.ts diff --git a/test/src/helper/mock/cHeader.ts b/test/src/helper/mock/cHeader.ts index 9b0eabc5a9..55389c771e 100644 --- a/test/src/helper/mock/cHeader.ts +++ b/test/src/helper/mock/cHeader.ts @@ -145,6 +145,10 @@ export class Header { this.seal = seal; } + public getParentHash(): H256 | null { + return this.parentHash; + } + public getHash(): H256 | null { return this.hash; } diff --git a/test/src/helper/mock/test/header.test.ts b/test/src/helper/mock/test/header.test.ts new file mode 100644 index 0000000000..fca41c550e --- /dev/null +++ b/test/src/helper/mock/test/header.test.ts @@ -0,0 +1,62 @@ +import { Buffer } from "buffer"; +import { expect } from "chai"; +import "mocha"; +import * as RLP from "rlp"; +import { + getPublicFromPrivate, + H256, + signEd25519, + U256 +} from "../../../primitives/src"; +import { blake256 } from "../../../primitives/src/hash"; +import { Header } from "../cHeader"; + +describe("Check Header RLP encoding", function() { + it("empty Header RLP encoding test", function() { + const header = Header.default(); + expect(header.rlpBytes().toString("hex")).deep.equal( + "f8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0808080" + ); + }); + + it("Header RLP encoding test", function() { + const privateKey = + "5a0391789b130315eebeb333d4fa641aee07242081ba8858ed3f36a937ca84653b21399e52ae4d7582032df537c00eaa3f4611210b3305ce48ac5407cd8f91bf"; + const publicKey = getPublicFromPrivate(privateKey); + const header = Header.default(); + header.setNumber(new U256(4)); + header.setAuthor(new H256(publicKey)); + const bitset = Buffer.alloc(100, 0); + bitset[0] = 4; + const signature = createPrecommit({ + height: 3, + view: 0, + step: 2, + parentHash: header.getParentHash()!, + privateKey + }); + header.setSeal([0, 0, [Buffer.from(signature, "hex")], bitset]); + expect(header.rlpBytes().toString("hex")).deep.equal( + "f90154a00000000000000000000000000000000000000000000000000000000000000000a03b21399e52ae4d7582032df537c00eaa3f4611210b3305ce48ac5407cd8f91bfa045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c00480808080f842b8404752ae46a97e2e4ff11b8212b85610f81ae63c5b541cc5f1e89238150c122be650b9abc954bab919de4be9a2f1dba992e88b9aa5596d2bdf2645597163697d07b86404000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ); + }); + + function createPrecommit({ + height, + view, + step, + parentHash, + privateKey + }: { + height: number; + view: number; + step: number; + parentHash: H256; + privateKey: string; + }): string { + const voteOn = [[height, view, step], [parentHash.toEncodeObject()]]; + const serializedVoteOn = RLP.encode(voteOn); + const message = blake256(serializedVoteOn); + return signEd25519(message, privateKey); + } +}); From ffb2ce02039a4a85ab0ae8ca6b79dd518a90aa6e Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 7 May 2020 18:27:09 +0900 Subject: [PATCH 4/5] Remove PartialEq derive from Header and Block Since Header has a cache of its hash using RefCell, the existence of the cache changes the compare result. --- core/src/block.rs | 2 +- sync/src/block/message/mod.rs | 23 +++++++++++++++++------ sync/src/block/message/response.rs | 15 ++++++++++----- sync/src/transaction/message.rs | 17 +++++++++++++---- types/src/header.rs | 2 +- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/core/src/block.rs b/core/src/block.rs index b200dfd752..585b23e67b 100644 --- a/core/src/block.rs +++ b/core/src/block.rs @@ -32,7 +32,7 @@ use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use std::collections::HashSet; /// A block, encoded as it is on the block chain. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct Block { /// The header of this block pub header: Header, diff --git a/sync/src/block/message/mod.rs b/sync/src/block/message/mod.rs index f9f656aff6..8d0d0fb9f7 100644 --- a/sync/src/block/message/mod.rs +++ b/sync/src/block/message/mod.rs @@ -58,7 +58,7 @@ impl Decodable for MessageID { } } -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum Message { Status { seq: U256, @@ -156,23 +156,34 @@ impl Decodable for Message { #[cfg(test)] mod tests { + use super::*; use primitives::H256; - use rlp::rlp_encode_and_decode_test; - use super::*; + /// For a type that does not have PartialEq, uses Debug instead. + fn assert_eq_by_debug(a: &T, b: &T) { + assert_eq!(format!("{:?}", a), format!("{:?}", b)); + } #[test] fn status_message_rlp() { - rlp_encode_and_decode_test!(Message::Status { + let status_message = Message::Status { seq: U256::zero(), best_hash: H256::default().into(), genesis_hash: H256::default().into(), - }); + }; + let encoded = rlp::encode(&status_message); + let decoded: Message = rlp::decode(&encoded).unwrap(); + + assert_eq_by_debug(&status_message, &decoded) } #[test] fn request_bodies_message_rlp() { let request_id = 10; - rlp_encode_and_decode_test!(Message::Request(request_id, RequestMessage::Bodies(vec![]))); + let message = Message::Request(request_id, RequestMessage::Bodies(vec![])); + let encoded = rlp::encode(&message); + let decoded: Message = rlp::decode(&encoded).unwrap(); + + assert_eq_by_debug(&message, &decoded) } } diff --git a/sync/src/block/message/response.rs b/sync/src/block/message/response.rs index 0179212843..a5303bbcee 100644 --- a/sync/src/block/message/response.rs +++ b/sync/src/block/message/response.rs @@ -19,7 +19,7 @@ use ccore::UnverifiedTransaction; use ctypes::Header; use rlp::{DecoderError, Encodable, Rlp, RlpStream}; -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub enum ResponseMessage { Headers(Vec
), Bodies(Vec>), @@ -125,6 +125,11 @@ mod tests { ResponseMessage::decode(id, &rlp).unwrap() } + /// For a type that does not have PartialEq, uses Debug instead. + fn assert_eq_by_debug(a: &T, b: &T) { + assert_eq!(format!("{:?}", a), format!("{:?}", b)); + } + #[test] fn headers_message_rlp() { let headers = vec![Header::default()]; @@ -133,13 +138,13 @@ mod tests { }); let message = ResponseMessage::Headers(headers); - assert_eq!(message, decode_bytes(message.message_id(), message.rlp_bytes().as_ref())); + assert_eq_by_debug(&message, &decode_bytes(message.message_id(), message.rlp_bytes().as_ref())) } #[test] fn bodies_message_rlp() { let message = ResponseMessage::Bodies(vec![vec![]]); - assert_eq!(message, decode_bytes(message.message_id(), message.rlp_bytes().as_ref())); + assert_eq_by_debug(&message, &decode_bytes(message.message_id(), message.rlp_bytes().as_ref())); let tx = UnverifiedTransaction::new( Transaction { @@ -156,12 +161,12 @@ mod tests { ); let message = ResponseMessage::Bodies(vec![vec![tx]]); - assert_eq!(message, decode_bytes(message.message_id(), message.rlp_bytes().as_ref())); + assert_eq_by_debug(&message, &decode_bytes(message.message_id(), message.rlp_bytes().as_ref())); } #[test] fn state_chunk_message_rlp() { let message = ResponseMessage::StateChunk(vec![]); - assert_eq!(message, decode_bytes(message.message_id(), message.rlp_bytes().as_ref())); + assert_eq_by_debug(&message, &decode_bytes(message.message_id(), message.rlp_bytes().as_ref())); } } diff --git a/sync/src/transaction/message.rs b/sync/src/transaction/message.rs index d9883bdf27..e0b1a2b6b4 100644 --- a/sync/src/transaction/message.rs +++ b/sync/src/transaction/message.rs @@ -63,17 +63,23 @@ impl Decodable for Message { #[cfg(test)] mod tests { - use rlp::rlp_encode_and_decode_test; - use ccore::UnverifiedTransaction; use ckey::{Ed25519Public as Public, Signature}; use ctypes::transaction::{Action, Transaction}; use super::Message; + /// For a type that does not have PartialEq, uses Debug instead. + fn assert_eq_by_debug(a: &T, b: &T) { + assert_eq!(format!("{:?}", a), format!("{:?}", b)); + } + #[test] fn transactions_message_rlp() { - rlp_encode_and_decode_test!(Message::Transactions(Vec::new())); + let message = Message::Transactions(Vec::new()); + let encoded = rlp::encode(&message); + let decoded: Message = rlp::decode(&encoded).unwrap(); + assert_eq_by_debug(&message, &decoded); } #[test] @@ -92,6 +98,9 @@ mod tests { Public::random(), ); - rlp_encode_and_decode_test!(Message::Transactions(vec![tx])); + let message = Message::Transactions(vec![tx]); + let encoded = rlp::encode(&message); + let decoded: Message = rlp::decode(&encoded).unwrap(); + assert_eq_by_debug(&message, &decoded); } } diff --git a/types/src/header.rs b/types/src/header.rs index 9de0528c95..cc873ec5a1 100644 --- a/types/src/header.rs +++ b/types/src/header.rs @@ -33,7 +33,7 @@ pub enum Seal { /// A block header. /// Note : you must modify /core/src/views/header.rs too when you modify this. -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone)] pub struct Header { /// Parent hash. parent_hash: BlockHash, From 1c033724f1f8de4fa282634ba81c47e7cef93696 Mon Sep 17 00:00:00 2001 From: Park Juhyung Date: Thu, 7 May 2020 18:35:34 +0900 Subject: [PATCH 5/5] Add a hint comment that helps programmer to manage tests --- test/src/helper/mock/test/header.test.ts | 2 ++ types/src/header.rs | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/test/src/helper/mock/test/header.test.ts b/test/src/helper/mock/test/header.test.ts index fca41c550e..6194fc991b 100644 --- a/test/src/helper/mock/test/header.test.ts +++ b/test/src/helper/mock/test/header.test.ts @@ -14,6 +14,7 @@ import { Header } from "../cHeader"; describe("Check Header RLP encoding", function() { it("empty Header RLP encoding test", function() { const header = Header.default(); + // Find the empty header's rlp encoded data in the unit test in header.rs file expect(header.rlpBytes().toString("hex")).deep.equal( "f8a8a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0808080" ); @@ -36,6 +37,7 @@ describe("Check Header RLP encoding", function() { privateKey }); header.setSeal([0, 0, [Buffer.from(signature, "hex")], bitset]); + // Find the header's rlp encoded data in the unit test in the tendermint/mod.rs file expect(header.rlpBytes().toString("hex")).deep.equal( "f90154a00000000000000000000000000000000000000000000000000000000000000000a03b21399e52ae4d7582032df537c00eaa3f4611210b3305ce48ac5407cd8f91bfa045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c0a045b0cfc220ceec5b7c1c62c4d4193d38e4eba48e8815729ce75f9c0ab0e4c1c00480808080f842b8404752ae46a97e2e4ff11b8212b85610f81ae63c5b541cc5f1e89238150c122be650b9abc954bab919de4be9a2f1dba992e88b9aa5596d2bdf2645597163697d07b86404000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" ); diff --git a/types/src/header.rs b/types/src/header.rs index cc873ec5a1..0567cdfd97 100644 --- a/types/src/header.rs +++ b/types/src/header.rs @@ -322,3 +322,16 @@ impl Encodable for Header { self.stream_rlp(s, &Seal::With); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn serialize_deserialize_test() { + let empty = Header::default(); + let encoded = rlp::encode(&empty); + let decoded: Header = rlp::decode(&encoded).unwrap(); + assert_eq!(empty.hash(), decoded.hash()); + } +}