From 9e76c7303293cb85df91320a012d8d7ccd23abae Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Thu, 26 Sep 2019 14:00:09 +0500 Subject: [PATCH 01/10] Adding merkle proof to prev chunk header (#1348) ISSUE === https://github.com/nearprotocol/nearcore/issues/1348 TEST PLAN === - test_catchup_random_single_part_sync - test_catchup_random_single_part_sync_non_zero_amounts - test_catchup_random_single_part_sync_height_6 - test_catchup_receipts_sync_third_epoch - CI tests --- chain/chain/src/chain.rs | 52 +++++++++++++++++++++++++++----- chain/client/src/client_actor.rs | 8 +++++ chain/network/src/types.rs | 4 ++- 3 files changed, 55 insertions(+), 9 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index 41c32aca64a..e9b82e670a1 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -807,6 +807,8 @@ impl Chain { ( ShardChunk, MerklePath, + ShardChunkHeader, + MerklePath, Vec, Vec, Vec, @@ -835,17 +837,19 @@ impl Chain { } // Chunk header here is the same chunk header as at the `current` height. let chunk_header = sync_prev_block.chunks[shard_id as usize].clone(); - let (chunk_headers_root, chunks_proofs) = merklize( + let (chunk_headers_root, chunk_proofs) = merklize( &sync_prev_block .chunks .iter() - .map(|chunk| ChunkHashHeight(chunk.hash.clone(), chunk.height_included)) + .map(|shard_chunk| { + ChunkHashHeight(shard_chunk.hash.clone(), shard_chunk.height_included) + }) .collect::>(), ); - assert_eq!(chunk_headers_root, sync_prev_block.header.inner.chunk_headers_root); + let chunk = self.get_chunk_clone_from_header(&chunk_header)?; - let chunk_proof = chunks_proofs[shard_id as usize].clone(); + let chunk_proof = chunk_proofs[shard_id as usize].clone(); let block_header = self.get_header_by_height(chunk_header.height_included)?.clone(); let block = self.get_block(&block_header.hash)?; let block_transactions = block.transactions.clone(); @@ -855,7 +859,19 @@ impl Chain { if shard_id as usize >= prev_block.chunks.len() { return Err(ErrorKind::Other("Invalid request: ShardId out of bounds".into()).into()); } - let prev_chunk_header = &prev_block.chunks[shard_id as usize]; + let prev_chunk_header = prev_block.chunks[shard_id as usize].clone(); + let (prev_chunk_headers_root, prev_chunk_proofs) = merklize( + &prev_block + .chunks + .iter() + .map(|shard_chunk| { + ChunkHashHeight(shard_chunk.hash.clone(), shard_chunk.height_included) + }) + .collect::>(), + ); + assert_eq!(prev_chunk_headers_root, prev_block.header.inner.chunk_headers_root); + + let prev_chunk_proof = prev_chunk_proofs[shard_id as usize].clone(); let prev_chunk_height_included = prev_chunk_header.height_included; let prev_payload = self .runtime_adapter @@ -905,6 +921,8 @@ impl Chain { Ok(( chunk, chunk_proof, + prev_chunk_header, + prev_chunk_proof, prev_payload, block_transactions, incoming_receipts_proofs, @@ -919,6 +937,8 @@ impl Chain { sync_hash: CryptoHash, chunk: ShardChunk, chunk_proof: MerklePath, + prev_chunk_header: ShardChunkHeader, + prev_chunk_proof: MerklePath, prev_payload: Vec, block_transactions: Vec, incoming_receipts_proofs: Vec, @@ -984,8 +1004,9 @@ impl Chain { // Consider chunk itself is valid. - // 3. Checking that chunk `chunk` is included into block at last height before sync_hash - // 3a. Also checking chunk.height_included + // 3. Checking that chunks `chunk` and `prev_chunk` are included in appropriate blocks + // 3a. Checking that chunk `chunk` is included into block at last height before sync_hash + // 3aa. Also checking chunk.height_included let sync_prev_block_header = self.get_block_header(&sync_block_header.inner.prev_hash)?.clone(); if !verify_path( @@ -1000,8 +1021,23 @@ impl Chain { .into()); } - // 4. Checking block_transactions validity let block_header = self.get_header_by_height(chunk.header.height_included)?.clone(); + // 3b. Checking that chunk `prev_chunk` is included into block at height before chunk.height_included + // 3ba. Also checking prev_chunk.height_included - it's important for getting correct incoming receipts + let prev_block_header = self.get_block_header(&block_header.inner.prev_hash)?.clone(); + if !verify_path( + prev_block_header.inner.chunk_headers_root, + &prev_chunk_proof, + &ChunkHashHeight(prev_chunk_header.hash.clone(), prev_chunk_header.height_included), + ) { + byzantine_assert!(false); + return Err(ErrorKind::Other( + "set_shard_state failed: prev_chunk isn't included into block".into(), + ) + .into()); + } + + // 4. Checking block_transactions validity if Block::compute_tx_root(&block_transactions) != block_header.inner.tx_root { byzantine_assert!(false); return Err(ErrorKind::Other( diff --git a/chain/client/src/client_actor.rs b/chain/client/src/client_actor.rs index fc67c10ff98..873d58abbf2 100644 --- a/chain/client/src/client_actor.rs +++ b/chain/client/src/client_actor.rs @@ -306,6 +306,8 @@ impl Handler for ClientActor { if let Ok(( chunk, chunk_proof, + prev_chunk_header, + prev_chunk_proof, prev_payload, block_transactions, incoming_receipts_proofs, @@ -317,6 +319,8 @@ impl Handler for ClientActor { hash, chunk, chunk_proof, + prev_chunk_header, + prev_chunk_proof, prev_payload, block_transactions, incoming_receipts_proofs, @@ -330,6 +334,8 @@ impl Handler for ClientActor { hash, chunk, chunk_proof, + prev_chunk_header, + prev_chunk_proof, prev_payload, block_transactions, incoming_receipts_proofs, @@ -371,6 +377,8 @@ impl Handler for ClientActor { hash, chunk, chunk_proof, + prev_chunk_header, + prev_chunk_proof, prev_payload, block_transactions, incoming_receipts_proofs, diff --git a/chain/network/src/types.rs b/chain/network/src/types.rs index bbcd49fbd69..131a3e605ae 100644 --- a/chain/network/src/types.rs +++ b/chain/network/src/types.rs @@ -18,7 +18,7 @@ use near_crypto::{PublicKey, ReadablePublicKey, SecretKey, Signature}; use near_primitives::hash::{hash, CryptoHash}; use near_primitives::merkle::MerklePath; pub use near_primitives::sharding::ChunkPartMsg; -use near_primitives::sharding::{ChunkHash, ChunkOnePart, ShardChunk}; +use near_primitives::sharding::{ChunkHash, ChunkOnePart, ShardChunk, ShardChunkHeader}; use near_primitives::transaction::SignedTransaction; use near_primitives::types::{AccountId, BlockIndex, EpochId, ShardId}; use near_primitives::utils::{from_timestamp, to_timestamp}; @@ -675,6 +675,8 @@ pub struct StateResponseInfo { pub hash: CryptoHash, pub chunk: ShardChunk, pub chunk_proof: MerklePath, + pub prev_chunk_header: ShardChunkHeader, + pub prev_chunk_proof: MerklePath, pub prev_payload: Vec, pub block_transactions: Vec, pub incoming_receipts_proofs: Vec, From 4ad2cec9b585a4066eb7918068b13209dc0703ce Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Fri, 27 Sep 2019 13:13:41 +0500 Subject: [PATCH 02/10] naming: receipts -> outgoing_receipts --- chain/chunks/src/lib.rs | 22 ++++++++++++---------- chain/client/src/client.rs | 15 ++++++++------- core/primitives/src/sharding.rs | 12 ++++++------ 3 files changed, 26 insertions(+), 23 deletions(-) diff --git a/chain/chunks/src/lib.rs b/chain/chunks/src/lib.rs index 6c5e423caf7..192f2d1fef8 100644 --- a/chain/chunks/src/lib.rs +++ b/chain/chunks/src/lib.rs @@ -717,8 +717,8 @@ impl ShardsManager { rent_paid: Balance, validator_proposals: Vec, transactions: &Vec, - receipts: &Vec, - receipts_root: CryptoHash, + outgoing_receipts: &Vec, + outgoing_receipts_root: CryptoHash, tx_root: CryptoHash, signer: Arc, ) -> Result { @@ -737,8 +737,8 @@ impl ShardsManager { tx_root, validator_proposals, transactions, - receipts, - receipts_root, + outgoing_receipts, + outgoing_receipts_root, signer, )?; @@ -753,16 +753,18 @@ impl ShardsManager { pub fn distribute_encoded_chunk( &mut self, encoded_chunk: EncodedShardChunk, - receipts: Vec, + outgoing_receipts: Vec, ) { // TODO: if the number of validators exceeds the number of parts, this logic must be changed let prev_block_hash = encoded_chunk.header.inner.prev_block_hash; let mut processed_one_part = false; let chunk_hash = encoded_chunk.chunk_hash(); let shard_id = encoded_chunk.header.inner.shard_id; - let receipts_hashes = self.runtime_adapter.build_receipts_hashes(&receipts).unwrap(); - let (receipts_root, receipts_proofs) = merklize(&receipts_hashes); - assert_eq!(encoded_chunk.header.inner.outgoing_receipts_root, receipts_root); + let outgoing_receipts_hashes = + self.runtime_adapter.build_receipts_hashes(&outgoing_receipts).unwrap(); + let (outgoing_receipts_root, outgoing_receipts_proofs) = + merklize(&outgoing_receipts_hashes); + assert_eq!(encoded_chunk.header.inner.outgoing_receipts_root, outgoing_receipts_root); for part_ord in 0..self.runtime_adapter.num_total_parts(&prev_block_hash) { let part_ord = part_ord as u64; let to_whom = self.runtime_adapter.get_part_owner(&prev_block_hash, part_ord).unwrap(); @@ -780,8 +782,8 @@ impl ShardsManager { let one_part_receipt_proofs = self.receipts_recipient_filter( shard_id, &tracking_shards, - &receipts, - &receipts_proofs, + &outgoing_receipts, + &outgoing_receipts_proofs, ); let one_part = encoded_chunk.create_chunk_one_part( part_ord, diff --git a/chain/client/src/client.rs b/chain/client/src/client.rs index 9ac45396284..cacd4caec4c 100644 --- a/chain/client/src/client.rs +++ b/chain/client/src/client.rs @@ -331,7 +331,7 @@ impl Client { shard_id ); - let ReceiptResponse(_, receipts) = self.chain.get_outgoing_receipts_for_shard( + let ReceiptResponse(_, outgoing_receipts) = self.chain.get_outgoing_receipts_for_shard( prev_block_hash, shard_id, last_header.height_included, @@ -349,8 +349,9 @@ impl Client { // 2. anyone who just asks for one's incoming receipts // will receive a piece of incoming receipts only // with merkle receipts proofs which can be checked locally - let receipts_hashes = self.runtime_adapter.build_receipts_hashes(&receipts)?; - let (receipts_root, _) = merklize(&receipts_hashes); + let outgoing_receipts_hashes = + self.runtime_adapter.build_receipts_hashes(&outgoing_receipts)?; + let (outgoing_receipts_root, _) = merklize(&outgoing_receipts_hashes); let encoded_chunk = self.shards_mgr.create_encoded_shard_chunk( prev_block_hash, @@ -362,8 +363,8 @@ impl Client { chunk_extra.rent_paid, chunk_extra.validator_proposals.clone(), &filtered_transactions, - &receipts, - receipts_root, + &outgoing_receipts, + outgoing_receipts_root, tx_root, block_producer.signer.clone(), )?; @@ -374,12 +375,12 @@ impl Client { next_height, shard_id, filtered_transactions.len(), - receipts.len(), + outgoing_receipts.len(), block_producer.account_id, encoded_chunk.chunk_hash().0, ); - self.shards_mgr.distribute_encoded_chunk(encoded_chunk, receipts); + self.shards_mgr.distribute_encoded_chunk(encoded_chunk, outgoing_receipts); Ok(()) } diff --git a/core/primitives/src/sharding.rs b/core/primitives/src/sharding.rs index 060025f57cb..fd19fb8a66f 100644 --- a/core/primitives/src/sharding.rs +++ b/core/primitives/src/sharding.rs @@ -195,12 +195,12 @@ impl EncodedShardChunk { tx_root: CryptoHash, validator_proposals: Vec, transactions: &Vec, - receipts: &Vec, - receipts_root: CryptoHash, + outgoing_receipts: &Vec, + outgoing_receipts_root: CryptoHash, signer: Arc, ) -> Result<(EncodedShardChunk, Vec), std::io::Error> { let mut bytes = - TransactionReceipt(transactions.to_vec(), receipts.to_vec()).try_to_vec()?; + TransactionReceipt(transactions.to_vec(), outgoing_receipts.to_vec()).try_to_vec()?; let parity_parts = total_parts - data_parts; let mut parts = vec![]; @@ -230,7 +230,7 @@ impl EncodedShardChunk { gas_used, gas_limit, rent_paid, - receipts_root, + outgoing_receipts_root, tx_root, validator_proposals, encoded_length as u64, @@ -250,7 +250,7 @@ impl EncodedShardChunk { gas_used: Gas, gas_limit: Gas, rent_paid: Balance, - receipts_root: CryptoHash, + outgoing_receipts_root: CryptoHash, tx_root: CryptoHash, validator_proposals: Vec, @@ -275,7 +275,7 @@ impl EncodedShardChunk { gas_used, gas_limit, rent_paid, - receipts_root, + outgoing_receipts_root, tx_root, validator_proposals, signer, From 3214ccf772a8875932f7044cfab9bdc4b7a67230 Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Fri, 27 Sep 2019 13:47:58 +0500 Subject: [PATCH 03/10] extend receipts_hash with destination shard_id --- chain/chain/src/chain.rs | 21 +++++++++------------ chain/chain/src/types.rs | 4 ++-- 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index e9b82e670a1..d09b1e0e189 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -2,7 +2,7 @@ use std::collections::{HashMap, HashSet}; use std::sync::Arc; use std::time::{Duration as TimeDuration, Instant}; -use borsh::{BorshDeserialize, BorshSerialize}; +use borsh::BorshSerialize; use chrono::prelude::{DateTime, Utc}; use chrono::Duration; use log::{debug, info}; @@ -21,7 +21,7 @@ use crate::byzantine_assert; use crate::error::{Error, ErrorKind}; use crate::store::{ChainStore, ChainStoreAccess, ChainStoreUpdate, ShardInfo, StateSyncInfo}; use crate::types::{ - AcceptedBlock, Block, BlockHeader, BlockStatus, Provenance, ReceiptProofResponse, + AcceptedBlock, Block, BlockHeader, BlockStatus, Provenance, ReceiptList, ReceiptProofResponse, ReceiptResponse, RootProof, RuntimeAdapter, Tip, ValidatorSignatureVerificationResult, }; @@ -889,12 +889,12 @@ impl Chain { let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; let mut root_proofs_cur = vec![]; for receipt_proof in receipt_proofs { - let ReceiptProof(receipts, ShardProof(from_shard, proof)) = receipt_proof; - let receipts_hash = hash(&ReceiptList(receipts.to_vec()).try_to_vec()?); - let from_shard = *from_shard as usize; + let ReceiptProof(receipts, ShardProof(from_shard_id, proof)) = receipt_proof; + let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); + let from_shard_id = *from_shard_id as usize; let block = self.get_block(&block_hash)?; // TODO assert that block.chunks[from_shard] is reachable? - let chunk_header = block.chunks[from_shard].clone(); + let chunk_header = block.chunks[from_shard_id].clone(); let root_proof = chunk_header.inner.outgoing_receipts_root; let (block_receipts_root, block_receipts_proofs) = merklize( &block @@ -904,14 +904,14 @@ impl Chain { .collect::>(), ); root_proofs_cur - .push(RootProof(root_proof, block_receipts_proofs[from_shard].clone())); + .push(RootProof(root_proof, block_receipts_proofs[from_shard_id].clone())); // Make sure we send something reasonable. assert_eq!(block.header.inner.chunk_receipts_root, block_receipts_root); assert!(verify_path(root_proof, &proof, &receipts_hash)); assert!(verify_path( block_receipts_root, - &block_receipts_proofs[from_shard], + &block_receipts_proofs[from_shard_id], &root_proof, )); } @@ -1063,7 +1063,7 @@ impl Chain { for (j, receipt_proof) in receipt_proofs.iter().enumerate() { let ReceiptProof(receipts, ShardProof(_, proof)) = receipt_proof; let RootProof(root, block_proof) = &root_proofs[i][j]; - let receipts_hash = hash(&ReceiptList(receipts.to_vec()).try_to_vec()?); + let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); if !verify_path(*root, &proof, &receipts_hash) { byzantine_assert!(false); return Err( @@ -1292,9 +1292,6 @@ impl Chain { } } -#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Default)] -struct ReceiptList(Vec); - /// Various chain getters. impl Chain { /// Gets chain head. diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs index 3583de45995..2060ad471ff 100644 --- a/chain/chain/src/types.rs +++ b/chain/chain/src/types.rs @@ -311,14 +311,14 @@ pub trait RuntimeAdapter: Send + Sync { .filter(|&receipt| self.account_id_to_shard_id(&receipt.receiver_id) == shard_id) .cloned() .collect(); - receipts_hashes.push(hash(&ReceiptList(shard_receipts).try_to_vec()?)); + receipts_hashes.push(hash(&ReceiptList(shard_id, shard_receipts).try_to_vec()?)); } Ok(receipts_hashes) } } #[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Default)] -struct ReceiptList(Vec); +pub struct ReceiptList(pub ShardId, pub Vec); /// The last known / checked height and time when we have processed it. /// Required to keep track of skipped blocks and not fallback to produce blocks at lower height. From dd15d7562741b02d43a247f19bfafeb26b3a2e04 Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Fri, 27 Sep 2019 14:20:59 +0500 Subject: [PATCH 04/10] added to_shard_id to proofs --- chain/chain/src/chain.rs | 19 +++++++------------ chain/chunks/src/lib.rs | 5 +++-- core/primitives/src/sharding.rs | 3 ++- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index d09b1e0e189..6dd8cadb789 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -889,7 +889,7 @@ impl Chain { let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; let mut root_proofs_cur = vec![]; for receipt_proof in receipt_proofs { - let ReceiptProof(receipts, ShardProof(from_shard_id, proof)) = receipt_proof; + let ReceiptProof(receipts, ShardProof(from_shard_id, _, proof)) = receipt_proof; let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); let from_shard_id = *from_shard_id as usize; let block = self.get_block(&block_hash)?; @@ -1061,7 +1061,7 @@ impl Chain { } for (j, receipt_proof) in receipt_proofs.iter().enumerate() { - let ReceiptProof(receipts, ShardProof(_, proof)) = receipt_proof; + let ReceiptProof(receipts, ShardProof(_, _, proof)) = receipt_proof; let RootProof(root, block_proof) = &root_proofs[i][j]; let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); if !verify_path(*root, &proof, &receipts_hash) { @@ -1543,16 +1543,11 @@ impl<'a> ChainUpdate<'a> { if chunk_header.height_included == height { let one_part = self.chain_store_update.get_chunk_one_part(chunk_header).unwrap(); for receipt_proof in one_part.receipt_proofs.iter() { - let ReceiptProof(receipt, _) = receipt_proof; - if !receipt.is_empty() { - let shard_id = - self.runtime_adapter.account_id_to_shard_id(&receipt[0].receiver_id); - // TODO in debug only: assert all receipts are from the same shard - receipt_proofs_by_shard_id - .entry(shard_id) - .or_insert_with(Vec::new) - .push(receipt_proof.clone()); - } + let ReceiptProof(_, ShardProof(_, to_shard_id, _)) = receipt_proof; + receipt_proofs_by_shard_id + .entry(*to_shard_id) + .or_insert_with(Vec::new) + .push(receipt_proof.clone()); } } } diff --git a/chain/chunks/src/lib.rs b/chain/chunks/src/lib.rs index 192f2d1fef8..f9d6d4350a8 100644 --- a/chain/chunks/src/lib.rs +++ b/chain/chunks/src/lib.rs @@ -379,7 +379,7 @@ impl ShardsManager { }) .cloned() .collect(), - ShardProof(from_shard_id, proofs[to_shard_id as usize].clone()), + ShardProof(from_shard_id, to_shard_id, proofs[to_shard_id as usize].clone()), )) } } @@ -600,9 +600,10 @@ impl ShardsManager { true, ) { if proof_index == one_part.receipt_proofs.len() + || shard_id != (one_part.receipt_proofs[proof_index].1).1 || !verify_path( one_part.header.inner.outgoing_receipts_root, - &(one_part.receipt_proofs[proof_index].1).1, + &(one_part.receipt_proofs[proof_index].1).2, &receipts_hashes[shard_id as usize], ) { diff --git a/core/primitives/src/sharding.rs b/core/primitives/src/sharding.rs index fd19fb8a66f..073e7830ec4 100644 --- a/core/primitives/src/sharding.rs +++ b/core/primitives/src/sharding.rs @@ -117,7 +117,8 @@ pub struct ChunkOnePart { } #[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] -pub struct ShardProof(pub ShardId, pub MerklePath); +// from_shard_id -> to_shard_id +pub struct ShardProof(pub ShardId, pub ShardId, pub MerklePath); #[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] // For each Merkle proof there is a subset of receipts which may be proven. From 861755b6c0d9413a61b602781069b7e6a8c69302 Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Fri, 27 Sep 2019 17:55:07 +0500 Subject: [PATCH 05/10] set_shard_state update - make sure the size of incoming receipts is always a diff between BlockIndices of sync height and prev chunk - extence ShardProof with to_shard_id field - store proofs for empty receipts - save the number of included chunks in block header - prove that incoming receipts are correct on all heights - panic while invalid last_chunk_height_included in getting incoming receipts - reasonable timeout in cross_shard_tx test --- chain/chain/src/chain.rs | 46 ++++++++++++++++++++++------ chain/chain/src/store.rs | 7 ++++- chain/client/tests/cross_shard_tx.rs | 2 +- core/primitives/src/block.rs | 14 +++++++++ core/primitives/src/views.rs | 3 ++ 5 files changed, 61 insertions(+), 11 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index 6dd8cadb789..01c523ba360 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -882,19 +882,26 @@ impl Chain { let incoming_receipts_proofs = ChainStoreUpdate::new(&mut self.store) .get_incoming_receipts_for_shard(shard_id, sync_hash, prev_chunk_height_included)? .clone(); + assert_eq!( + incoming_receipts_proofs.len(), + (sync_block_header.inner.height - prev_chunk_height_included) as usize + ); // Collecting proofs for incoming receipts. let mut root_proofs = vec![]; for receipt_response in incoming_receipts_proofs.iter() { let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; + let block_header = self.get_block_header(block_hash)?.clone(); + let block = self.get_block(&block_hash)?; + let mut root_proofs_cur = vec![]; + assert_eq!(receipt_proofs.len(), block_header.inner.chunks_included as usize); for receipt_proof in receipt_proofs { let ReceiptProof(receipts, ShardProof(from_shard_id, _, proof)) = receipt_proof; let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); let from_shard_id = *from_shard_id as usize; - let block = self.get_block(&block_hash)?; - // TODO assert that block.chunks[from_shard] is reachable? let chunk_header = block.chunks[from_shard_id].clone(); + let root_proof = chunk_header.inner.outgoing_receipts_root; let (block_receipts_root, block_receipts_proofs) = merklize( &block @@ -907,7 +914,7 @@ impl Chain { .push(RootProof(root_proof, block_receipts_proofs[from_shard_id].clone())); // Make sure we send something reasonable. - assert_eq!(block.header.inner.chunk_receipts_root, block_receipts_root); + assert_eq!(block_header.inner.chunk_receipts_root, block_receipts_root); assert!(verify_path(root_proof, &proof, &receipts_hash)); assert!(verify_path( block_receipts_root, @@ -1025,10 +1032,11 @@ impl Chain { // 3b. Checking that chunk `prev_chunk` is included into block at height before chunk.height_included // 3ba. Also checking prev_chunk.height_included - it's important for getting correct incoming receipts let prev_block_header = self.get_block_header(&block_header.inner.prev_hash)?.clone(); + let prev_chunk_height_included = prev_chunk_header.height_included; if !verify_path( prev_block_header.inner.chunk_headers_root, &prev_chunk_proof, - &ChunkHashHeight(prev_chunk_header.hash.clone(), prev_chunk_header.height_included), + &ChunkHashHeight(prev_chunk_header.hash.clone(), prev_chunk_height_included), ) { byzantine_assert!(false); return Err(ErrorKind::Other( @@ -1047,21 +1055,42 @@ impl Chain { } // 5. Proving incoming receipts validity - if incoming_receipts_proofs.len() != root_proofs.len() { + if root_proofs.len() != incoming_receipts_proofs.len() + || root_proofs.len() + != (sync_block_header.inner.height - prev_chunk_height_included) as usize + { byzantine_assert!(false); return Err(ErrorKind::Other("set_shard_state failed: invalid proofs".into()).into()); } for (i, receipt_response) in incoming_receipts_proofs.iter().enumerate() { let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; - if receipt_proofs.len() != root_proofs[i].len() { + let block_header = self.get_block_header(&block_hash)?; + if receipt_proofs.len() != root_proofs[i].len() + || receipt_proofs.len() != block_header.inner.chunks_included as usize + { byzantine_assert!(false); return Err( ErrorKind::Other("set_shard_state failed: invalid proofs".into()).into() ); } - + // We know there were exactly `block_header.inner.chunks_included` chunks included + // on the height of block `block_hash`. + // There were no other proofs except for included chunks. + // According to Dirichlet's principle, it's enough to ensure all receipt_proofs are distinct + // to prove that all receipts were received and no receipts were hidden. + let mut visited_shard_ids = HashSet::::new(); for (j, receipt_proof) in receipt_proofs.iter().enumerate() { - let ReceiptProof(receipts, ShardProof(_, _, proof)) = receipt_proof; + let ReceiptProof(receipts, ShardProof(from_shard_id, _, proof)) = receipt_proof; + match visited_shard_ids.get(from_shard_id) { + Some(_) => { + byzantine_assert!(false); + return Err(ErrorKind::Other( + "set_shard_state failed: invalid proofs".into(), + ) + .into()); + } + _ => visited_shard_ids.insert(*from_shard_id), + }; let RootProof(root, block_proof) = &root_proofs[i][j]; let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); if !verify_path(*root, &proof, &receipts_hash) { @@ -1070,7 +1099,6 @@ impl Chain { ErrorKind::Other("set_shard_state failed: invalid proofs".into()).into() ); } - let block_header = self.get_block_header(&block_hash)?; if !verify_path(block_header.inner.chunk_receipts_root, block_proof, root) { byzantine_assert!(false); return Err( diff --git a/chain/chain/src/store.rs b/chain/chain/src/store.rs index effe307abd5..1d0d2dd3a70 100644 --- a/chain/chain/src/store.rs +++ b/chain/chain/src/store.rs @@ -483,7 +483,10 @@ impl<'a, T: ChainStoreAccess> ChainStoreUpdate<'a, T> { loop { let header = self.get_block_header(&block_hash)?; - // TODO >= <= ? + if header.inner.height < last_chunk_height_included { + panic!("get_incoming_receipts_for_shard failed"); + } + if header.inner.height == last_chunk_height_included { break; } @@ -492,6 +495,8 @@ impl<'a, T: ChainStoreAccess> ChainStoreUpdate<'a, T> { if let Ok(receipt_proofs) = self.get_incoming_receipts(&block_hash, shard_id) { ret.push(ReceiptProofResponse(block_hash, receipt_proofs.clone())); + } else { + ret.push(ReceiptProofResponse(block_hash, vec![])); } block_hash = prev_hash; diff --git a/chain/client/tests/cross_shard_tx.rs b/chain/client/tests/cross_shard_tx.rs index 2fdf71aca73..9c33bbeb75e 100644 --- a/chain/client/tests/cross_shard_tx.rs +++ b/chain/client/tests/cross_shard_tx.rs @@ -439,7 +439,7 @@ mod tests { } // On X1 it takes ~1m 15s - near_network::test_utils::wait_or_panic(600000); + near_network::test_utils::wait_or_panic(120000); }) .unwrap(); } diff --git a/core/primitives/src/block.rs b/core/primitives/src/block.rs index 7fee0093f49..23f7ce080d1 100644 --- a/core/primitives/src/block.rs +++ b/core/primitives/src/block.rs @@ -32,6 +32,8 @@ pub struct BlockHeaderInner { pub chunk_headers_root: MerkleHash, /// Root hash of the chunk transactions in the given block. pub chunk_tx_root: MerkleHash, + /// Number of chunks included into the block. + pub chunks_included: u64, /// Timestamp at which the block was built. pub timestamp: u64, /// Approval mask, given current block producers. @@ -66,6 +68,7 @@ impl BlockHeaderInner { chunk_receipts_root: MerkleHash, chunk_headers_root: MerkleHash, chunk_tx_root: MerkleHash, + chunks_included: u64, time: DateTime, approval_mask: Vec, approval_sigs: Vec, @@ -87,6 +90,7 @@ impl BlockHeaderInner { chunk_receipts_root, chunk_headers_root, chunk_tx_root, + chunks_included, timestamp: to_timestamp(time), approval_mask, approval_sigs, @@ -129,6 +133,7 @@ impl BlockHeader { chunk_receipts_root: MerkleHash, chunk_headers_root: MerkleHash, chunk_tx_root: MerkleHash, + chunks_included: u64, timestamp: DateTime, approval_mask: Vec, approval_sigs: Vec, @@ -152,6 +157,7 @@ impl BlockHeader { chunk_receipts_root, chunk_headers_root, chunk_tx_root, + chunks_included, timestamp, approval_mask, approval_sigs, @@ -173,6 +179,7 @@ impl BlockHeader { chunk_receipts_root: MerkleHash, chunk_headers_root: MerkleHash, chunk_tx_root: MerkleHash, + chunks_included: u64, timestamp: DateTime, initial_gas_limit: Gas, initial_gas_price: Balance, @@ -187,6 +194,7 @@ impl BlockHeader { chunk_receipts_root, chunk_headers_root, chunk_tx_root, + chunks_included, timestamp, vec![], vec![], @@ -260,6 +268,7 @@ impl Block { Block::compute_chunk_receipts_root(&chunks), Block::compute_chunk_headers_root(&chunks), Block::compute_chunk_tx_root(&chunks), + Block::compute_chunks_included(&chunks, 0), timestamp, initial_gas_limit, initial_gas_price, @@ -332,6 +341,7 @@ impl Block { Block::compute_chunk_receipts_root(&chunks), Block::compute_chunk_headers_root(&chunks), Block::compute_chunk_tx_root(&chunks), + Block::compute_chunks_included(&chunks, height), Utc::now(), approval_mask, approval_sigs, @@ -387,6 +397,10 @@ impl Block { merklize(&chunks.iter().map(|chunk| chunk.inner.tx_root).collect::>()).0 } + pub fn compute_chunks_included(chunks: &Vec, height: BlockIndex) -> u64 { + chunks.iter().filter(|chunk| chunk.height_included == height).count() as u64 + } + pub fn hash(&self) -> CryptoHash { self.header.hash() } diff --git a/core/primitives/src/views.rs b/core/primitives/src/views.rs index 406914a4437..c98e772cb0b 100644 --- a/core/primitives/src/views.rs +++ b/core/primitives/src/views.rs @@ -282,6 +282,7 @@ pub struct BlockHeaderView { pub chunk_receipts_root: CryptoHashView, pub chunk_headers_root: CryptoHashView, pub chunk_tx_root: CryptoHashView, + pub chunks_included: u64, pub timestamp: u64, pub approval_mask: Vec, pub approval_sigs: Vec, @@ -311,6 +312,7 @@ impl From for BlockHeaderView { chunk_receipts_root: header.inner.chunk_receipts_root.into(), chunk_headers_root: header.inner.chunk_headers_root.into(), chunk_tx_root: header.inner.chunk_tx_root.into(), + chunks_included: header.inner.chunks_included.into(), timestamp: header.inner.timestamp, approval_mask: header.inner.approval_mask, approval_sigs: header.inner.approval_sigs, @@ -344,6 +346,7 @@ impl From for BlockHeader { chunk_receipts_root: view.chunk_receipts_root.into(), chunk_headers_root: view.chunk_headers_root.into(), chunk_tx_root: view.chunk_tx_root.into(), + chunks_included: view.chunks_included.into(), timestamp: view.timestamp, approval_mask: view.approval_mask, approval_sigs: view.approval_sigs, From c1fcf4185d08224c3ec1d7c7ad9fc4bf7f027388 Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Sat, 28 Sep 2019 00:07:14 +0500 Subject: [PATCH 06/10] updates - block skipping idea included into shard state proving - ShardState struct - enumeration and comments for proving activities under p. 5 - Dirichlet's -> Pigeonhole (for non-Russians) --- chain/chain/src/chain.rs | 83 ++++++++++++++++++-------------- chain/chain/src/types.rs | 39 ++++++++++++++- chain/client/src/client_actor.rs | 42 +++------------- chain/network/src/types.rs | 14 ++---- 4 files changed, 95 insertions(+), 83 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index 01c523ba360..14988b18224 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -8,12 +8,12 @@ use chrono::Duration; use log::{debug, info}; use near_primitives::hash::{hash, CryptoHash}; -use near_primitives::merkle::{merklize, verify_path, MerklePath}; +use near_primitives::merkle::{merklize, verify_path}; use near_primitives::receipt::Receipt; use near_primitives::sharding::{ ChunkHash, ChunkHashHeight, ReceiptProof, ShardChunk, ShardChunkHeader, ShardProof, }; -use near_primitives::transaction::{check_tx_history, ExecutionOutcome, SignedTransaction}; +use near_primitives::transaction::{check_tx_history, ExecutionOutcome}; use near_primitives::types::{AccountId, Balance, BlockIndex, ChunkExtra, Gas, ShardId}; use near_store::{Store, COL_CHUNKS}; @@ -22,7 +22,8 @@ use crate::error::{Error, ErrorKind}; use crate::store::{ChainStore, ChainStoreAccess, ChainStoreUpdate, ShardInfo, StateSyncInfo}; use crate::types::{ AcceptedBlock, Block, BlockHeader, BlockStatus, Provenance, ReceiptList, ReceiptProofResponse, - ReceiptResponse, RootProof, RuntimeAdapter, Tip, ValidatorSignatureVerificationResult, + ReceiptResponse, RootProof, RuntimeAdapter, ShardState, Tip, + ValidatorSignatureVerificationResult, }; /// Maximum number of orphans chain can store. @@ -803,19 +804,7 @@ impl Chain { &mut self, shard_id: ShardId, sync_hash: CryptoHash, - ) -> Result< - ( - ShardChunk, - MerklePath, - ShardChunkHeader, - MerklePath, - Vec, - Vec, - Vec, - Vec>, - ), - Error, - > { + ) -> Result { // Consistency rules: // 1. Everything prefixed with `sync_` indicates new epoch, for which we are syncing. // 1a. `sync_prev` means the last of the prev epoch. @@ -882,16 +871,12 @@ impl Chain { let incoming_receipts_proofs = ChainStoreUpdate::new(&mut self.store) .get_incoming_receipts_for_shard(shard_id, sync_hash, prev_chunk_height_included)? .clone(); - assert_eq!( - incoming_receipts_proofs.len(), - (sync_block_header.inner.height - prev_chunk_height_included) as usize - ); // Collecting proofs for incoming receipts. let mut root_proofs = vec![]; for receipt_response in incoming_receipts_proofs.iter() { let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; - let block_header = self.get_block_header(block_hash)?.clone(); + let block_header = self.get_block_header(&block_hash)?.clone(); let block = self.get_block(&block_hash)?; let mut root_proofs_cur = vec![]; @@ -925,7 +910,7 @@ impl Chain { root_proofs.push(root_proofs_cur); } - Ok(( + Ok(ShardState::new( chunk, chunk_proof, prev_chunk_header, @@ -942,14 +927,7 @@ impl Chain { _me: &Option, shard_id: ShardId, sync_hash: CryptoHash, - chunk: ShardChunk, - chunk_proof: MerklePath, - prev_chunk_header: ShardChunkHeader, - prev_chunk_proof: MerklePath, - prev_payload: Vec, - block_transactions: Vec, - incoming_receipts_proofs: Vec, - root_proofs: Vec>, + shard_state: ShardState, ) -> Result<(), Error> { // Ensure that sync_hash block is included into the canonical chain let sync_block_header = self.get_block_header(&sync_hash)?.clone(); @@ -963,6 +941,17 @@ impl Chain { .into()); } + let ShardState { + chunk, + chunk_proof, + prev_chunk_header, + prev_chunk_proof, + prev_payload, + block_transactions, + incoming_receipts_proofs, + root_proofs, + } = shard_state; + // 1. Checking that chunk header is at least valid // 1a. Checking chunk.header.hash if chunk.header.hash != ChunkHash(hash(&chunk.header.inner.try_to_vec()?)) { @@ -1055,16 +1044,28 @@ impl Chain { } // 5. Proving incoming receipts validity - if root_proofs.len() != incoming_receipts_proofs.len() - || root_proofs.len() - != (sync_block_header.inner.height - prev_chunk_height_included) as usize - { + // 5a. Checking len of proofs + if root_proofs.len() != incoming_receipts_proofs.len() { byzantine_assert!(false); return Err(ErrorKind::Other("set_shard_state failed: invalid proofs".into()).into()); } + let mut hash_to_compare = sync_hash; for (i, receipt_response) in incoming_receipts_proofs.iter().enumerate() { let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; + + // 5b. Checking that there is a valid sequence of continuous blocks + if *block_hash != hash_to_compare { + byzantine_assert!(false); + return Err(ErrorKind::Other( + "set_shard_state failed: invalid incoming receipts".into(), + ) + .into()); + } + let header = self.get_block_header(&hash_to_compare)?; + hash_to_compare = header.inner.prev_hash; + let block_header = self.get_block_header(&block_hash)?; + // 5c. Checking len of receipt_proofs for current block if receipt_proofs.len() != root_proofs[i].len() || receipt_proofs.len() != block_header.inner.chunks_included as usize { @@ -1076,11 +1077,12 @@ impl Chain { // We know there were exactly `block_header.inner.chunks_included` chunks included // on the height of block `block_hash`. // There were no other proofs except for included chunks. - // According to Dirichlet's principle, it's enough to ensure all receipt_proofs are distinct + // According to Pigeonhole principle, it's enough to ensure all receipt_proofs are distinct // to prove that all receipts were received and no receipts were hidden. let mut visited_shard_ids = HashSet::::new(); for (j, receipt_proof) in receipt_proofs.iter().enumerate() { let ReceiptProof(receipts, ShardProof(from_shard_id, _, proof)) = receipt_proof; + // 5d. Checking uniqueness for set of `from_shard_id` match visited_shard_ids.get(from_shard_id) { Some(_) => { byzantine_assert!(false); @@ -1093,12 +1095,14 @@ impl Chain { }; let RootProof(root, block_proof) = &root_proofs[i][j]; let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); + // 5e. Proving the set of receipts is the subset of outgoing_receipts of shard `shard_id` if !verify_path(*root, &proof, &receipts_hash) { byzantine_assert!(false); return Err( ErrorKind::Other("set_shard_state failed: invalid proofs".into()).into() ); } + // 5f. Proving the outgoing_receipts_root contains in the block if !verify_path(block_header.inner.chunk_receipts_root, block_proof, root) { byzantine_assert!(false); return Err( @@ -1107,6 +1111,15 @@ impl Chain { } } } + // 5g. Checking that there are no more heights to get incoming_receipts + let header = self.get_block_header(&hash_to_compare)?; + if header.inner.height != prev_chunk_height_included { + byzantine_assert!(false); + return Err(ErrorKind::Other( + "set_shard_state failed: invalid incoming receipts".into(), + ) + .into()); + } // 6. Proving prev_payload validity and setting it // Its hash should be equal to chunk prev_state_root. It is checked in set_state. diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs index 2060ad471ff..4bc9cd32659 100644 --- a/chain/chain/src/types.rs +++ b/chain/chain/src/types.rs @@ -7,7 +7,7 @@ pub use near_primitives::block::{Block, BlockHeader, Weight}; use near_primitives::hash::{hash, CryptoHash}; use near_primitives::merkle::MerklePath; use near_primitives::receipt::Receipt; -use near_primitives::sharding::{ReceiptProof, ShardChunkHeader}; +use near_primitives::sharding::{ReceiptProof, ShardChunk, ShardChunkHeader}; use near_primitives::transaction::{ExecutionOutcomeWithId, SignedTransaction}; use near_primitives::types::{ AccountId, Balance, BlockIndex, EpochId, Gas, MerkleHash, ShardId, ValidatorStake, @@ -373,6 +373,43 @@ impl BlockApproval { } } +/// Block approval by other block producers. +#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] +pub struct ShardState { + pub chunk: ShardChunk, + pub chunk_proof: MerklePath, + pub prev_chunk_header: ShardChunkHeader, + pub prev_chunk_proof: MerklePath, + pub prev_payload: Vec, + pub block_transactions: Vec, + pub incoming_receipts_proofs: Vec, + pub root_proofs: Vec>, +} + +impl ShardState { + pub fn new( + chunk: ShardChunk, + chunk_proof: MerklePath, + prev_chunk_header: ShardChunkHeader, + prev_chunk_proof: MerklePath, + prev_payload: Vec, + block_transactions: Vec, + incoming_receipts_proofs: Vec, + root_proofs: Vec>, + ) -> Self { + Self { + chunk, + chunk_proof, + prev_chunk_header, + prev_chunk_proof, + prev_payload, + block_transactions, + incoming_receipts_proofs, + root_proofs, + } + } +} + #[cfg(test)] mod tests { use std::sync::Arc; diff --git a/chain/client/src/client_actor.rs b/chain/client/src/client_actor.rs index 873d58abbf2..3c039430d8d 100644 --- a/chain/client/src/client_actor.rs +++ b/chain/client/src/client_actor.rs @@ -39,7 +39,8 @@ use crate::client::Client; use crate::info::InfoHelper; use crate::sync::{most_weight_peer, StateSync, StateSyncResult}; use crate::types::{ - BlockProducer, ClientConfig, Error, ShardSyncStatus, Status, StatusSyncInfo, SyncStatus, GetNetworkInfo, NetworkInfoResponse + BlockProducer, ClientConfig, Error, GetNetworkInfo, NetworkInfoResponse, ShardSyncStatus, + Status, StatusSyncInfo, SyncStatus, }; use crate::{sync, StatusResponse}; @@ -303,28 +304,11 @@ impl Handler for ClientActor { } } NetworkClientMessages::StateRequest(shard_id, hash) => { - if let Ok(( - chunk, - chunk_proof, - prev_chunk_header, - prev_chunk_proof, - prev_payload, - block_transactions, - incoming_receipts_proofs, - root_proofs, - )) = self.client.chain.get_state_for_shard(shard_id, hash) - { + if let Ok(shard_state) = self.client.chain.get_state_for_shard(shard_id, hash) { return NetworkClientResponses::StateResponse(StateResponseInfo { shard_id, hash, - chunk, - chunk_proof, - prev_chunk_header, - prev_chunk_proof, - prev_payload, - block_transactions, - incoming_receipts_proofs, - root_proofs, + shard_state, }); } NetworkClientResponses::NoResponse @@ -332,14 +316,7 @@ impl Handler for ClientActor { NetworkClientMessages::StateResponse(StateResponseInfo { shard_id, hash, - chunk, - chunk_proof, - prev_chunk_header, - prev_chunk_proof, - prev_payload, - block_transactions, - incoming_receipts_proofs, - root_proofs, + shard_state, }) => { // Populate the hashmaps with shard statuses that might be interested in this state // (naturally, the plural of statuses is statuseses) @@ -375,14 +352,7 @@ impl Handler for ClientActor { &self.client.block_producer.as_ref().map(|bp| bp.account_id.clone()), shard_id, hash, - chunk, - chunk_proof, - prev_chunk_header, - prev_chunk_proof, - prev_payload, - block_transactions, - incoming_receipts_proofs, - root_proofs, + shard_state, ) { Ok(()) => { for shard_statuses in shard_statuseses { diff --git a/chain/network/src/types.rs b/chain/network/src/types.rs index 131a3e605ae..081bd94455c 100644 --- a/chain/network/src/types.rs +++ b/chain/network/src/types.rs @@ -12,13 +12,12 @@ use borsh::{BorshDeserialize, BorshSerialize}; use chrono::{DateTime, Utc}; use tokio::net::TcpStream; -use near_chain::types::{ReceiptProofResponse, RootProof}; +use near_chain::types::ShardState; use near_chain::{Block, BlockApproval, BlockHeader, Weight}; use near_crypto::{PublicKey, ReadablePublicKey, SecretKey, Signature}; use near_primitives::hash::{hash, CryptoHash}; -use near_primitives::merkle::MerklePath; pub use near_primitives::sharding::ChunkPartMsg; -use near_primitives::sharding::{ChunkHash, ChunkOnePart, ShardChunk, ShardChunkHeader}; +use near_primitives::sharding::{ChunkHash, ChunkOnePart}; use near_primitives::transaction::SignedTransaction; use near_primitives::types::{AccountId, BlockIndex, EpochId, ShardId}; use near_primitives::utils::{from_timestamp, to_timestamp}; @@ -673,14 +672,7 @@ impl Message for NetworkRequests { pub struct StateResponseInfo { pub shard_id: ShardId, pub hash: CryptoHash, - pub chunk: ShardChunk, - pub chunk_proof: MerklePath, - pub prev_chunk_header: ShardChunkHeader, - pub prev_chunk_proof: MerklePath, - pub prev_payload: Vec, - pub block_transactions: Vec, - pub incoming_receipts_proofs: Vec, - pub root_proofs: Vec>, + pub shard_state: ShardState, } #[derive(Debug)] From 18a5f1548c91ccb7ee1b4215ce35ec5efaa59027 Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Sun, 29 Sep 2019 18:12:12 +0500 Subject: [PATCH 07/10] minors --- chain/chain/src/chain.rs | 12 ++++++------ chain/chain/src/types.rs | 4 ++-- chain/network/src/types.rs | 4 ++-- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index 14988b18224..171faa10da6 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -22,7 +22,7 @@ use crate::error::{Error, ErrorKind}; use crate::store::{ChainStore, ChainStoreAccess, ChainStoreUpdate, ShardInfo, StateSyncInfo}; use crate::types::{ AcceptedBlock, Block, BlockHeader, BlockStatus, Provenance, ReceiptList, ReceiptProofResponse, - ReceiptResponse, RootProof, RuntimeAdapter, ShardState, Tip, + ReceiptResponse, RootProof, RuntimeAdapter, ShardStateSyncResponse, Tip, ValidatorSignatureVerificationResult, }; @@ -804,7 +804,7 @@ impl Chain { &mut self, shard_id: ShardId, sync_hash: CryptoHash, - ) -> Result { + ) -> Result { // Consistency rules: // 1. Everything prefixed with `sync_` indicates new epoch, for which we are syncing. // 1a. `sync_prev` means the last of the prev epoch. @@ -910,7 +910,7 @@ impl Chain { root_proofs.push(root_proofs_cur); } - Ok(ShardState::new( + Ok(ShardStateSyncResponse::new( chunk, chunk_proof, prev_chunk_header, @@ -927,7 +927,7 @@ impl Chain { _me: &Option, shard_id: ShardId, sync_hash: CryptoHash, - shard_state: ShardState, + shard_state: ShardStateSyncResponse, ) -> Result<(), Error> { // Ensure that sync_hash block is included into the canonical chain let sync_block_header = self.get_block_header(&sync_hash)?.clone(); @@ -941,7 +941,7 @@ impl Chain { .into()); } - let ShardState { + let ShardStateSyncResponse { chunk, chunk_proof, prev_chunk_header, @@ -1102,7 +1102,7 @@ impl Chain { ErrorKind::Other("set_shard_state failed: invalid proofs".into()).into() ); } - // 5f. Proving the outgoing_receipts_root contains in the block + // 5f. Proving the outgoing_receipts_root matches that in the block if !verify_path(block_header.inner.chunk_receipts_root, block_proof, root) { byzantine_assert!(false); return Err( diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs index 4bc9cd32659..4b6e30d0ccd 100644 --- a/chain/chain/src/types.rs +++ b/chain/chain/src/types.rs @@ -375,7 +375,7 @@ impl BlockApproval { /// Block approval by other block producers. #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] -pub struct ShardState { +pub struct ShardStateSyncResponse { pub chunk: ShardChunk, pub chunk_proof: MerklePath, pub prev_chunk_header: ShardChunkHeader, @@ -386,7 +386,7 @@ pub struct ShardState { pub root_proofs: Vec>, } -impl ShardState { +impl ShardStateSyncResponse { pub fn new( chunk: ShardChunk, chunk_proof: MerklePath, diff --git a/chain/network/src/types.rs b/chain/network/src/types.rs index 081bd94455c..ea8e9c7282e 100644 --- a/chain/network/src/types.rs +++ b/chain/network/src/types.rs @@ -12,7 +12,7 @@ use borsh::{BorshDeserialize, BorshSerialize}; use chrono::{DateTime, Utc}; use tokio::net::TcpStream; -use near_chain::types::ShardState; +use near_chain::types::ShardStateSyncResponse; use near_chain::{Block, BlockApproval, BlockHeader, Weight}; use near_crypto::{PublicKey, ReadablePublicKey, SecretKey, Signature}; use near_primitives::hash::{hash, CryptoHash}; @@ -672,7 +672,7 @@ impl Message for NetworkRequests { pub struct StateResponseInfo { pub shard_id: ShardId, pub hash: CryptoHash, - pub shard_state: ShardState, + pub shard_state: ShardStateSyncResponse, } #[derive(Debug)] From 9b86a335c4ceb508ac91f052377a841509284949 Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Mon, 30 Sep 2019 20:37:16 +0500 Subject: [PATCH 08/10] minors - check_block_validity - moving some calculation outside the loop - removed hash check - assert -> byzantine_assert --- chain/chain/src/chain.rs | 75 ++++++---------------------------------- chain/chain/src/store.rs | 37 +++++++++++++++++++- 2 files changed, 47 insertions(+), 65 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index 171faa10da6..d1bdebfbd57 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -288,24 +288,7 @@ impl Chain { pub fn save_block(&mut self, block: &Block) -> Result<(), Error> { let mut chain_store_update = ChainStoreUpdate::new(&mut self.store); - // Before saving the very first block in the epoch, - // we ensure that the block contains the correct data. - // It is sufficient to check the correctness of tx_root, state_root - // of the block matches the stated hash - // - // NOTE: we don't need to re-compute hash of entire block - // because we always call BlockHeader::init() when we received a block - // - // 1. Checking state_root validity - let state_root = Block::compute_state_root(&block.chunks); - if block.header.inner.prev_state_root != state_root { - return Err(ErrorKind::InvalidStateRoot.into()); - } - // 2. Checking tx_root validity - let tx_root = Block::compute_tx_root(&block.transactions); - if block.header.inner.tx_root != tx_root { - return Err(ErrorKind::InvalidTxRoot.into()); - } + chain_store_update.check_block_validity(block)?; chain_store_update.save_block(block.clone()); @@ -878,6 +861,13 @@ impl Chain { let ReceiptProofResponse(block_hash, receipt_proofs) = receipt_response; let block_header = self.get_block_header(&block_hash)?.clone(); let block = self.get_block(&block_hash)?; + let (block_receipts_root, block_receipts_proofs) = merklize( + &block + .chunks + .iter() + .map(|chunk| chunk.inner.outgoing_receipts_root) + .collect::>(), + ); let mut root_proofs_cur = vec![]; assert_eq!(receipt_proofs.len(), block_header.inner.chunks_included as usize); @@ -885,16 +875,8 @@ impl Chain { let ReceiptProof(receipts, ShardProof(from_shard_id, _, proof)) = receipt_proof; let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); let from_shard_id = *from_shard_id as usize; - let chunk_header = block.chunks[from_shard_id].clone(); - let root_proof = chunk_header.inner.outgoing_receipts_root; - let (block_receipts_root, block_receipts_proofs) = merklize( - &block - .chunks - .iter() - .map(|chunk| chunk.inner.outgoing_receipts_root) - .collect::>(), - ); + let root_proof = block.chunks[from_shard_id].inner.outgoing_receipts_root; root_proofs_cur .push(RootProof(root_proof, block_receipts_proofs[from_shard_id].clone())); @@ -953,14 +935,7 @@ impl Chain { } = shard_state; // 1. Checking that chunk header is at least valid - // 1a. Checking chunk.header.hash - if chunk.header.hash != ChunkHash(hash(&chunk.header.inner.try_to_vec()?)) { - byzantine_assert!(false); - return Err(ErrorKind::Other( - "set_shard_state failed: chunk header hash is broken".into(), - ) - .into()); - } + // 1a. Checking chunk.header.hash - don't check here, hash is computed locally // 1b. Checking signature if !self.runtime_adapter.verify_chunk_header_signature(&chunk.header)? { byzantine_assert!(false); @@ -1831,35 +1806,7 @@ impl<'a> ChainUpdate<'a> { // Check the header is valid before we proceed with the full block. self.process_header_for_block(&block.header, provenance)?; - // Check that state root stored in the header matches the state root of the chunks - let state_root = Block::compute_state_root(&block.chunks); - if block.header.inner.prev_state_root != state_root { - return Err(ErrorKind::InvalidStateRoot.into()); - } - - // Check that tx root stored in the header matches the state root of the block transactions - let tx_root = Block::compute_tx_root(&block.transactions); - if block.header.inner.tx_root != tx_root { - return Err(ErrorKind::InvalidTxRoot.into()); - } - - // Check that chunk receipts root stored in the header matches the state root of the chunks - let chunk_receipts_root = Block::compute_chunk_receipts_root(&block.chunks); - if block.header.inner.chunk_receipts_root != chunk_receipts_root { - return Err(ErrorKind::InvalidChunkReceiptsRoot.into()); - } - - // Check that chunk headers root stored in the header matches the state root of the chunks - let chunk_headers_root = Block::compute_chunk_headers_root(&block.chunks); - if block.header.inner.chunk_headers_root != chunk_headers_root { - return Err(ErrorKind::InvalidChunkHeadersRoot.into()); - } - - // Check that chunk headers root stored in the header matches the state root of the chunks - let chunk_tx_root = Block::compute_chunk_tx_root(&block.chunks); - if block.header.inner.chunk_tx_root != chunk_tx_root { - return Err(ErrorKind::InvalidChunkTxRoot.into()); - } + self.chain_store_update.check_block_validity(block)?; let prev_block = self.chain_store_update.get_block(&prev_hash)?.clone(); diff --git a/chain/chain/src/store.rs b/chain/chain/src/store.rs index 1d0d2dd3a70..8bfbdb09a7d 100644 --- a/chain/chain/src/store.rs +++ b/chain/chain/src/store.rs @@ -23,6 +23,7 @@ use near_store::{ COL_TRANSACTION_RESULT, }; +use crate::byzantine_assert; use crate::error::{Error, ErrorKind}; use crate::types::{Block, BlockHeader, LatestKnown, ReceiptProofResponse, ReceiptResponse, Tip}; use chrono::Utc; @@ -84,7 +85,7 @@ pub trait ChainStoreAccess { return Err(ErrorKind::ChunksMissing(vec![header.clone()]).into()); } Ok(shard_chunk) => { - assert_ne!(header.height_included, 0); + byzantine_assert!(header.height_included > 0); if header.height_included == 0 { return Err(ErrorKind::Other(format!( "Invalid header: {:?} for chunk {:?}", @@ -949,4 +950,38 @@ impl<'a, T: ChainStoreAccess> ChainStoreUpdate<'a, T> { let store_update = self.finalize()?; store_update.commit().map_err(|e| e.into()) } + + pub fn check_block_validity(&mut self, block: &Block) -> Result<(), Error> { + // Check that state root stored in the header matches the state root of the chunks + let state_root = Block::compute_state_root(&block.chunks); + if block.header.inner.prev_state_root != state_root { + return Err(ErrorKind::InvalidStateRoot.into()); + } + + // Check that tx root stored in the header matches the state root of the block transactions + let tx_root = Block::compute_tx_root(&block.transactions); + if block.header.inner.tx_root != tx_root { + return Err(ErrorKind::InvalidTxRoot.into()); + } + + // Check that chunk receipts root stored in the header matches the state root of the chunks + let chunk_receipts_root = Block::compute_chunk_receipts_root(&block.chunks); + if block.header.inner.chunk_receipts_root != chunk_receipts_root { + return Err(ErrorKind::InvalidChunkReceiptsRoot.into()); + } + + // Check that chunk headers root stored in the header matches the state root of the chunks + let chunk_headers_root = Block::compute_chunk_headers_root(&block.chunks); + if block.header.inner.chunk_headers_root != chunk_headers_root { + return Err(ErrorKind::InvalidChunkHeadersRoot.into()); + } + + // Check that chunk headers root stored in the header matches the state root of the chunks + let chunk_tx_root = Block::compute_chunk_tx_root(&block.chunks); + if block.header.inner.chunk_tx_root != chunk_tx_root { + return Err(ErrorKind::InvalidChunkTxRoot.into()); + } + + Ok(()) + } } From a54a3443135a5ae40d4edf8011151be18194ff16 Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Tue, 1 Oct 2019 09:34:48 +0500 Subject: [PATCH 09/10] checks chunks_included_root --- chain/chain/src/store.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/chain/chain/src/store.rs b/chain/chain/src/store.rs index 45a2d568c11..98fb6b8894b 100644 --- a/chain/chain/src/store.rs +++ b/chain/chain/src/store.rs @@ -1011,18 +1011,25 @@ impl<'a, T: ChainStoreAccess> ChainStoreUpdate<'a, T> { return Err(ErrorKind::InvalidChunkReceiptsRoot.into()); } - // Check that chunk headers root stored in the header matches the state root of the chunks + // Check that chunk headers root stored in the header matches the chunk headers root of the chunks let chunk_headers_root = Block::compute_chunk_headers_root(&block.chunks); if block.header.inner.chunk_headers_root != chunk_headers_root { return Err(ErrorKind::InvalidChunkHeadersRoot.into()); } - // Check that chunk headers root stored in the header matches the state root of the chunks + // Check that chunk tx root stored in the header matches the tx root of the chunks let chunk_tx_root = Block::compute_chunk_tx_root(&block.chunks); if block.header.inner.chunk_tx_root != chunk_tx_root { return Err(ErrorKind::InvalidChunkTxRoot.into()); } + // Check that chunk included root stored in the header matches the chunk included root of the chunks + let chunks_included_root = + Block::compute_chunks_included(&block.chunks, block.header.inner.height); + if block.header.inner.chunks_included != chunks_included_root { + return Err(ErrorKind::InvalidChunkHeadersRoot.into()); + } + Ok(()) } } From e3bdcd7cf024181cff045f2fab9e24a9fa356027 Mon Sep 17 00:00:00 2001 From: Alex Kouprin Date: Tue, 1 Oct 2019 20:15:56 +0500 Subject: [PATCH 10/10] mostly minors related to Illia's review --- chain/chain/src/chain.rs | 60 +++++++++------------------------ chain/chain/src/store.rs | 35 ------------------- chain/chain/src/types.rs | 46 ++++++++++++++++++++++--- chain/chunks/src/lib.rs | 10 ++++-- core/primitives/src/block.rs | 35 +++++++++++++++++++ core/primitives/src/sharding.rs | 7 ++-- 6 files changed, 104 insertions(+), 89 deletions(-) diff --git a/chain/chain/src/chain.rs b/chain/chain/src/chain.rs index ea1ab024767..9d7fa975bff 100644 --- a/chain/chain/src/chain.rs +++ b/chain/chain/src/chain.rs @@ -288,7 +288,10 @@ impl Chain { pub fn save_block(&mut self, block: &Block) -> Result<(), Error> { let mut chain_store_update = ChainStoreUpdate::new(&mut self.store); - chain_store_update.check_block_validity(block)?; + if !block.check_validity() { + byzantine_assert!(false); + return Err(ErrorKind::Other("Invalid block".into()).into()); + } chain_store_update.save_block(block.clone()); @@ -887,7 +890,8 @@ impl Chain { let mut root_proofs_cur = vec![]; assert_eq!(receipt_proofs.len(), block_header.inner.chunks_included as usize); for receipt_proof in receipt_proofs { - let ReceiptProof(receipts, ShardProof(from_shard_id, _, proof)) = receipt_proof; + let ReceiptProof(receipts, shard_proof) = receipt_proof; + let ShardProof { from_shard_id, to_shard_id: _, proof } = shard_proof; let receipts_hash = hash(&ReceiptList(shard_id, receipts.to_vec()).try_to_vec()?); let from_shard_id = *from_shard_id as usize; @@ -947,45 +951,8 @@ impl Chain { root_proofs, } = shard_state; - // 1. Checking that chunk header is at least valid - // 1a. Checking chunk.header.hash - don't check here, hash is computed locally - // 1b. Checking signature - if !self.runtime_adapter.verify_chunk_header_signature(&chunk.header)? { - byzantine_assert!(false); - return Err(ErrorKind::Other( - "set_shard_state failed: incorrect chunk signature".to_string(), - ) - .into()); - } - - // 2. Checking that chunk body is at least valid - // 2a. Checking chunk hash - if chunk.chunk_hash != chunk.header.hash { - byzantine_assert!(false); - return Err( - ErrorKind::Other("set_shard_state failed: chunk hash is broken".into()).into() - ); - } - // 2b. Checking that chunk transactions are valid - let (tx_root, _) = merklize(&chunk.transactions); - if tx_root != chunk.header.inner.tx_root { - byzantine_assert!(false); - return Err( - ErrorKind::Other("set_shard_state failed: chunk tx_root is broken".into()).into() - ); - } - // 2c. Checking that chunk receipts are valid - let outgoing_receipts_hashes = - self.runtime_adapter.build_receipts_hashes(&chunk.receipts)?; - let (receipts_root, _) = merklize(&outgoing_receipts_hashes); - if receipts_root != chunk.header.inner.outgoing_receipts_root { - byzantine_assert!(false); - return Err(ErrorKind::Other( - "set_shard_state failed: chunk receipts_root is broken".into(), - ) - .into()); - } - + // 1-2. Checking chunk validity + self.runtime_adapter.check_chunk_validity(&chunk)?; // Consider chunk itself is valid. // 3. Checking that chunks `chunk` and `prev_chunk` are included in appropriate blocks @@ -1060,7 +1027,8 @@ impl Chain { // to prove that all receipts were received and no receipts were hidden. let mut visited_shard_ids = HashSet::::new(); for (j, receipt_proof) in receipt_proofs.iter().enumerate() { - let ReceiptProof(receipts, ShardProof(from_shard_id, _, proof)) = receipt_proof; + let ReceiptProof(receipts, shard_proof) = receipt_proof; + let ShardProof { from_shard_id, to_shard_id: _, proof } = shard_proof; // 4d. Checking uniqueness for set of `from_shard_id` match visited_shard_ids.get(from_shard_id) { Some(_) => { @@ -1562,7 +1530,8 @@ impl<'a> ChainUpdate<'a> { if chunk_header.height_included == height { let one_part = self.chain_store_update.get_chunk_one_part(chunk_header).unwrap(); for receipt_proof in one_part.receipt_proofs.iter() { - let ReceiptProof(_, ShardProof(_, to_shard_id, _)) = receipt_proof; + let ReceiptProof(_, shard_proof) = receipt_proof; + let ShardProof { from_shard_id: _, to_shard_id, proof: _ } = shard_proof; receipt_proofs_by_shard_id .entry(*to_shard_id) .or_insert_with(Vec::new) @@ -1800,7 +1769,10 @@ impl<'a> ChainUpdate<'a> { // Check the header is valid before we proceed with the full block. self.process_header_for_block(&block.header, provenance)?; - self.chain_store_update.check_block_validity(block)?; + if !block.check_validity() { + byzantine_assert!(false); + return Err(ErrorKind::Other("Invalid block".into()).into()); + } let prev_block = self.chain_store_update.get_block(&prev_hash)?.clone(); diff --git a/chain/chain/src/store.rs b/chain/chain/src/store.rs index 98fb6b8894b..60b274ec988 100644 --- a/chain/chain/src/store.rs +++ b/chain/chain/src/store.rs @@ -997,39 +997,4 @@ impl<'a, T: ChainStoreAccess> ChainStoreUpdate<'a, T> { let store_update = self.finalize()?; store_update.commit().map_err(|e| e.into()) } - - pub fn check_block_validity(&mut self, block: &Block) -> Result<(), Error> { - // Check that state root stored in the header matches the state root of the chunks - let state_root = Block::compute_state_root(&block.chunks); - if block.header.inner.prev_state_root != state_root { - return Err(ErrorKind::InvalidStateRoot.into()); - } - - // Check that chunk receipts root stored in the header matches the state root of the chunks - let chunk_receipts_root = Block::compute_chunk_receipts_root(&block.chunks); - if block.header.inner.chunk_receipts_root != chunk_receipts_root { - return Err(ErrorKind::InvalidChunkReceiptsRoot.into()); - } - - // Check that chunk headers root stored in the header matches the chunk headers root of the chunks - let chunk_headers_root = Block::compute_chunk_headers_root(&block.chunks); - if block.header.inner.chunk_headers_root != chunk_headers_root { - return Err(ErrorKind::InvalidChunkHeadersRoot.into()); - } - - // Check that chunk tx root stored in the header matches the tx root of the chunks - let chunk_tx_root = Block::compute_chunk_tx_root(&block.chunks); - if block.header.inner.chunk_tx_root != chunk_tx_root { - return Err(ErrorKind::InvalidChunkTxRoot.into()); - } - - // Check that chunk included root stored in the header matches the chunk included root of the chunks - let chunks_included_root = - Block::compute_chunks_included(&block.chunks, block.header.inner.height); - if block.header.inner.chunks_included != chunks_included_root { - return Err(ErrorKind::InvalidChunkHeadersRoot.into()); - } - - Ok(()) - } } diff --git a/chain/chain/src/types.rs b/chain/chain/src/types.rs index a76b7e01c67..ca6d39a6b54 100644 --- a/chain/chain/src/types.rs +++ b/chain/chain/src/types.rs @@ -4,10 +4,11 @@ use borsh::{BorshDeserialize, BorshSerialize}; use near_crypto::{BlsSignature, BlsSigner}; pub use near_primitives::block::{Block, BlockHeader, Weight}; +use near_primitives::errors::InvalidTxErrorOrStorageError; use near_primitives::hash::{hash, CryptoHash}; -use near_primitives::merkle::MerklePath; +use near_primitives::merkle::{merklize, MerklePath}; use near_primitives::receipt::Receipt; -use near_primitives::sharding::{ReceiptProof, ShardChunk, ShardChunkHeader}; +use near_primitives::sharding::{ChunkHash, ReceiptProof, ShardChunk, ShardChunkHeader}; use near_primitives::transaction::{ExecutionOutcomeWithId, SignedTransaction}; use near_primitives::types::{ AccountId, Balance, BlockIndex, EpochId, Gas, MerkleHash, ShardId, ValidatorStake, @@ -15,8 +16,8 @@ use near_primitives::types::{ use near_primitives::views::QueryResponse; use near_store::{PartialStorage, StoreUpdate, WrappedTrieChanges}; -use crate::error::Error; -use near_primitives::errors::InvalidTxErrorOrStorageError; +use crate::byzantine_assert; +use crate::error::{Error, ErrorKind}; #[derive(PartialEq, Eq, Clone, Debug, BorshSerialize, BorshDeserialize)] pub struct ReceiptResponse(pub CryptoHash, pub Vec); @@ -322,6 +323,42 @@ pub trait RuntimeAdapter: Send + Sync { } Ok(receipts_hashes) } + + /// Check chunk validity. + fn check_chunk_validity(&self, chunk: &ShardChunk) -> Result<(), Error> { + // 1. Checking that chunk header is valid + // 1a. Checking chunk.header.hash + if chunk.header.hash != ChunkHash(hash(&chunk.header.inner.try_to_vec()?)) { + byzantine_assert!(false); + return Err(ErrorKind::Other("Incorrect chunk hash".to_string()).into()); + } + // 1b. Checking signature + if !self.verify_chunk_header_signature(&chunk.header)? { + byzantine_assert!(false); + return Err(ErrorKind::Other("Incorrect chunk signature".to_string()).into()); + } + // 2. Checking that chunk body is valid + // 2a. Checking chunk hash + if chunk.chunk_hash != chunk.header.hash { + byzantine_assert!(false); + return Err(ErrorKind::Other("Incorrect chunk hash".to_string()).into()); + } + // 2b. Checking that chunk transactions are valid + let (tx_root, _) = merklize(&chunk.transactions); + if tx_root != chunk.header.inner.tx_root { + byzantine_assert!(false); + return Err(ErrorKind::Other("Incorrect chunk tx_root".to_string()).into()); + } + // 2c. Checking that chunk receipts are valid + let outgoing_receipts_hashes = self.build_receipts_hashes(&chunk.receipts)?; + let (receipts_root, _) = merklize(&outgoing_receipts_hashes); + if receipts_root != chunk.header.inner.outgoing_receipts_root { + byzantine_assert!(false); + return Err(ErrorKind::Other("Incorrect chunk receipts root".to_string()).into()); + } + + Ok(()) + } } #[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Default)] @@ -380,7 +417,6 @@ impl BlockApproval { } } -/// Block approval by other block producers. #[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize)] pub struct ShardStateSyncResponse { pub chunk: ShardChunk, diff --git a/chain/chunks/src/lib.rs b/chain/chunks/src/lib.rs index bff3d991047..75e9cabc04a 100644 --- a/chain/chunks/src/lib.rs +++ b/chain/chunks/src/lib.rs @@ -379,7 +379,11 @@ impl ShardsManager { }) .cloned() .collect(), - ShardProof(from_shard_id, to_shard_id, proofs[to_shard_id as usize].clone()), + ShardProof { + from_shard_id, + to_shard_id, + proof: proofs[to_shard_id as usize].clone(), + }, )) } } @@ -600,10 +604,10 @@ impl ShardsManager { true, ) { if proof_index == one_part.receipt_proofs.len() - || shard_id != (one_part.receipt_proofs[proof_index].1).1 + || shard_id != (one_part.receipt_proofs[proof_index].1).to_shard_id || !verify_path( one_part.header.inner.outgoing_receipts_root, - &(one_part.receipt_proofs[proof_index].1).2, + &(one_part.receipt_proofs[proof_index].1).proof, &receipts_hashes[shard_id as usize], ) { diff --git a/core/primitives/src/block.rs b/core/primitives/src/block.rs index 05d5b79a848..88ced788bf0 100644 --- a/core/primitives/src/block.rs +++ b/core/primitives/src/block.rs @@ -391,6 +391,41 @@ impl Block { pub fn hash(&self) -> CryptoHash { self.header.hash() } + + pub fn check_validity(&self) -> bool { + // Check that state root stored in the header matches the state root of the chunks + let state_root = Block::compute_state_root(&self.chunks); + if self.header.inner.prev_state_root != state_root { + return false; + } + + // Check that chunk receipts root stored in the header matches the state root of the chunks + let chunk_receipts_root = Block::compute_chunk_receipts_root(&self.chunks); + if self.header.inner.chunk_receipts_root != chunk_receipts_root { + return false; + } + + // Check that chunk headers root stored in the header matches the chunk headers root of the chunks + let chunk_headers_root = Block::compute_chunk_headers_root(&self.chunks); + if self.header.inner.chunk_headers_root != chunk_headers_root { + return false; + } + + // Check that chunk tx root stored in the header matches the tx root of the chunks + let chunk_tx_root = Block::compute_chunk_tx_root(&self.chunks); + if self.header.inner.chunk_tx_root != chunk_tx_root { + return false; + } + + // Check that chunk included root stored in the header matches the chunk included root of the chunks + let chunks_included_root = + Block::compute_chunks_included(&self.chunks, self.header.inner.height); + if self.header.inner.chunks_included != chunks_included_root { + return false; + } + + true + } } /// The weight is defined as the number of unique validators approving this fork. diff --git a/core/primitives/src/sharding.rs b/core/primitives/src/sharding.rs index 428577c71fb..178abe354ef 100644 --- a/core/primitives/src/sharding.rs +++ b/core/primitives/src/sharding.rs @@ -117,8 +117,11 @@ pub struct ChunkOnePart { } #[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] -// from_shard_id -> to_shard_id -pub struct ShardProof(pub ShardId, pub ShardId, pub MerklePath); +pub struct ShardProof { + pub from_shard_id: ShardId, + pub to_shard_id: ShardId, + pub proof: MerklePath, +} #[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)] // For each Merkle proof there is a subset of receipts which may be proven.