Skip to content

Commit 252142a

Browse files
authored
Merge pull request #6486 from rdeioris/feat/max_tenure_bytes
Added tenure size checker/limiter
2 parents ec23f0e + 5f6c0c4 commit 252142a

File tree

18 files changed

+774
-9
lines changed

18 files changed

+774
-9
lines changed

stacks-node/src/tests/nakamoto_integrations.rs

Lines changed: 581 additions & 0 deletions
Large diffs are not rendered by default.

stacks-node/src/tests/signer/v0.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ use stacks::chainstate::stacks::{
5656
StacksTransaction, TenureChangeCause, TenureChangePayload, TransactionPayload,
5757
};
5858
use stacks::codec::StacksMessageCodec;
59-
use stacks::config::{Config as NeonConfig, EventKeyType, EventObserverConfig};
59+
use stacks::config::{
60+
Config as NeonConfig, EventKeyType, EventObserverConfig, DEFAULT_MAX_TENURE_BYTES,
61+
};
6062
use stacks::core::mempool::MemPoolWalkStrategy;
6163
use stacks::core::test_util::{
6264
insert_tx_in_mempool, make_big_read_count_contract, make_contract_call, make_contract_publish,
@@ -2048,6 +2050,7 @@ fn sip034_tenure_extend_proposal(allow: bool) {
20482050
None,
20492051
None,
20502052
None,
2053+
u64::from(DEFAULT_MAX_TENURE_BYTES),
20512054
)
20522055
.expect("Failed to build Nakamoto block");
20532056

@@ -3119,6 +3122,7 @@ fn forked_tenure_testing(
31193122
burn_header_timestamp: tip_sn.burn_header_timestamp,
31203123
anchored_block_size: tip_b_block.serialize_to_vec().len() as u64,
31213124
burn_view: Some(tip_b_block.header.consensus_hash),
3125+
total_tenure_size: 0,
31223126
};
31233127

31243128
let blocks = test_observer::get_mined_nakamoto_blocks();
@@ -17840,6 +17844,7 @@ fn reorging_signers_capitulate_to_nonreorging_signers_during_tenure_fork() {
1784017844
burn_header_timestamp: tip_sn.burn_header_timestamp,
1784117845
anchored_block_size: tenure_b_block.serialize_to_vec().len() as u64,
1784217846
burn_view: Some(tenure_b_block.header.consensus_hash),
17847+
total_tenure_size: 0,
1784317848
};
1784417849

1784517850
// Block B was built atop block A

stackslib/src/chainstate/nakamoto/miner.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use crate::chainstate::stacks::miner::{
3737
};
3838
use crate::chainstate::stacks::{Error, StacksBlockHeader, *};
3939
use crate::clarity_vm::clarity::ClarityInstance;
40-
use crate::config::DEFAULT_CONTRACT_COST_LIMIT_PERCENTAGE;
40+
use crate::config::{DEFAULT_CONTRACT_COST_LIMIT_PERCENTAGE, DEFAULT_MAX_TENURE_BYTES};
4141
use crate::core::mempool::*;
4242
use crate::core::*;
4343
use crate::monitoring::{
@@ -96,6 +96,8 @@ pub struct NakamotoBlockBuilder {
9696
/// Percentage of a block's budget that may be consumed by
9797
/// contract calls before reverting to stx transfers/boot contract calls only
9898
contract_limit_percentage: Option<u8>,
99+
/// Maximum size of the whole tenure
100+
pub max_tenure_bytes: u64,
99101
}
100102

101103
/// NB: No PartialEq implementation is deliberate in order to ensure that we use the appropriate
@@ -235,6 +237,7 @@ impl NakamotoBlockBuilder {
235237
header: NakamotoBlockHeader::genesis(),
236238
soft_limit: None,
237239
contract_limit_percentage: None,
240+
max_tenure_bytes: u64::from(DEFAULT_MAX_TENURE_BYTES),
238241
}
239242
}
240243

@@ -266,6 +269,7 @@ impl NakamotoBlockBuilder {
266269
soft_limit: Option<ExecutionCost>,
267270
contract_limit_percentage: Option<u8>,
268271
timestamp: Option<u64>,
272+
max_tenure_bytes: u64,
269273
) -> Result<NakamotoBlockBuilder, Error> {
270274
let next_height = parent_stacks_header
271275
.anchored_header
@@ -308,6 +312,7 @@ impl NakamotoBlockBuilder {
308312
),
309313
soft_limit,
310314
contract_limit_percentage,
315+
max_tenure_bytes,
311316
})
312317
}
313318

@@ -671,6 +676,7 @@ impl NakamotoBlockBuilder {
671676
None,
672677
settings.mempool_settings.contract_cost_limit_percentage,
673678
None,
679+
settings.max_tenure_bytes,
674680
)?;
675681

676682
let ts_start = get_epoch_time_ms();
@@ -806,6 +812,20 @@ impl BlockBuilder for NakamotoBlockBuilder {
806812
return TransactionResult::skipped_due_to_error(tx, Error::BlockTooBigError);
807813
}
808814

815+
if let Some(parent_header) = &self.parent_header {
816+
let mut total_tenure_size = self.bytes_so_far + tx_len;
817+
818+
// if we are in the same tenure of the parent, accumulate the parent total_tenure_size
819+
// note that total_tenure_size is reset whenever a new tenure extend happens
820+
if parent_header.consensus_hash == self.header.consensus_hash {
821+
total_tenure_size += parent_header.total_tenure_size;
822+
}
823+
824+
if total_tenure_size >= self.max_tenure_bytes {
825+
return TransactionResult::skipped_due_to_error(tx, Error::TenureTooBigError);
826+
}
827+
}
828+
809829
let non_boot_code_contract_call = match &tx.payload {
810830
TransactionPayload::ContractCall(cc) => !cc.address.is_boot_code_addr(),
811831
TransactionPayload::SmartContract(..) => true,

stackslib/src/chainstate/nakamoto/mod.rs

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,24 @@ pub static NAKAMOTO_CHAINSTATE_SCHEMA_6: &[&str] = &[
288288
"CREATE INDEX IF NOT EXISTS nakamoto_block_headers_by_ch_bv ON nakamoto_block_headers(consensus_hash, burn_view);"
289289
];
290290

291+
pub static NAKAMOTO_CHAINSTATE_SCHEMA_7: &[&str] = &[
292+
r#"
293+
UPDATE db_config SET version = "12";
294+
"#,
295+
// Add a `total_tenure_size` field to the block header row, so we can keep track
296+
// of the whole tenure size (and eventually limit it)
297+
//
298+
//
299+
//
300+
// Default to 0.
301+
r#"
302+
-- total_tenure_size cannot be consensus critical as existing nodes which migrate will report a 0 size while
303+
-- nodes booting from genesis sync will get the true tenure size
304+
ALTER TABLE nakamoto_block_headers
305+
ADD COLUMN total_tenure_size NOT NULL DEFAULT 0;
306+
"#,
307+
];
308+
291309
#[cfg(test)]
292310
mod fault_injection {
293311
static PROCESS_BLOCK_STALL: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
@@ -2649,6 +2667,19 @@ impl NakamotoChainState {
26492667
Ok(result)
26502668
}
26512669

2670+
/// Load the total_tenure_size for a Nakamoto header
2671+
pub fn get_block_header_nakamoto_total_tenure_size(
2672+
chainstate_conn: &Connection,
2673+
index_block_hash: &StacksBlockId,
2674+
) -> Result<Option<u64>, ChainstateError> {
2675+
let sql =
2676+
"SELECT total_tenure_size FROM nakamoto_block_headers WHERE index_block_hash = ?1";
2677+
let result = query_row_panic(chainstate_conn, sql, &[&index_block_hash], || {
2678+
"FATAL: multiple rows for the same block hash".to_string()
2679+
})?;
2680+
Ok(result)
2681+
}
2682+
26522683
/// Load an epoch2 header
26532684
pub fn get_block_header_epoch2(
26542685
chainstate_conn: &Connection,
@@ -3245,6 +3276,7 @@ impl NakamotoChainState {
32453276
stacks_block_height,
32463277
burn_header_height,
32473278
burn_header_timestamp,
3279+
total_tenure_size,
32483280
..
32493281
} = tip_info;
32503282

@@ -3301,6 +3333,7 @@ impl NakamotoChainState {
33013333
"Nakamoto block StacksHeaderInfo did not set burnchain view".into(),
33023334
))
33033335
})?,
3336+
total_tenure_size
33043337
];
33053338

33063339
chainstate_tx.execute(
@@ -3333,8 +3366,9 @@ impl NakamotoChainState {
33333366
vrf_proof,
33343367
signer_bitvec,
33353368
height_in_tenure,
3336-
burn_view)
3337-
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, ?27)",
3369+
burn_view,
3370+
total_tenure_size)
3371+
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, ?14, ?15, ?16, ?17, ?18, ?19, ?20, ?21, ?22, ?23, ?24, ?25, ?26, ?27, ?28)",
33383372
args
33393373
)?;
33403374

@@ -3400,6 +3434,9 @@ impl NakamotoChainState {
34003434
let mut marf_keys = vec![];
34013435
let mut marf_values = vec![];
34023436

3437+
// assume a new tenure (we will eventually add the parent accumulated size later)
3438+
let mut total_tenure_size = block_size;
3439+
34033440
if new_tenure {
34043441
// make the coinbase height point to this tenure-start block
34053442
marf_keys.push(nakamoto_keys::ongoing_tenure_coinbase_height(
@@ -3463,6 +3500,28 @@ impl NakamotoChainState {
34633500

34643501
marf_keys.push(nakamoto_keys::ongoing_tenure_id().to_string());
34653502
marf_values.push(nakamoto_keys::make_tenure_id_value(&tenure_id));
3503+
} else {
3504+
// if we are here (no new tenure or tenure_extend) we need to accumulate the parent total tenure size
3505+
if let Some(current_total_tenure_size) =
3506+
NakamotoChainState::get_block_header_nakamoto_total_tenure_size(
3507+
&headers_tx,
3508+
&new_tip.parent_block_id,
3509+
)?
3510+
{
3511+
total_tenure_size = match total_tenure_size.checked_add(current_total_tenure_size) {
3512+
Some(total_tenure_size) => total_tenure_size,
3513+
// in the extremely improbable case of overflow, just throw the tenure too big error
3514+
None => {
3515+
return Err(ChainstateError::TenureTooBigError);
3516+
}
3517+
};
3518+
} else {
3519+
warn!(
3520+
"Unable to retrieve total tenure size";
3521+
"consensus_hash" => %new_tip.consensus_hash,
3522+
"parent_block_id" => %new_tip.parent_block_id,
3523+
);
3524+
}
34663525
}
34673526

34683527
// record the highest block in this tenure
@@ -3494,6 +3553,7 @@ impl NakamotoChainState {
34943553
burn_header_timestamp: new_burnchain_timestamp,
34953554
anchored_block_size: block_size,
34963555
burn_view: Some(burn_view.clone()),
3556+
total_tenure_size,
34973557
};
34983558

34993559
let tenure_fees = block_fees

stackslib/src/chainstate/nakamoto/shadow.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ use crate::chainstate::stacks::{
7070
use crate::clarity::vm::types::StacksAddressExtensions;
7171
use crate::clarity_vm::clarity::ClarityInstance;
7272
use crate::clarity_vm::database::SortitionDBRef;
73+
use crate::config::DEFAULT_MAX_TENURE_BYTES;
7374
use crate::net::Error as NetError;
7475
use crate::util_lib::db::{query_row, u64_to_sql, Error as DBError};
7576

@@ -726,6 +727,7 @@ impl NakamotoBlockBuilder {
726727
None,
727728
None,
728729
None,
730+
u64::from(DEFAULT_MAX_TENURE_BYTES),
729731
)?;
730732

731733
let mut block_txs = vec![tenure_change_tx, coinbase_tx];

stackslib/src/chainstate/nakamoto/tests/mod.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,6 +668,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
668668
burn_header_timestamp: 1000,
669669
anchored_block_size: 12345,
670670
burn_view: None,
671+
total_tenure_size: 0,
671672
};
672673

673674
let epoch2_execution_cost = ExecutionCost {
@@ -801,6 +802,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
801802
burn_header_timestamp: 1001,
802803
anchored_block_size: 123,
803804
burn_view: Some(nakamoto_header.consensus_hash.clone()),
805+
total_tenure_size: 0,
804806
};
805807

806808
let epoch2_block = StacksBlock {
@@ -847,6 +849,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
847849
burn_header_timestamp: 1001,
848850
anchored_block_size: 123,
849851
burn_view: Some(nakamoto_header_2.consensus_hash.clone()),
852+
total_tenure_size: 0,
850853
};
851854

852855
let nakamoto_block_2 = NakamotoBlock {
@@ -888,6 +891,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
888891
burn_header_timestamp: 1001,
889892
anchored_block_size: 123,
890893
burn_view: Some(nakamoto_header_3.consensus_hash.clone()),
894+
total_tenure_size: 0,
891895
};
892896

893897
let nakamoto_block_3 = NakamotoBlock {
@@ -921,6 +925,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
921925
burn_header_timestamp: 1001,
922926
anchored_block_size: 123,
923927
burn_view: Some(nakamoto_header_3.consensus_hash.clone()),
928+
total_tenure_size: 0,
924929
};
925930

926931
let nakamoto_block_3_weight_2 = NakamotoBlock {
@@ -954,6 +959,7 @@ pub fn test_load_store_update_nakamoto_blocks() {
954959
burn_header_timestamp: 1001,
955960
anchored_block_size: 123,
956961
burn_view: Some(nakamoto_header_4.consensus_hash.clone()),
962+
total_tenure_size: 0,
957963
};
958964

959965
let nakamoto_block_4 = NakamotoBlock {

stackslib/src/chainstate/nakamoto/tests/node.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ use crate::chainstate::stacks::db::*;
4848
use crate::chainstate::stacks::miner::*;
4949
use crate::chainstate::stacks::tests::TestStacksNode;
5050
use crate::chainstate::stacks::{Error as ChainstateError, StacksBlock, *};
51+
use crate::config::DEFAULT_MAX_TENURE_BYTES;
5152
use crate::core::{BOOT_BLOCK_HASH, STACKS_EPOCH_3_0_MARKER};
5253
use crate::net::relay::{BlockAcceptResponse, Relayer};
5354
use crate::net::test::{TestPeer, *};
@@ -796,6 +797,7 @@ impl TestStacksNode {
796797
None,
797798
None,
798799
None,
800+
u64::from(DEFAULT_MAX_TENURE_BYTES),
799801
)?
800802
} else {
801803
NakamotoBlockBuilder::new_first_block(

0 commit comments

Comments
 (0)