Skip to content

Commit

Permalink
relaxed_chunk_validation: stabilize
Browse files Browse the repository at this point in the history
  • Loading branch information
nagisa committed Jan 14, 2025
1 parent 8c0e9b9 commit 2cd4726
Show file tree
Hide file tree
Showing 18 changed files with 94 additions and 176 deletions.
6 changes: 0 additions & 6 deletions chain/chain/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,6 @@ test_features = [
]
shadow_chunk_validation = []

protocol_feature_relaxed_chunk_validation = [
"near-primitives/protocol_feature_relaxed_chunk_validation",
"node-runtime/protocol_feature_relaxed_chunk_validation",
]

nightly = [
"near-async/nightly",
"near-chain-configs/nightly",
Expand All @@ -97,7 +92,6 @@ nightly = [
"near-vm-runner/nightly",
"nightly_protocol",
"node-runtime/nightly",
"protocol_feature_relaxed_chunk_validation",
]
nightly_protocol = [
"near-async/nightly_protocol",
Expand Down
7 changes: 2 additions & 5 deletions chain/chain/src/chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3198,11 +3198,8 @@ impl Chain {
) -> Result<Vec<bool>, Error> {
let protocol_version =
self.epoch_manager.get_epoch_protocol_version(block.header().epoch_id())?;
let relaxed_chunk_validation = checked_feature!(
"protocol_feature_relaxed_chunk_validation",
RelaxedChunkValidation,
protocol_version
);
let relaxed_chunk_validation =
checked_feature!("stable", RelaxedChunkValidation, protocol_version);

if !relaxed_chunk_validation {
if !validate_transactions_order(chunk.transactions()) {
Expand Down
6 changes: 1 addition & 5 deletions chain/chain/src/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1325,11 +1325,7 @@ fn calculate_transactions_size_limit(
) -> u64 {
// Checking feature WitnessTransactionLimits
if ProtocolFeature::StatelessValidation.enabled(protocol_version) {
if near_primitives::checked_feature!(
"protocol_feature_relaxed_chunk_validation",
RelaxedChunkValidation,
protocol_version
) {
if near_primitives::checked_feature!("stable", RelaxedChunkValidation, protocol_version) {
last_chunk_transactions_size = 0;
}
// Sum of transactions in the previous and current chunks should not exceed the limit.
Expand Down
121 changes: 59 additions & 62 deletions chain/chain/src/stateless_validation/chunk_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -376,73 +376,70 @@ 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_results = if checked_feature!(
"protocol_feature_relaxed_chunk_validation",
RelaxedChunkValidation,
current_protocol_version
) {
if !state_witness.new_transactions.is_empty() {
return Err(Error::InvalidChunkStateWitness(format!(
"Witness new_transactions must be empty",
)));
}
if last_chunk_block.header().is_genesis() {
vec![true; state_witness.transactions.len()]
let transaction_validity_check_results =
if checked_feature!("stable", RelaxedChunkValidation, current_protocol_version) {
if !state_witness.new_transactions.is_empty() {
return Err(Error::InvalidChunkStateWitness(format!(
"Witness new_transactions must be empty",
)));
}
if last_chunk_block.header().is_genesis() {
vec![true; state_witness.transactions.len()]
} else {
let prev_block_header =
store.get_block_header(last_chunk_block.header().prev_hash())?;
let mut check = chain.transaction_validity_check(prev_block_header);
state_witness.transactions.iter().map(|t| check(t)).collect::<Vec<_>>()
}
} else {
let prev_block_header =
store.get_block_header(last_chunk_block.header().prev_hash())?;
let mut check = chain.transaction_validity_check(prev_block_header);
state_witness.transactions.iter().map(|t| check(t)).collect::<Vec<_>>()
}
} else {
let new_transactions = &state_witness.new_transactions;
let (new_tx_root_from_state_witness, _) = merklize(&new_transactions);
let chunk_tx_root = state_witness.chunk_header.tx_root();
if new_tx_root_from_state_witness != chunk_tx_root {
return Err(Error::InvalidChunkStateWitness(format!(
"Witness new transactions root {:?} does not match chunk {:?}",
new_tx_root_from_state_witness, chunk_tx_root
)));
}
// Verify that all proposed transactions are valid.
if !new_transactions.is_empty() {
let transactions_validation_storage_config = RuntimeStorageConfig {
state_root: state_witness.chunk_header.prev_state_root(),
use_flat_storage: true,
source: StorageDataSource::Recorded(PartialStorage {
nodes: state_witness.new_transactions_validation_state.clone(),
}),
state_patch: Default::default(),
};
let new_transactions = &state_witness.new_transactions;
let (new_tx_root_from_state_witness, _) = merklize(&new_transactions);
let chunk_tx_root = state_witness.chunk_header.tx_root();
if new_tx_root_from_state_witness != chunk_tx_root {
return Err(Error::InvalidChunkStateWitness(format!(
"Witness new transactions root {:?} does not match chunk {:?}",
new_tx_root_from_state_witness, chunk_tx_root
)));
}
// Verify that all proposed transactions are valid.
if !new_transactions.is_empty() {
let transactions_validation_storage_config = RuntimeStorageConfig {
state_root: state_witness.chunk_header.prev_state_root(),
use_flat_storage: true,
source: StorageDataSource::Recorded(PartialStorage {
nodes: state_witness.new_transactions_validation_state.clone(),
}),
state_patch: Default::default(),
};

match validate_prepared_transactions(
chain,
runtime_adapter,
&state_witness.chunk_header,
transactions_validation_storage_config,
&new_transactions,
&state_witness.transactions,
) {
Ok(result) => {
if result.transactions.len() != new_transactions.len() {
return Err(Error::InvalidChunkStateWitness(format!(
"New transactions validation failed. \
match validate_prepared_transactions(
chain,
runtime_adapter,
&state_witness.chunk_header,
transactions_validation_storage_config,
&new_transactions,
&state_witness.transactions,
) {
Ok(result) => {
if result.transactions.len() != new_transactions.len() {
return Err(Error::InvalidChunkStateWitness(format!(
"New transactions validation failed. \
{} transactions out of {} proposed transactions were valid.",
result.transactions.len(),
new_transactions.len(),
result.transactions.len(),
new_transactions.len(),
)));
}
}
Err(error) => {
return Err(Error::InvalidChunkStateWitness(format!(
"New transactions validation failed: {}",
error,
)));
}
}
Err(error) => {
return Err(Error::InvalidChunkStateWitness(format!(
"New transactions validation failed: {}",
error,
)));
}
};
}
vec![true; state_witness.transactions.len()]
};
};
}
vec![true; state_witness.transactions.len()]
};

let main_transition_params = if last_chunk_block.header().is_genesis() {
let epoch_id = last_chunk_block.header().epoch_id();
Expand Down
7 changes: 2 additions & 5 deletions chain/chain/src/store/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -727,11 +727,8 @@ impl ChainStore {
prev_block_header: &BlockHeader,
chunk: &ShardChunk,
) -> Result<Vec<bool>, Error> {
let relaxed_chunk_validation = near_primitives::checked_feature!(
"protocol_feature_relaxed_chunk_validation",
RelaxedChunkValidation,
protocol_version
);
let relaxed_chunk_validation =
near_primitives::checked_feature!("stable", RelaxedChunkValidation, protocol_version);
if near_primitives::checked_feature!("stable", AccessKeyNonceRange, protocol_version) {
let mut valid_txs = Vec::with_capacity(chunk.transactions().len());
if relaxed_chunk_validation {
Expand Down
4 changes: 2 additions & 2 deletions chain/chain/src/tests/simple_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ fn build_chain() {
if cfg!(feature = "nightly") {
insta::assert_snapshot!(hash, @"GARF4HBtQJ41quFA9fvjHpbVYT4o15syhL3FkH1o7poT");
} else {
insta::assert_snapshot!(hash, @"5LkmueLrB2cc3vURr6VKvT9acRTuNzWGTvzLGFJkRD9c");
insta::assert_snapshot!(hash, @"DNN142QSegYKSH5UHAsNpa7ZaPH4DvGpN3H9tRz9ZYMW");
}

for i in 1..5 {
Expand All @@ -53,7 +53,7 @@ fn build_chain() {
if cfg!(feature = "nightly") {
insta::assert_snapshot!(hash, @"HiXuBfW5Xd6e8ZTbMhwtPEXeZxe7macc8DvaWryNdvcf");
} else {
insta::assert_snapshot!(hash, @"5txsrLCmQp9kn3jYRp1VHrCDt7oBTnhyi71rEPZmm8Ce");
insta::assert_snapshot!(hash, @"HaJRSFSiUVVZ2uwaxZZ1oSpka1mz5tmeioaUSQVWqsHJ");
}
}

Expand Down
6 changes: 0 additions & 6 deletions chain/client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,6 @@ near-primitives = { workspace = true, features = ["clock", "solomon", "rand"] }
near-actix-test-utils.workspace = true

[features]
protocol_feature_relaxed_chunk_validation = [
"near-chain/protocol_feature_relaxed_chunk_validation",
"near-primitives/protocol_feature_relaxed_chunk_validation",
]

# if enabled, we assert in most situations that are impossible unless some byzantine behavior is observed.
byzantine_asserts = ["near-chain/byzantine_asserts"]
shadow_chunk_validation = ["near-chain/shadow_chunk_validation"]
Expand Down Expand Up @@ -125,7 +120,6 @@ nightly = [
"near-telemetry/nightly",
"near-vm-runner/nightly",
"nightly_protocol",
"protocol_feature_relaxed_chunk_validation",
]
sandbox = [
"near-client-primitives/sandbox",
Expand Down
35 changes: 16 additions & 19 deletions chain/client/src/stateless_validation/state_witness_producer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,27 +134,24 @@ impl Client {
contract_updates,
} = self.collect_state_transition_data(&chunk_header, prev_chunk_header)?;

let (new_transactions, new_transactions_validation_state) = if checked_feature!(
"protocol_feature_relaxed_chunk_validation",
RelaxedChunkValidation,
protocol_version
) {
(Vec::new(), PartialState::default())
} else {
let new_transactions = chunk.transactions().to_vec();
let new_transactions_validation_state = if new_transactions.is_empty() {
PartialState::default()
let (new_transactions, new_transactions_validation_state) =
if checked_feature!("stable", RelaxedChunkValidation, protocol_version) {
(Vec::new(), PartialState::default())
} else {
// With stateless validation chunk producer uses recording reads when validating
// transactions. The storage proof must be available here.
transactions_storage_proof.ok_or_else(|| {
let message = "Missing storage proof for transactions validation";
log_assert_fail!("{message}");
Error::Other(message.to_owned())
})?
let new_transactions = chunk.transactions().to_vec();
let new_transactions_validation_state = if new_transactions.is_empty() {
PartialState::default()
} else {
// With stateless validation chunk producer uses recording reads when validating
// transactions. The storage proof must be available here.
transactions_storage_proof.ok_or_else(|| {
let message = "Missing storage proof for transactions validation";
log_assert_fail!("{message}");
Error::Other(message.to_owned())
})?
};
(new_transactions, new_transactions_validation_state)
};
(new_transactions, new_transactions_validation_state)
};

let source_receipt_proofs =
self.collect_source_receipt_proofs(prev_block_header, prev_chunk_header)?;
Expand Down
4 changes: 2 additions & 2 deletions chain/jsonrpc/jsonrpc-tests/res/genesis_config.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"protocol_version": 74,
"protocol_version": 75,
"genesis_time": "1970-01-01T00:00:00.000000000Z",
"chain_id": "sample",
"genesis_height": 0,
Expand Down Expand Up @@ -86,4 +86,4 @@
"num_chunk_validator_seats": 300,
"chunk_producer_assignment_changes_limit": 5,
"records": []
}
}
2 changes: 0 additions & 2 deletions core/primitives-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,11 @@ expect-test.workspace = true
default = []
protocol_feature_fix_contract_loading_cost = []
protocol_feature_nonrefundable_transfer_nep491 = []
protocol_feature_relaxed_chunk_validation = []

nightly = [
"nightly_protocol",
"protocol_feature_fix_contract_loading_cost",
"protocol_feature_nonrefundable_transfer_nep491",
"protocol_feature_relaxed_chunk_validation",
]

nightly_protocol = [
Expand Down
6 changes: 2 additions & 4 deletions core/primitives-core/src/version.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,6 @@ pub enum ProtocolFeature {
///
/// Chunks no longer become entirely invalid in case invalid transactions are included in the
/// chunk. Instead the transactions are discarded during their conversion to receipts.
#[cfg(feature = "protocol_feature_relaxed_chunk_validation")]
RelaxedChunkValidation,
/// Exclude existing contract code in deploy-contract and delete-account actions from the chunk state witness.
/// Instead of sending code in the witness, the code checks the code-size using the internal trie nodes.
Expand Down Expand Up @@ -258,6 +257,7 @@ impl ProtocolFeature {
ProtocolFeature::FixStakingThreshold
| ProtocolFeature::RejectBlocksWithOutdatedProtocolVersions
| ProtocolFeature::FixChunkProducerStakingThreshold => 74,
ProtocolFeature::RelaxedChunkValidation => 75,

// This protocol version is reserved for use in resharding tests. An extra resharding
// is simulated on top of the latest shard layout in production. Note that later
Expand All @@ -281,8 +281,6 @@ impl ProtocolFeature {
// protocol upgrades for those features!
ProtocolFeature::BandwidthScheduler => 145,
ProtocolFeature::SimpleNightshadeV4 => 146,
#[cfg(feature = "protocol_feature_relaxed_chunk_validation")]
ProtocolFeature::RelaxedChunkValidation => 147,
ProtocolFeature::ExcludeExistingCodeFromWitnessForCodeLen => 148,
ProtocolFeature::BlockHeightForReceiptId => 149,
// Place features that are not yet in Nightly below this line.
Expand All @@ -295,7 +293,7 @@ impl ProtocolFeature {
}

/// Current protocol version used on the mainnet with all stable features.
const STABLE_PROTOCOL_VERSION: ProtocolVersion = 74;
const STABLE_PROTOCOL_VERSION: ProtocolVersion = 75;

// On nightly, pick big enough version to support all features.
const NIGHTLY_PROTOCOL_VERSION: ProtocolVersion = 149;
Expand Down
4 changes: 0 additions & 4 deletions core/primitives/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,6 @@ protocol_feature_fix_contract_loading_cost = [
protocol_feature_nonrefundable_transfer_nep491 = [
"near-primitives-core/protocol_feature_nonrefundable_transfer_nep491",
]
protocol_feature_relaxed_chunk_validation = [
"near-primitives-core/protocol_feature_relaxed_chunk_validation",
]

nightly = [
"near-fmt/nightly",
Expand All @@ -79,7 +76,6 @@ nightly = [
"nightly_protocol",
"protocol_feature_fix_contract_loading_cost",
"protocol_feature_nonrefundable_transfer_nep491",
"protocol_feature_relaxed_chunk_validation",
]

nightly_protocol = [
Expand Down
4 changes: 0 additions & 4 deletions integration-tests/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,6 @@ protocol_feature_fix_contract_loading_cost = [
protocol_feature_nonrefundable_transfer_nep491 = [
"near-primitives/protocol_feature_nonrefundable_transfer_nep491",
]
protocol_feature_relaxed_chunk_validation = [
"near-primitives-core/protocol_feature_relaxed_chunk_validation",
]

nightly = [
"near-actix-test-utils/nightly",
Expand Down Expand Up @@ -129,7 +126,6 @@ nightly = [
"node-runtime/nightly",
"protocol_feature_fix_contract_loading_cost",
"protocol_feature_nonrefundable_transfer_nep491",
"protocol_feature_relaxed_chunk_validation",
"testlib/nightly",
]
nightly_protocol = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ fn test_banning_chunk_producer_when_seeing_invalid_chunk() {

#[test]
#[cfg(feature = "test_features")]
#[cfg_attr(feature = "protocol_feature_relaxed_chunk_validation", ignore)]
fn test_banning_chunk_producer_when_seeing_invalid_tx_in_chunk() {
init_test_logger();
let mut test = AdversarialBehaviorTestData::new();
Expand Down
Loading

0 comments on commit 2cd4726

Please sign in to comment.