Skip to content

Commit

Permalink
impl
Browse files Browse the repository at this point in the history
  • Loading branch information
pugachAG committed Sep 27, 2023
1 parent 1d66984 commit 5315641
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 14 deletions.
68 changes: 67 additions & 1 deletion chain/chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ use near_primitives::types::{
};
use near_primitives::unwrap_or_return;
use near_primitives::utils::MaybeValidated;
use near_primitives::version::PROTOCOL_VERSION;
use near_primitives::version::{PROTOCOL_VERSION, ProtocolVersion};
use near_primitives::views::{
BlockStatusView, DroppedReason, ExecutionOutcomeWithIdView, ExecutionStatusView,
FinalExecutionOutcomeView, FinalExecutionOutcomeWithReceiptView, FinalExecutionStatus,
Expand Down Expand Up @@ -832,6 +832,72 @@ impl Chain {
Ok(())
}

pub fn apply_chunk_for_post_state_root(
&self,
shard_id: ShardId,
epoch_id: &EpochId,
prev_state_root: StateRoot,
block_height: BlockHeight,
prev_block_header: &BlockHeader,
transactions: &[SignedTransaction],
last_validator_proposals: near_primitives::types::validator_stake::ValidatorStakeIter,
gas_price: Balance,
gas_limit: Gas,
last_chunk_height_included: BlockHeight,
) -> Result<ApplyTransactionResult, Error> {
let prev_block_hash = *prev_block_header.hash();
let shard_layout = self.epoch_manager.get_shard_layout(epoch_id)?;
let is_first_block_with_chunk_of_version = check_if_block_is_first_with_chunk_of_version(
self.store(),
self.epoch_manager.as_ref(),
&prev_block_hash,
shard_id,
)?;
let mut new_receipts = Vec::new();
for (from_shard_id, &chunk_included) in prev_block_header.chunk_mask().iter().enumerate() {
if chunk_included {
for receipt in self.store.get_outgoing_receipts(&prev_block_hash, from_shard_id as ShardId).unwrap().iter() {
if account_id_to_shard_id(&receipt.receiver_id, &shard_layout) == shard_id {
new_receipts.push(receipt.clone());
}
}
}
}
// TODO(post-state-root): shuffle new_receipts
let old_receipts =
collect_receipts_from_response(&self.store.get_incoming_receipts_for_shard(
self.epoch_manager.as_ref(),
shard_id,
prev_block_hash,
last_chunk_height_included,
)?);
let receipts = [new_receipts, old_receipts].concat();
// TODO(post-state-root): block-level fields
let block_timestamp = 0;
let block_hash = CryptoHash::default();
let random_seed = CryptoHash::default();

self.runtime_adapter.apply_transactions(
shard_id,
&prev_state_root,
block_height,
block_timestamp,
&prev_block_hash,
&block_hash,
&receipts,
transactions,
last_validator_proposals,
gas_price,
gas_limit,
&vec![],
random_seed,
true,
is_first_block_with_chunk_of_version,
Default::default(),
true,
)
}

pub fn save_orphan(
&mut self,
block: MaybeValidated<Block>,
Expand Down
171 changes: 158 additions & 13 deletions chain/client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ use near_chain::flat_storage_creator::FlatStorageCreator;
use near_chain::resharding::StateSplitRequest;
use near_chain::state_snapshot_actor::MakeSnapshotCallback;
use near_chain::test_utils::format_hash;
use near_chain::types::ApplyTransactionResult;
use near_chain::types::RuntimeAdapter;
use near_chain::types::{ChainConfig, LatestKnown};
use near_chain::{
Expand Down Expand Up @@ -55,6 +56,7 @@ use near_primitives::hash::CryptoHash;
use near_primitives::merkle::{merklize, MerklePath, PartialMerkleTree};
use near_primitives::network::PeerId;
use near_primitives::receipt::Receipt;
use near_primitives::shard_layout::account_id_to_shard_id;
use near_primitives::sharding::StateSyncInfo;
use near_primitives::sharding::{
ChunkHash, EncodedShardChunk, PartialEncodedChunk, ReedSolomonWrapper, ShardChunk,
Expand All @@ -63,6 +65,8 @@ use near_primitives::sharding::{
use near_primitives::static_clock::StaticClock;
use near_primitives::transaction::SignedTransaction;
use near_primitives::types::chunk_extra::ChunkExtra;
use near_primitives::types::Gas;
use near_primitives::types::StateRoot;
use near_primitives::types::{AccountId, ApprovalStake, BlockHeight, EpochId, NumBlocks, ShardId};
use near_primitives::unwrap_or_return;
use near_primitives::utils::MaybeValidated;
Expand Down Expand Up @@ -801,15 +805,48 @@ impl Client {
validator_signer.validator_id()
);

let ret = self.produce_pre_state_root_chunk(
validator_signer.as_ref(),
prev_block_hash,
epoch_id,
last_header,
next_height,
shard_id,
)?;

metrics::CHUNK_PRODUCED_TOTAL.inc();
self.chunk_production_info.put(
(next_height, shard_id),
ChunkProduction {
chunk_production_time: Some(StaticClock::utc()),
chunk_production_duration_millis: Some(timer.elapsed().as_millis() as u64),
},
);
Ok(Some(ret))
}

fn produce_pre_state_root_chunk(
&mut self,
validator_signer: &dyn ValidatorSigner,
prev_block_hash: CryptoHash,
epoch_id: &EpochId,
last_header: ShardChunkHeader,
next_height: BlockHeight,
shard_id: ShardId,
) -> Result<(EncodedShardChunk, Vec<MerklePath>, Vec<Receipt>), Error> {
let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, epoch_id)?;
let chunk_extra = self
.chain
.get_chunk_extra(&prev_block_hash, &shard_uid)
.map_err(|err| Error::ChunkProducer(format!("No chunk extra available: {}", err)))?;

let prev_block_header = self.chain.get_block_header(&prev_block_hash)?;
let transactions =
self.prepare_transactions(shard_uid, &chunk_extra, &prev_block_header)?;
let transactions = self.prepare_transactions(
shard_uid,
chunk_extra.gas_limit(),
*chunk_extra.state_root(),
&prev_block_header,
)?;
let transactions = transactions;
#[cfg(feature = "test_features")]
let transactions = Self::maybe_insert_invalid_transaction(
Expand Down Expand Up @@ -875,15 +912,122 @@ impl Client {
outgoing_receipts.len(),
);

metrics::CHUNK_PRODUCED_TOTAL.inc();
self.chunk_production_info.put(
(next_height, shard_id),
ChunkProduction {
chunk_production_time: Some(StaticClock::utc()),
chunk_production_duration_millis: Some(timer.elapsed().as_millis() as u64),
},
Ok((encoded_chunk, merkle_paths, outgoing_receipts))
}

fn produce_post_state_root_chunk(
&mut self,
validator_signer: &dyn ValidatorSigner,
prev_block_hash: CryptoHash,
epoch_id: &EpochId,
last_header: ShardChunkHeader,
next_height: BlockHeight,
shard_id: ShardId,
) -> Result<Option<(EncodedShardChunk, Vec<MerklePath>, Vec<Receipt>)>, Error> {
let shard_uid = self.epoch_manager.shard_id_to_uid(shard_id, epoch_id)?;
let prev_block_header = self.chain.get_block_header(&prev_block_hash)?;
let protocol_version = self.epoch_manager.get_epoch_protocol_version(&epoch_id)?;
let gas_limit;
let prev_gas_used;
let prev_state_root;
let prev_validator_proposals;
let prev_outcome_root;
match &last_header {
ShardChunkHeader::V3(near_primitives::sharding::ShardChunkHeaderV3 {
inner: near_primitives::sharding::ShardChunkHeaderInner::V3(header_inner),
..
}) => {
gas_limit = 0;
prev_gas_used = 0;
prev_state_root = StateRoot::default();
prev_validator_proposals =
near_primitives::types::validator_stake::ValidatorStakeIter::empty();
prev_outcome_root = CryptoHash::default();
}
_ => {
let chunk_extra =
self.chain.get_chunk_extra(&prev_block_hash, &shard_uid).map_err(|err| {
Error::ChunkProducer(format!("No chunk extra available: {}", err))
})?;
gas_limit = chunk_extra.gas_limit();
prev_gas_used = chunk_extra.gas_used();
prev_state_root = *chunk_extra.state_root();
prev_validator_proposals = chunk_extra.validator_proposals();
prev_outcome_root = *chunk_extra.outcome_root();
}
}

let transactions =
self.prepare_transactions(shard_uid, gas_limit, prev_state_root, &prev_block_header)?;
#[cfg(feature = "test_features")]
let transactions = Self::maybe_insert_invalid_transaction(
transactions,
prev_block_hash,
self.produce_invalid_tx_in_chunks,
);
Ok(Some((encoded_chunk, merkle_paths, outgoing_receipts)))
let num_filtered_transactions = transactions.len();
let (tx_root, _) = merklize(&transactions);

// TODO(post-state-root): block-level field, temporary using the value from the prev block
let gas_price = prev_block_header.gas_price();
let ApplyTransactionResult { outgoing_receipts, .. } = self.chain.apply_chunk_for_post_state_root(
shard_id,
epoch_id,
prev_state_root,
// TODO(post-state-root): block-level field, need to double check if using next_height is correct here
next_height,
&prev_block_header,
&transactions,
prev_validator_proposals,
gas_price,
gas_limit,
last_header.height_included(),
)?;

// Receipts proofs root is calculating here
//
// For each subset of incoming_receipts_into_shard_i_from_the_current_one
// we calculate hash here and save it
// and then hash all of them into a single receipts root
//
// We check validity in two ways:
// 1. someone who cares about shard will download all the receipts
// and checks that receipts_root equals to all receipts hashed
// 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 outgoing_receipts_root = self.calculate_receipts_root(epoch_id, &outgoing_receipts)?;
let protocol_version = self.epoch_manager.get_epoch_protocol_version(epoch_id)?;
#[cfg(feature = "test_features")]
let gas_used = if self.produce_invalid_chunks { gas_used + 1 } else { gas_used };

let (encoded_chunk, merkle_paths) = ShardsManager::create_encoded_shard_chunk(
prev_block_hash,
*chunk_extra.state_root(),
*chunk_extra.outcome_root(),
next_height,
shard_id,
gas_used,
chunk_extra.gas_limit(),
chunk_extra.balance_burnt(),
chunk_extra.validator_proposals().collect(),
transactions,
&outgoing_receipts,
outgoing_receipts_root,
tx_root,
&*validator_signer,
&mut self.rs_for_chunk_production,
protocol_version,
)?;
todo!()
}

fn calculate_receipts_root(&self, epoch_id: &EpochId, receipts: &[Receipt]) -> Result<CryptoHash, Error> {
let shard_layout = self.epoch_manager.get_shard_layout(epoch_id)?;
let receipts_hashes =
Chain::build_receipts_hashes(&receipts, &shard_layout);
let (receipts_root, _) = merklize(&receipts_hashes);
Ok(receipts_root)
}

#[cfg(feature = "test_features")]
Expand Down Expand Up @@ -911,7 +1055,8 @@ impl Client {
fn prepare_transactions(
&mut self,
shard_uid: ShardUId,
chunk_extra: &ChunkExtra,
gas_limit: Gas,
state_root: StateRoot,
prev_block_header: &BlockHeader,
) -> Result<Vec<SignedTransaction>, Error> {
let Self { chain, sharded_tx_pool, epoch_manager, runtime_adapter: runtime, .. } = self;
Expand All @@ -924,10 +1069,10 @@ impl Client {
let transaction_validity_period = chain.transaction_validity_period;
runtime.prepare_transactions(
prev_block_header.gas_price(),
chunk_extra.gas_limit(),
gas_limit,
&next_epoch_id,
shard_id,
*chunk_extra.state_root(),
state_root,
// while the height of the next block that includes the chunk might not be prev_height + 1,
// passing it will result in a more conservative check and will not accidentally allow
// invalid transactions to be included.
Expand Down

0 comments on commit 5315641

Please sign in to comment.