Skip to content

Commit

Permalink
tx: filter out expired transactions in the tx runtime
Browse files Browse the repository at this point in the history
  • Loading branch information
nagisa committed Jan 7, 2025
1 parent 0eacf3f commit b903e67
Show file tree
Hide file tree
Showing 19 changed files with 205 additions and 160 deletions.
75 changes: 44 additions & 31 deletions chain/chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3170,48 +3170,59 @@ impl Chain {
block: &Block,
prev_block_header: &BlockHeader,
chunk: &ShardChunk,
) -> Result<(), Error> {
) -> Result<Vec<bool>, Error> {
let protocol_version =
self.epoch_manager.get_epoch_protocol_version(block.header().epoch_id())?;

if checked_feature!(
let relaxed_chunk_validation = checked_feature!(
"protocol_feature_relaxed_chunk_validation",
RelaxedChunkValidation,
protocol_version
) {
return Ok(());
}
);

if !validate_transactions_order(chunk.transactions()) {
let merkle_paths =
Block::compute_chunk_headers_root(block.chunks().iter_deprecated()).1;
let epoch_id = block.header().epoch_id();
let shard_layout = self.epoch_manager.get_shard_layout(&epoch_id)?;
let shard_id = chunk.shard_id();
let shard_index = shard_layout.get_shard_index(shard_id)?;

let chunk_proof = ChunkProofs {
block_header: borsh::to_vec(&block.header()).expect("Failed to serialize"),
merkle_proof: merkle_paths[shard_index].clone(),
chunk: MaybeEncodedShardChunk::Decoded(chunk.clone()).into(),
};
return Err(Error::InvalidChunkProofs(Box::new(chunk_proof)));
if !relaxed_chunk_validation {
if !validate_transactions_order(chunk.transactions()) {
let merkle_paths =
Block::compute_chunk_headers_root(block.chunks().iter_deprecated()).1;
let epoch_id = block.header().epoch_id();
let shard_layout = self.epoch_manager.get_shard_layout(&epoch_id)?;
let shard_id = chunk.shard_id();
let shard_index = shard_layout.get_shard_index(shard_id)?;

let chunk_proof = ChunkProofs {
block_header: borsh::to_vec(&block.header()).expect("Failed to serialize"),
merkle_proof: merkle_paths[shard_index].clone(),
chunk: MaybeEncodedShardChunk::Decoded(chunk.clone()).into(),
};
return Err(Error::InvalidChunkProofs(Box::new(chunk_proof)));
}
}

if checked_feature!("stable", AccessKeyNonceRange, protocol_version) {
let transaction_validity_period = self.transaction_validity_period;
for transaction in chunk.transactions() {
self.chain_store()
.check_transaction_validity_period(
prev_block_header,
transaction.transaction.block_hash(),
transaction_validity_period,
)
.map_err(|_| Error::from(Error::InvalidTransactions))?;
}
return chunk
.transactions()
.into_iter()
.map(|transaction| {
let tx_valid = self
.chain_store()
.check_transaction_validity_period(
prev_block_header,
transaction.transaction.block_hash(),
transaction_validity_period,
)
.is_ok();
if relaxed_chunk_validation {
Ok(tx_valid)
} else if !tx_valid {
Err(Error::from(Error::InvalidTransactions))
} else {
Ok(true)
}
})
.collect::<Result<_, _>>();
};

Ok(())
Ok(vec![true; chunk.transactions().len()])
}

pub fn transaction_validity_check<'a>(
Expand Down Expand Up @@ -3773,7 +3784,8 @@ impl Chain {
}
})?;

self.validate_chunk_transactions(&block, prev_block.header(), &chunk)?;
let tx_valid_list =
self.validate_chunk_transactions(&block, prev_block.header(), &chunk)?;

// we can't use hash from the current block here yet because the incoming receipts
// for this block is not stored yet
Expand Down Expand Up @@ -3806,6 +3818,7 @@ impl Chain {
ShardUpdateReason::NewChunk(NewChunkData {
chunk_header: chunk_header.clone(),
transactions: chunk.transactions().to_vec(),
transaction_validity_check_results: tx_valid_list,
receipts,
block: block_context,
is_first_block_with_chunk_of_version,
Expand Down
10 changes: 7 additions & 3 deletions chain/chain/src/chain_update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use near_primitives::state_sync::{ReceiptProofResponse, ShardStateSyncResponseHe
use near_primitives::types::chunk_extra::ChunkExtra;
use near_primitives::types::{BlockExtra, BlockHeight, BlockHeightDelta, ShardId};
use near_primitives::views::LightClientBlockView;
use node_runtime::SignedValidPeriodTransactions;
use std::sync::Arc;
use tracing::{debug, info, warn};

Expand Down Expand Up @@ -528,7 +529,10 @@ impl<'a> ChainUpdate<'a> {
let is_first_block_with_chunk_of_version = false;

let block = self.chain_store_update.get_block(block_header.hash())?;

let transactions = chunk.transactions();
// All transactions in state sync are assumed to have had correct validity period.
let transaction_validity = vec![true; transactions.len()];
let transactions = SignedValidPeriodTransactions::new(transactions, &transaction_validity);
let apply_result = self.runtime_adapter.apply_chunk(
RuntimeStorageConfig::new(chunk_header.prev_state_root(), true),
ApplyChunkReason::UpdateTrackedShard,
Expand All @@ -551,7 +555,7 @@ impl<'a> ChainUpdate<'a> {
bandwidth_requests: block.block_bandwidth_requests(),
},
&receipts,
chunk.transactions(),
transactions,
)?;

let (outcome_root, outcome_proofs) =
Expand Down Expand Up @@ -659,7 +663,7 @@ impl<'a> ChainUpdate<'a> {
block.block_bandwidth_requests(),
),
&[],
&[],
SignedValidPeriodTransactions::new(&[], &[]),
)?;
let flat_storage_manager = self.runtime_adapter.get_flat_storage_manager();
let store_update = flat_storage_manager.save_flat_state_changes(
Expand Down
4 changes: 2 additions & 2 deletions chain/chain/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ impl NightshadeRuntime {
chunk: ApplyChunkShardContext,
block: ApplyChunkBlockContext,
receipts: &[Receipt],
transactions: &[SignedTransaction],
transactions: node_runtime::SignedValidPeriodTransactions<'_>,
state_patch: SandboxStatePatch,
) -> Result<ApplyChunkResult, Error> {
let ApplyChunkBlockContext {
Expand Down Expand Up @@ -838,7 +838,7 @@ impl RuntimeAdapter for NightshadeRuntime {
chunk: ApplyChunkShardContext,
block: ApplyChunkBlockContext,
receipts: &[Receipt],
transactions: &[SignedTransaction],
transactions: node_runtime::SignedValidPeriodTransactions<'_>,
) -> Result<ApplyChunkResult, Error> {
let shard_id = chunk.shard_id;
let _timer = metrics::APPLYING_CHUNKS_TIME
Expand Down
3 changes: 3 additions & 0 deletions chain/chain/src/runtime/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use near_store::genesis::initialize_genesis_state;
use near_vm_runner::{
get_contract_cache_key, CompiledContract, CompiledContractInfo, FilesystemContractRuntimeCache,
};
use node_runtime::SignedValidPeriodTransactions;
use num_rational::Ratio;
use rand::{rngs::StdRng, seq::SliceRandom, SeedableRng};

Expand Down Expand Up @@ -244,6 +245,8 @@ impl TestEnv {
.collect();
BlockCongestionInfo::new(shards_congestion_info)
};
let transaction_validity = vec![true; transactions.len()];
let transactions = SignedValidPeriodTransactions::new(transactions, &transaction_validity);
self.runtime
.apply_chunk(
RuntimeStorageConfig::new(state_root, true),
Expand Down
37 changes: 4 additions & 33 deletions chain/chain/src/stateless_validation/chunk_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,6 @@ impl MainTransition {
pub struct PreValidationOutput {
pub main_transition_params: MainTransition,
pub implicit_transition_params: Vec<ImplicitTransitionParams>,
/// List of the transactions that are valid and should be processed by e.g.
/// `validate_chunk_state_witness`.
///
/// This list is exactly the length of the corresponding `ChunkStateWitness::transactions`
/// field. Element at the index N in this array corresponds to an element at index N in the
/// transactions list.
///
/// Transactions for which a `false` is stored here ought to be ignored/dropped/skipped.
///
/// All elements will be true for protocol versions where `RelaxedChunkValidation` is not
/// enabled.
pub transaction_validity_check_passed: Vec<bool>,
}

#[derive(Clone)]
Expand Down Expand Up @@ -381,7 +369,7 @@ pub fn pre_validate_chunk_state_witness(

let current_protocol_version =
epoch_manager.get_epoch_protocol_version(&state_witness.epoch_id)?;
let transaction_validity_check_passed = if checked_feature!(
let transaction_validity_check_results = if checked_feature!(
"protocol_feature_relaxed_chunk_validation",
RelaxedChunkValidation,
current_protocol_version
Expand Down Expand Up @@ -467,6 +455,7 @@ pub fn pre_validate_chunk_state_witness(
MainTransition::NewChunk(NewChunkData {
chunk_header: last_chunk_block.chunks().get(last_chunk_shard_index).unwrap().clone(),
transactions: state_witness.transactions.clone(),
transaction_validity_check_results,
receipts: receipts_to_apply,
block: Chain::get_apply_chunk_block_context(
epoch_manager,
Expand All @@ -484,11 +473,7 @@ pub fn pre_validate_chunk_state_witness(
})
};

Ok(PreValidationOutput {
main_transition_params,
implicit_transition_params,
transaction_validity_check_passed,
})
Ok(PreValidationOutput { main_transition_params, implicit_transition_params })
}

/// Validate that receipt proofs contain the receipts that should be applied during the
Expand Down Expand Up @@ -637,21 +622,7 @@ pub fn validate_chunk_state_witness(
let (mut chunk_extra, mut outgoing_receipts) =
match (pre_validation_output.main_transition_params, cache_result) {
(MainTransition::Genesis { chunk_extra, .. }, _) => (chunk_extra, vec![]),
(MainTransition::NewChunk(mut new_chunk_data), None) => {
let mut validity_iterator =
pre_validation_output.transaction_validity_check_passed.iter();
new_chunk_data.transactions.retain(|t| {
let valid = *validity_iterator.next().unwrap();
if !valid {
tracing::debug!(
target: "chain",
message="discarding invalid transaction",
tx=%t.get_hash()
);
}
valid
});

(MainTransition::NewChunk(new_chunk_data), None) => {
let chunk_header = new_chunk_data.chunk_header.clone();
let NewChunkResult { apply_result: mut main_apply_result, .. } = apply_new_chunk(
ApplyChunkReason::ValidateChunkStateWitness,
Expand Down
5 changes: 3 additions & 2 deletions chain/chain/src/test_utils/kv_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ use near_store::{
TrieChanges, WrappedTrieChanges,
};
use near_vm_runner::{ContractCode, ContractRuntimeCache, NoContractRuntimeCache};
use node_runtime::SignedValidPeriodTransactions;
use std::cmp::Ordering;
use std::collections::{BTreeMap, HashMap, HashSet};
use std::sync::{Arc, RwLock};
Expand Down Expand Up @@ -1091,7 +1092,7 @@ impl RuntimeAdapter for KeyValueRuntime {
chunk: ApplyChunkShardContext,
block: ApplyChunkBlockContext,
receipts: &[Receipt],
transactions: &[SignedTransaction],
transactions: SignedValidPeriodTransactions<'_>,
) -> Result<ApplyChunkResult, Error> {
let mut tx_results = vec![];
let shard_id = chunk.shard_id;
Expand Down Expand Up @@ -1128,7 +1129,7 @@ impl RuntimeAdapter for KeyValueRuntime {
}
}

for transaction in transactions {
for transaction in transactions.iter_nonexpired_transactions() {
assert_eq!(
account_id_to_shard_id(transaction.transaction.signer_id(), self.num_shards),
shard_id
Expand Down
3 changes: 2 additions & 1 deletion chain/chain/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ use near_store::flat::FlatStorageManager;
use near_store::{PartialStorage, ShardTries, Store, Trie, WrappedTrieChanges};
use near_vm_runner::ContractCode;
use near_vm_runner::ContractRuntimeCache;
use node_runtime::SignedValidPeriodTransactions;
use num_rational::Rational32;
use tracing::instrument;

Expand Down Expand Up @@ -472,7 +473,7 @@ pub trait RuntimeAdapter: Send + Sync {
chunk: ApplyChunkShardContext,
block: ApplyChunkBlockContext,
receipts: &[Receipt],
transactions: &[SignedTransaction],
transactions: SignedValidPeriodTransactions<'_>,
) -> Result<ApplyChunkResult, Error>;

/// Query runtime with given `path` and `data`.
Expand Down
7 changes: 5 additions & 2 deletions chain/chain/src/update_shard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use near_primitives::sharding::ShardChunkHeader;
use near_primitives::transaction::SignedTransaction;
use near_primitives::types::chunk_extra::ChunkExtra;
use near_primitives::types::Gas;
use node_runtime::SignedValidPeriodTransactions;

/// Result of updating a shard for some block when it has a new chunk for this
/// shard.
Expand Down Expand Up @@ -43,6 +44,7 @@ pub enum ShardUpdateResult {
pub struct NewChunkData {
pub chunk_header: ShardChunkHeader,
pub transactions: Vec<SignedTransaction>,
pub transaction_validity_check_results: Vec<bool>,
pub receipts: Vec<Receipt>,
pub block: ApplyChunkBlockContext,
pub is_first_block_with_chunk_of_version: bool,
Expand Down Expand Up @@ -119,6 +121,7 @@ pub fn apply_new_chunk(
let NewChunkData {
chunk_header,
transactions,
transaction_validity_check_results,
block,
receipts,
is_first_block_with_chunk_of_version,
Expand Down Expand Up @@ -153,7 +156,7 @@ pub fn apply_new_chunk(
},
block,
&receipts,
&transactions,
SignedValidPeriodTransactions::new(&transactions, &transaction_validity_check_results),
) {
Ok(apply_result) => {
Ok(NewChunkResult { gas_limit, shard_uid: shard_context.shard_uid, apply_result })
Expand Down Expand Up @@ -200,7 +203,7 @@ pub fn apply_old_chunk(
},
block,
&[],
&[],
SignedValidPeriodTransactions::new(&[], &[]),
) {
Ok(apply_result) => Ok(OldChunkResult { shard_uid: shard_context.shard_uid, apply_result }),
Err(err) => Err(err),
Expand Down
3 changes: 2 additions & 1 deletion integration-tests/src/user/runtime_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use near_primitives::views::{
use near_store::adapter::StoreUpdateAdapter;
use near_store::{ShardTries, TrieUpdate};
use node_runtime::state_viewer::TrieViewer;
use node_runtime::SignedValidPeriodTransactions;
use node_runtime::{state_viewer::ViewApplyState, ApplyState, Runtime};

use crate::user::{User, POISONED_LOCK_ERR};
Expand Down Expand Up @@ -111,7 +112,7 @@ impl RuntimeUser {
&None,
&apply_state,
&receipts,
&txs,
SignedValidPeriodTransactions::new(&txs, &vec![true; txs.len()]),
&self.epoch_info_provider,
Default::default(),
)
Expand Down
4 changes: 2 additions & 2 deletions runtime/runtime-params-estimator/src/estimator_context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use near_store::{ShardTries, ShardUId, StateSnapshotConfig, TrieUpdate};
use near_store::{TrieCache, TrieCachingStorage, TrieConfig};
use near_vm_runner::logic::LimitConfig;
use near_vm_runner::FilesystemContractRuntimeCache;
use node_runtime::{ApplyState, Runtime};
use node_runtime::{ApplyState, Runtime, SignedValidPeriodTransactions};
use std::collections::HashMap;
use std::iter;
use std::sync::Arc;
Expand Down Expand Up @@ -355,7 +355,7 @@ impl Testbed<'_> {
&None,
&self.apply_state,
&self.prev_receipts,
transactions,
SignedValidPeriodTransactions::new(transactions, &vec![true; transactions.len()]),
&self.epoch_info_provider,
Default::default(),
)
Expand Down
Loading

0 comments on commit b903e67

Please sign in to comment.