Skip to content

Commit 0865787

Browse files
authored
Merge branch 'develop' into fix/5285
2 parents 8e8981e + 12296c7 commit 0865787

File tree

5 files changed

+142
-97
lines changed

5 files changed

+142
-97
lines changed

clarity/src/vm/docs/mod.rs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,17 +1775,17 @@ this value is less than or equal to the value for `miner-spend-total` at the sam
17751775
const GET_BURN_BLOCK_INFO_API: SpecialAPI = SpecialAPI {
17761776
input_type: "BurnBlockInfoPropertyName, uint",
17771777
output_type: "(optional buff) | (optional (tuple (addrs (list 2 (tuple (hashbytes (buff 32)) (version (buff 1))))) (payout uint)))",
1778-
snippet: "get-burn-block-info? ${1:prop} ${2:block-height}",
1779-
signature: "(get-burn-block-info? prop-name block-height)",
1778+
snippet: "get-burn-block-info? ${1:prop} ${2:burn-block-height}",
1779+
signature: "(get-burn-block-info? prop-name burn-block-height)",
17801780
description: "The `get-burn-block-info?` function fetches data for a block of the given *burnchain* block height. The
1781-
value and type returned are determined by the specified `BlockInfoPropertyName`. Valid values for `block-height` only
1781+
value and type returned are determined by the specified `BlockInfoPropertyName`. Valid values for `burn-block-height` only
17821782
include heights between the burnchain height at the time the Stacks chain was launched, and the last-processed burnchain
1783-
block. If the `block-height` argument falls outside of this range, then `none` shall be returned.
1783+
block. If the `burn-block-height` argument falls outside of this range, then `none` shall be returned.
17841784
17851785
The following `BlockInfoPropertyName` values are defined:
17861786
17871787
* The `header-hash` property returns a 32-byte buffer representing the header hash of the burnchain block at
1788-
burnchain height `block-height`.
1788+
burnchain height `burn-block-height`.
17891789
17901790
* The `pox-addrs` property returns a tuple with two items: a list of up to two PoX addresses that received a PoX payout at that block height, and the amount of burnchain
17911791
tokens paid to each address (note that per the blockchain consensus rules, each PoX payout will be the same for each address in the block-commit transaction).
@@ -1811,11 +1811,11 @@ The `addrs` list contains the same PoX address values passed into the PoX smart
18111811

18121812
const GET_STACKS_BLOCK_INFO_API: SpecialAPI = SpecialAPI {
18131813
input_type: "StacksBlockInfoPropertyName, uint",
1814-
snippet: "get-stacks-block-info? ${1:prop} ${2:block-height}",
1814+
snippet: "get-stacks-block-info? ${1:prop} ${2:stacks-block-height}",
18151815
output_type: "(optional buff) | (optional uint)",
1816-
signature: "(get-stacks-block-info? prop-name block-height)",
1816+
signature: "(get-stacks-block-info? prop-name stacks-block-height)",
18171817
description: "The `get-stacks-block-info?` function fetches data for a block of the given *Stacks* block height. The
1818-
value and type returned are determined by the specified `StacksBlockInfoPropertyName`. If the provided `block-height` does
1818+
value and type returned are determined by the specified `StacksBlockInfoPropertyName`. If the provided `stacks-block-height` does
18191819
not correspond to an existing block prior to the current block, the function returns `none`. The currently available property names
18201820
are as follows:
18211821
@@ -1840,11 +1840,11 @@ the mining of this block started, but is not guaranteed to be accurate. This tim
18401840

18411841
const GET_TENURE_INFO_API: SpecialAPI = SpecialAPI {
18421842
input_type: "TenureInfoPropertyName, uint",
1843-
snippet: "get-tenure-info? ${1:prop} ${2:block-height}",
1843+
snippet: "get-tenure-info? ${1:prop} ${2:stacks-block-height}",
18441844
output_type: "(optional buff) | (optional uint)",
1845-
signature: "(get-tenure-info? prop-name block-height)",
1845+
signature: "(get-tenure-info? prop-name stacks-block-height)",
18461846
description: "The `get-tenure-info?` function fetches data for the tenure at the given block height. The
1847-
value and type returned are determined by the specified `TenureInfoPropertyName`. If the provided `block-height` does
1847+
value and type returned are determined by the specified `TenureInfoPropertyName`. If the provided `stacks-block-height` does
18481848
not correspond to an existing block prior to the current block, the function returns `none`. The currently available property names
18491849
are as follows:
18501850

stacks-signer/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SpawnedSigner
121121
as this could potentially expose sensitive data or functionalities to security risks \
122122
if additional proper security checks are not integrated in place. \
123123
For more information, check the documentation at \
124-
https://docs.stacks.co/nakamoto-upgrade/signing-and-stacking/faq#what-should-the-networking-setup-for-my-signer-look-like."
124+
https://docs.stacks.co/guides-and-tutorials/running-a-signer#preflight-setup"
125125
);
126126
let (res_send, res_recv) = channel();
127127
let ev = SignerEventReceiver::new(config.network.is_mainnet());

stackslib/src/net/relay.rs

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2504,14 +2504,27 @@ impl Relayer {
25042504
for chunk in sync_result.chunks_to_store.into_iter() {
25052505
let md = chunk.get_slot_metadata();
25062506
if let Err(e) = tx.try_replace_chunk(&sc, &md, &chunk.data) {
2507-
warn!(
2508-
"Failed to store chunk for StackerDB";
2509-
"stackerdb_contract_id" => &format!("{}", &sync_result.contract_id),
2510-
"slot_id" => md.slot_id,
2511-
"slot_version" => md.slot_version,
2512-
"num_bytes" => chunk.data.len(),
2513-
"error" => %e
2514-
);
2507+
if matches!(e, Error::StaleChunk { .. }) {
2508+
// This is a common and expected message, so log it as a debug and with a sep message
2509+
// to distinguish it from other message types.
2510+
debug!(
2511+
"Dropping stale StackerDB chunk";
2512+
"stackerdb_contract_id" => &format!("{}", &sync_result.contract_id),
2513+
"slot_id" => md.slot_id,
2514+
"slot_version" => md.slot_version,
2515+
"num_bytes" => chunk.data.len(),
2516+
"error" => %e
2517+
);
2518+
} else {
2519+
warn!(
2520+
"Failed to store chunk for StackerDB";
2521+
"stackerdb_contract_id" => &format!("{}", &sync_result.contract_id),
2522+
"slot_id" => md.slot_id,
2523+
"slot_version" => md.slot_version,
2524+
"num_bytes" => chunk.data.len(),
2525+
"error" => %e
2526+
);
2527+
}
25152528
continue;
25162529
} else {
25172530
debug!("Stored chunk"; "stackerdb_contract_id" => &format!("{}", &sync_result.contract_id), "slot_id" => md.slot_id, "slot_version" => md.slot_version);

testnet/stacks-node/src/run_loop/nakamoto.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -723,10 +723,17 @@ impl RunLoop {
723723

724724
// at tip, and not downloading. proceed to mine.
725725
if last_tenure_sortition_height != sortition_db_height {
726-
info!(
727-
"Runloop: Synchronized full burnchain up to height {}. Proceeding to mine blocks",
728-
sortition_db_height
729-
);
726+
if is_miner {
727+
info!(
728+
"Runloop: Synchronized full burnchain up to height {}. Proceeding to mine blocks",
729+
sortition_db_height
730+
);
731+
} else {
732+
info!(
733+
"Runloop: Synchronized full burnchain up to height {}.",
734+
sortition_db_height
735+
);
736+
}
730737
last_tenure_sortition_height = sortition_db_height;
731738
globals.raise_initiative("runloop-synced".to_string());
732739
}

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

Lines changed: 98 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,7 @@ fn multiple_miners() {
14601460
let node_2_rpc_bind = format!("{localhost}:{node_2_rpc}");
14611461
let mut node_2_listeners = Vec::new();
14621462

1463+
let max_nakamoto_tenures = 30;
14631464
// partition the signer set so that ~half are listening and using node 1 for RPC and events,
14641465
// and the rest are using node 2
14651466

@@ -1481,6 +1482,7 @@ fn multiple_miners() {
14811482
config.node.p2p_address = format!("{localhost}:{node_1_p2p}");
14821483
config.miner.wait_on_interim_blocks = Duration::from_secs(5);
14831484
config.node.pox_sync_sample_secs = 30;
1485+
config.burnchain.pox_reward_length = Some(max_nakamoto_tenures);
14841486

14851487
config.node.seed = btc_miner_1_seed.clone();
14861488
config.node.local_peer_seed = btc_miner_1_seed.clone();
@@ -1560,8 +1562,6 @@ fn multiple_miners() {
15601562

15611563
info!("------------------------- Reached Epoch 3.0 -------------------------");
15621564

1563-
let max_nakamoto_tenures = 20;
1564-
15651565
// due to the random nature of mining sortitions, the way this test is structured
15661566
// is that we keep track of how many tenures each miner produced, and once enough sortitions
15671567
// have been produced such that each miner has produced 3 tenures, we stop and check the
@@ -1645,11 +1645,11 @@ fn multiple_miners() {
16451645
assert_eq!(peer_1_height, peer_2_height);
16461646
assert_eq!(
16471647
peer_1_height,
1648-
pre_nakamoto_peer_1_height + btc_blocks_mined - 1
1648+
pre_nakamoto_peer_1_height + btc_blocks_mined as u64 - 1
16491649
);
16501650
assert_eq!(
16511651
btc_blocks_mined,
1652-
u64::try_from(miner_1_tenures + miner_2_tenures).unwrap()
1652+
u32::try_from(miner_1_tenures + miner_2_tenures).unwrap()
16531653
);
16541654

16551655
rl2_coord_channels
@@ -1664,7 +1664,7 @@ fn multiple_miners() {
16641664
/// Read processed nakamoto block IDs from the test observer, and use `config` to open
16651665
/// a chainstate DB and returns their corresponding StacksHeaderInfos
16661666
fn get_nakamoto_headers(config: &Config) -> Vec<StacksHeaderInfo> {
1667-
let nakamoto_block_ids: Vec<_> = test_observer::get_blocks()
1667+
let nakamoto_block_ids: HashSet<_> = test_observer::get_blocks()
16681668
.into_iter()
16691669
.filter_map(|block_json| {
16701670
if block_json
@@ -1746,6 +1746,8 @@ fn miner_forking() {
17461746
let node_2_rpc_bind = format!("{localhost}:{node_2_rpc}");
17471747
let mut node_2_listeners = Vec::new();
17481748

1749+
let max_sortitions = 30;
1750+
17491751
// partition the signer set so that ~half are listening and using node 1 for RPC and events,
17501752
// and the rest are using node 2
17511753

@@ -1776,6 +1778,7 @@ fn miner_forking() {
17761778
config.burnchain.local_mining_public_key = Some(btc_miner_1_pk.to_hex());
17771779
config.miner.mining_key = Some(Secp256k1PrivateKey::from_seed(&[1]));
17781780
config.node.pox_sync_sample_secs = 30;
1781+
config.burnchain.pox_reward_length = Some(max_sortitions as u32);
17791782

17801783
config.events_observers.retain(|listener| {
17811784
let Ok(addr) = std::net::SocketAddr::from_str(&listener.endpoint) else {
@@ -1797,11 +1800,10 @@ fn miner_forking() {
17971800
);
17981801
let conf = signer_test.running_nodes.conf.clone();
17991802
let mut conf_node_2 = conf.clone();
1800-
let localhost = "127.0.0.1";
1801-
conf_node_2.node.rpc_bind = format!("{}:{}", localhost, node_2_rpc);
1802-
conf_node_2.node.p2p_bind = format!("{}:{}", localhost, node_2_p2p);
1803-
conf_node_2.node.data_url = format!("http://{}:{}", localhost, node_2_rpc);
1804-
conf_node_2.node.p2p_address = format!("{}:{}", localhost, node_2_p2p);
1803+
conf_node_2.node.rpc_bind = node_2_rpc_bind;
1804+
conf_node_2.node.p2p_bind = format!("{localhost}:{node_2_p2p}");
1805+
conf_node_2.node.data_url = format!("http://{localhost}:{node_2_rpc}");
1806+
conf_node_2.node.p2p_address = format!("{localhost}:{node_2_p2p}");
18051807
conf_node_2.node.seed = btc_miner_2_seed.clone();
18061808
conf_node_2.burnchain.local_mining_public_key = Some(btc_miner_2_pk.to_hex());
18071809
conf_node_2.node.local_peer_seed = btc_miner_2_seed.clone();
@@ -1931,7 +1933,6 @@ fn miner_forking() {
19311933
// (a) its the first nakamoto tenure
19321934
// (b) the prior sortition didn't have a tenure (because by this time RL2 will have up-to-date block processing)
19331935
let mut expects_miner_2_to_be_valid = true;
1934-
let max_sortitions = 20;
19351936
// due to the random nature of mining sortitions, the way this test is structured
19361937
// is that keeps track of two scenarios that we want to cover, and once enough sortitions
19371938
// have been produced to cover those scenarios, it stops and checks the results at the end.
@@ -3242,7 +3243,8 @@ fn signer_set_rollover() {
32423243

32433244
#[test]
32443245
#[ignore]
3245-
/// This test checks that the signers will broadcast a block once they receive enough signatures.
3246+
/// This test checks that the miners and signers will not produce Nakamoto blocks
3247+
/// until the minimum time has passed between blocks.
32463248
fn min_gap_between_blocks() {
32473249
if env::var("BITCOIND_TEST") != Ok("1".into()) {
32483250
return;
@@ -3259,11 +3261,14 @@ fn min_gap_between_blocks() {
32593261
let sender_addr = tests::to_addr(&sender_sk);
32603262
let send_amt = 100;
32613263
let send_fee = 180;
3264+
3265+
let mut sender_nonce = 0;
3266+
let interim_blocks = 5;
32623267
let recipient = PrincipalData::from(StacksAddress::burn_address(false));
32633268
let time_between_blocks_ms = 10_000;
32643269
let mut signer_test: SignerTest<SpawnedSigner> = SignerTest::new_with_config_modifications(
32653270
num_signers,
3266-
vec![(sender_addr.clone(), send_amt + send_fee)],
3271+
vec![(sender_addr.clone(), (send_amt + send_fee) * interim_blocks)],
32673272
|_config| {},
32683273
|config| {
32693274
config.miner.min_time_between_blocks_ms = time_between_blocks_ms;
@@ -3276,73 +3281,81 @@ fn min_gap_between_blocks() {
32763281

32773282
signer_test.boot_to_epoch_3();
32783283

3279-
info!("Ensure that the first Nakamoto block is mined after the gap is exceeded");
3284+
info!("Ensure that the first Nakamoto block was mined");
32803285
let blocks = get_nakamoto_headers(&signer_test.running_nodes.conf);
32813286
assert_eq!(blocks.len(), 1);
3282-
let first_block = blocks.last().unwrap();
3283-
let blocks = test_observer::get_blocks();
3284-
let parent = blocks
3285-
.iter()
3286-
.find(|b| b.get("block_height").unwrap() == first_block.stacks_block_height - 1)
3287-
.unwrap();
3288-
let first_block_time = first_block
3289-
.anchored_header
3290-
.as_stacks_nakamoto()
3291-
.unwrap()
3292-
.timestamp;
3293-
let parent_block_time = parent.get("burn_block_time").unwrap().as_u64().unwrap();
3294-
assert!(
3295-
Duration::from_secs(first_block_time - parent_block_time)
3296-
>= Duration::from_millis(time_between_blocks_ms),
3297-
"First block proposed before gap was exceeded: {}s - {}s > {}ms",
3298-
first_block_time,
3299-
parent_block_time,
3300-
time_between_blocks_ms
3301-
);
3287+
// mine the interim blocks
3288+
info!("Mining interim blocks");
3289+
for interim_block_ix in 0..interim_blocks {
3290+
let blocks_processed_before = signer_test
3291+
.running_nodes
3292+
.nakamoto_blocks_mined
3293+
.load(Ordering::SeqCst);
3294+
// submit a tx so that the miner will mine an extra block
3295+
let transfer_tx = make_stacks_transfer(
3296+
&sender_sk,
3297+
sender_nonce,
3298+
send_fee,
3299+
signer_test.running_nodes.conf.burnchain.chain_id,
3300+
&recipient,
3301+
send_amt,
3302+
);
3303+
sender_nonce += 1;
3304+
submit_tx(&http_origin, &transfer_tx);
33023305

3303-
// Submit a tx so that the miner will mine a block
3304-
let sender_nonce = 0;
3305-
let transfer_tx = make_stacks_transfer(
3306-
&sender_sk,
3307-
sender_nonce,
3308-
send_fee,
3309-
signer_test.running_nodes.conf.burnchain.chain_id,
3310-
&recipient,
3311-
send_amt,
3312-
);
3313-
submit_tx(&http_origin, &transfer_tx);
3306+
info!("Submitted transfer tx and waiting for block to be processed");
3307+
wait_for(60, || {
3308+
let blocks_processed = signer_test
3309+
.running_nodes
3310+
.nakamoto_blocks_mined
3311+
.load(Ordering::SeqCst);
3312+
Ok(blocks_processed > blocks_processed_before)
3313+
})
3314+
.unwrap();
3315+
info!("Mined interim block:{}", interim_block_ix);
3316+
}
33143317

3315-
info!("Submitted transfer tx and waiting for block to be processed. Ensure it does not arrive before the gap is exceeded");
33163318
wait_for(60, || {
3317-
let blocks = get_nakamoto_headers(&signer_test.running_nodes.conf);
3318-
Ok(blocks.len() >= 2)
3319+
let new_blocks = get_nakamoto_headers(&signer_test.running_nodes.conf);
3320+
Ok(new_blocks.len() == blocks.len() + interim_blocks as usize)
33193321
})
33203322
.unwrap();
33213323

3322-
// Verify that the second Nakamoto block is mined after the gap is exceeded
3323-
let blocks = get_nakamoto_headers(&signer_test.running_nodes.conf);
3324-
let last_block = blocks.last().unwrap();
3325-
let last_block_time = last_block
3326-
.anchored_header
3327-
.as_stacks_nakamoto()
3328-
.unwrap()
3329-
.timestamp;
3330-
assert!(blocks.len() >= 2, "Expected at least 2 mined blocks");
3331-
let penultimate_block = blocks.get(blocks.len() - 2).unwrap();
3332-
let penultimate_block_time = penultimate_block
3333-
.anchored_header
3334-
.as_stacks_nakamoto()
3335-
.unwrap()
3336-
.timestamp;
3337-
assert!(
3338-
Duration::from_secs(last_block_time - penultimate_block_time)
3339-
>= Duration::from_millis(time_between_blocks_ms),
3340-
"Block proposed before gap was exceeded: {}s - {}s > {}ms",
3341-
last_block_time,
3342-
penultimate_block_time,
3343-
time_between_blocks_ms
3344-
);
3345-
3324+
// Verify that every Nakamoto block is mined after the gap is exceeded between each
3325+
let mut blocks = get_nakamoto_headers(&signer_test.running_nodes.conf);
3326+
blocks.sort_by(|a, b| a.stacks_block_height.cmp(&b.stacks_block_height));
3327+
for i in 1..blocks.len() {
3328+
let block = &blocks[i];
3329+
let parent_block = &blocks[i - 1];
3330+
assert_eq!(
3331+
block.stacks_block_height,
3332+
parent_block.stacks_block_height + 1
3333+
);
3334+
info!(
3335+
"Checking that the time between blocks {} and {} is respected",
3336+
parent_block.stacks_block_height, block.stacks_block_height
3337+
);
3338+
let block_time = block
3339+
.anchored_header
3340+
.as_stacks_nakamoto()
3341+
.unwrap()
3342+
.timestamp;
3343+
let parent_block_time = parent_block
3344+
.anchored_header
3345+
.as_stacks_nakamoto()
3346+
.unwrap()
3347+
.timestamp;
3348+
assert!(
3349+
block_time > parent_block_time,
3350+
"Block time is BEFORE parent block time"
3351+
);
3352+
assert!(
3353+
Duration::from_secs(block_time - parent_block_time)
3354+
>= Duration::from_millis(time_between_blocks_ms),
3355+
"Block mined before gap was exceeded: {block_time}s - {parent_block_time}s > {time_between_blocks_ms}ms",
3356+
);
3357+
}
3358+
debug!("Shutting down min_gap_between_blocks test");
33463359
signer_test.shutdown();
33473360
}
33483361

@@ -5316,7 +5329,19 @@ fn signing_in_0th_tenure_of_reward_cycle() {
53165329
})
53175330
.unwrap();
53185331

5319-
for signer in &signer_public_keys {
5332+
let block_mined = test_observer::get_mined_nakamoto_blocks()
5333+
.last()
5334+
.unwrap()
5335+
.clone();
5336+
// Must ensure that the signers that signed the block have their blocks_signed updated appropriately
5337+
for signature in &block_mined.signer_signature {
5338+
let signer = signer_public_keys
5339+
.iter()
5340+
.find(|pk| {
5341+
pk.verify(block_mined.signer_signature_hash.as_bytes(), signature)
5342+
.unwrap()
5343+
})
5344+
.expect("Unknown signer signature");
53205345
let blocks_signed = get_v3_signer(&signer, next_reward_cycle);
53215346
assert_eq!(blocks_signed, 1);
53225347
}

0 commit comments

Comments
 (0)