Skip to content

Commit ba74430

Browse files
committed
Some cleanup; fix spurious failure in chainstate bootstrap tests
1 parent 63b7369 commit ba74430

11 files changed

Lines changed: 61 additions & 67 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ The format is loosely based on [Keep a Changelog](https://keepachangelog.com/en/
8484
- Importing a bootstrap file will no longer fail if some of the blocks already exist in the
8585
chainstate.
8686

87+
- Bootstrapping can now be interrupted via Ctrl-C.
88+
8789
- The speed of the import was improved.
8890

8991
- General

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

chainstate/src/detail/bootstrap.rs

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ struct BootstrapFileHeader {
5454
pub blocks_count: u64,
5555
}
5656

57+
/// Size of the encoded BootstrapFileHeader.
5758
const FILE_HEADER_SIZE: usize = 24;
5859

5960
// In format v0, blocks go directly after the header, each block preceded by its length
@@ -134,20 +135,20 @@ pub fn import_bootstrap_stream<P, S: std::io::Read>(
134135
where
135136
P: FnMut(WithId<Block>) -> Result<bool, BootstrapError>,
136137
{
137-
let mut buffer_queue = Vec::<u8>::with_capacity(1024 * 1024);
138+
let mut buffer = Vec::<u8>::with_capacity(1024 * 1024);
138139

139140
let header = {
140-
fill_buffer(&mut buffer_queue, file_reader, FILE_HEADER_SIZE)?;
141+
fill_buffer(&mut buffer, file_reader, FILE_HEADER_SIZE)?;
141142
ensure!(
142-
buffer_queue.len() == FILE_HEADER_SIZE,
143+
buffer.len() == FILE_HEADER_SIZE,
143144
BootstrapError::FileTooSmall
144145
);
145-
check_for_legacy_format(&buffer_queue)?;
146+
check_for_legacy_format(&buffer)?;
146147

147-
BootstrapFileHeader::decode_all(&mut buffer_queue.as_slice())?
148+
BootstrapFileHeader::decode_all(&mut buffer.as_slice())?
148149
};
149150

150-
buffer_queue.clear();
151+
buffer.clear();
151152

152153
ensure!(
153154
&header.file_magic_bytes == FILE_MAGIC_BYTES,
@@ -163,33 +164,27 @@ where
163164
);
164165

165166
for _ in 0..header.blocks_count {
166-
fill_buffer(&mut buffer_queue, file_reader, size_of::<BlockSizeType>())?;
167+
fill_buffer(&mut buffer, file_reader, size_of::<BlockSizeType>())?;
167168
ensure!(
168-
buffer_queue.len() == size_of::<BlockSizeType>(),
169+
buffer.len() == size_of::<BlockSizeType>(),
169170
BootstrapError::BadFileFormat
170171
);
171172
let block_size = BlockSizeType::from_le_bytes(
172-
buffer_queue
173-
.as_slice()
174-
.try_into()
175-
.expect("Buffer is known to have the correct size"),
173+
buffer.as_slice().try_into().expect("Buffer is known to have the correct size"),
176174
)
177175
.try_into()?;
178176
ensure!(
179177
block_size <= MAX_BLOCK_SIZE,
180178
BootstrapError::BlockSizeTooBig(block_size)
181179
);
182-
buffer_queue.clear();
180+
buffer.clear();
183181

184-
fill_buffer(&mut buffer_queue, file_reader, block_size)?;
185-
ensure!(
186-
buffer_queue.len() == block_size,
187-
BootstrapError::BadFileFormat
188-
);
182+
fill_buffer(&mut buffer, file_reader, block_size)?;
183+
ensure!(buffer.len() == block_size, BootstrapError::BadFileFormat);
189184

190-
let block = Block::decode_all(&mut buffer_queue.as_slice())?;
185+
let block = Block::decode_all(&mut buffer.as_slice())?;
191186
let should_continue = process_block_func(block.into())?;
192-
buffer_queue.clear();
187+
buffer.clear();
193188

194189
if !should_continue {
195190
break;
@@ -210,19 +205,19 @@ fn check_for_legacy_format(header_bytes: &[u8]) -> Result<(), BootstrapError> {
210205
}
211206

212207
fn fill_buffer<S: std::io::Read>(
213-
buffer_queue: &mut Vec<u8>,
208+
buffer: &mut Vec<u8>,
214209
reader: &mut std::io::BufReader<S>,
215210
max_buffer_size: usize,
216211
) -> Result<(), BootstrapError> {
217-
while buffer_queue.len() < max_buffer_size {
212+
while buffer.len() < max_buffer_size {
218213
let data = reader.fill_buf()?;
219214
if data.is_empty() {
220215
break;
221216
}
222217

223-
let remaining_len = max_buffer_size - buffer_queue.len();
218+
let remaining_len = max_buffer_size - buffer.len();
224219
let len_to_consume = std::cmp::min(remaining_len, data.len());
225-
buffer_queue.extend_from_slice(&data[..len_to_consume]);
220+
buffer.extend_from_slice(&data[..len_to_consume]);
226221
reader.consume(len_to_consume);
227222
}
228223

chainstate/src/detail/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -903,6 +903,8 @@ impl<S: BlockchainStorage, V: TransactionVerificationStrategy> Chainstate<S, V>
903903
let chain_config = Arc::clone(&self.chain_config);
904904
let mut block_processor = |block: WithId<Block>| -> Result<_, BootstrapError> {
905905
// If chainstate is being shutdown, stop immediately.
906+
// Note that we return an error instead of Ok(false) to avoid an interrupted import
907+
// being treated as successful.
906908
if self.shutdown_initiated_rx.as_ref().is_some_and(|rx| rx.borrow().test()) {
907909
return Err(BootstrapError::Interrupted);
908910
}
@@ -995,6 +997,8 @@ where
995997
///
996998
/// The main purpose is to make sure the reckless mode stays enabled during bootstrapping for
997999
/// the entire duration (i.e. for fresh blocks too).
1000+
///
1001+
/// Note: it is assumed that inc/dec are only called during bootstrapping or ibd.
9981002
struct DbRecklessModeCounter {
9991003
counter: u32,
10001004
}

chainstate/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,10 @@ use common::{
3030
primitives::{BlockHeight, Id},
3131
time_getter::TimeGetter,
3232
};
33-
use detail::Chainstate;
34-
use interface::chainstate_interface_impl;
3533
use utils::set_flag::SetFlag;
3634

35+
use crate::{detail::Chainstate, interface::chainstate_interface_impl};
36+
3737
pub use crate::{
3838
config::{ChainstateConfig, MaxTipAge},
3939
detail::{

chainstate/test-suite/src/tests/bootstrap.rs

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ fn check_height_order(blocks: &[Id<Block>], tf: &TestFramework) {
4747
}
4848
}
4949

50+
fn make_chain_config(chain_type: ChainType) -> ChainConfig {
51+
chain::config::Builder::new(chain_type)
52+
.consensus_upgrades(NetUpgrades::unit_tests())
53+
.data_in_no_signature_witness_allowed(true)
54+
.genesis_unittest(Destination::AnyoneCanSpend)
55+
// Force empty checkpoints because a custom genesis is used.
56+
.checkpoints(BTreeMap::new())
57+
.build()
58+
}
59+
5060
const EXPECTED_MAGIC_BYTES: &str = "MLBTSTRP";
5161

5262
fn make_header_data(chain_config: &ChainConfig, version: u32, blocks_count: u64) -> Vec<u8> {
@@ -69,16 +79,20 @@ fn gen_blocks(
6979
blocks_count: usize,
7080
mut rng: impl Rng + CryptoRng,
7181
) -> Vec<Block> {
72-
let mut tf = TestFramework::builder(&mut rng).with_chain_config(chain_config).build();
73-
let genesis_id = tf.genesis().get_id();
74-
tf.create_chain(&genesis_id.into(), blocks_count, &mut rng).unwrap();
75-
76-
tf.chainstate
77-
.get_block_id_tree_as_list()
78-
.unwrap()
79-
.iter()
80-
.map(|block_id| tf.chainstate.get_block(block_id).unwrap().unwrap())
81-
.collect_vec()
82+
if blocks_count > 0 {
83+
let mut tf = TestFramework::builder(&mut rng).with_chain_config(chain_config).build();
84+
let genesis_id = tf.genesis().get_id();
85+
tf.create_chain(&genesis_id.into(), blocks_count, &mut rng).unwrap();
86+
87+
tf.chainstate
88+
.get_block_id_tree_as_list()
89+
.unwrap()
90+
.iter()
91+
.map(|block_id| tf.chainstate.get_block(block_id).unwrap().unwrap())
92+
.collect_vec()
93+
} else {
94+
Vec::new()
95+
}
8296
}
8397

8498
fn export_to_vec(tf: &TestFramework, with_stale_blocks: bool) -> Vec<u8> {
@@ -528,16 +542,6 @@ fn unsupported_version(#[case] seed: Seed) {
528542
});
529543
}
530544

531-
fn make_chain_config(chain_type: ChainType) -> ChainConfig {
532-
chain::config::Builder::new(chain_type)
533-
.consensus_upgrades(NetUpgrades::unit_tests())
534-
.data_in_no_signature_witness_allowed(true)
535-
.genesis_unittest(Destination::AnyoneCanSpend)
536-
// Force empty checkpoints because a custom genesis is used.
537-
.checkpoints(BTreeMap::new())
538-
.build()
539-
}
540-
541545
// The data starts with wrong format magic bytes.
542546
#[rstest]
543547
#[trace]
@@ -707,6 +711,8 @@ fn block_size_too_big(#[case] seed: Seed) {
707711
});
708712
}
709713

714+
// If chainstate reckless mode is enabled, it should be switched on when bootstrapping starts
715+
// and off when it ends, including the case when it ends due to an error.
710716
#[rstest]
711717
#[trace]
712718
#[case(Seed::from_entropy())]

node-daemon/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ authors = ["Samer Afach <samer.afach@mintlayer.org>", "Ben Marsh <benjamin.marsh
99
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1010

1111
[dependencies]
12-
chainstate = { path = "../chainstate" }
1312
logging = { path = "../logging" }
1413
node-lib = { path = "../node-lib/" }
1514
utils = { path = "../utils" }

node-lib/src/node_daemon_runner.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,10 @@ pub async fn run_node_daemon() -> anyhow::Result<ExitCode> {
5252
);
5353
}
5454
Err(err) => {
55-
// Note: we don't return it as a generic error, because bootstrapping will
56-
// likely fail due to user's mistake rather than node's malfunction, so we
57-
// don't want for e.g. the stack trace to be printed (which would happen with
58-
// a "normal" error when backtrace is enabled).
55+
// Note: we don't return an error here, because bootstrapping will likely fail
56+
// due to a user mistake rather than node malfunction, so we don't want for
57+
// e.g. the stack trace to be printed in this case (anyhow::Error does this
58+
// when backtrace is enabled).
5959
logging::log::error!("Node bootstrapping failed: {err}");
6060
return Ok(ExitCode(1));
6161
}

storage/failing/src/backend.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -107,14 +107,6 @@ impl<T> FailingImpl<T> {
107107
transaction_failures: 0,
108108
}
109109
}
110-
111-
pub fn inner(&self) -> &T {
112-
&self.inner
113-
}
114-
115-
pub fn inner_mut(&mut self) -> &mut T {
116-
&mut self.inner
117-
}
118110
}
119111

120112
impl<T: Clone> Clone for FailingImpl<T> {

storage/src/database/mod.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ impl<B: Backend, Sch: Schema> Storage<B, Sch> {
7171
pub fn backend_impl(&self) -> &B::Impl {
7272
&self.backend
7373
}
74-
75-
pub fn backend_impl_mut(&mut self) -> &mut B::Impl {
76-
&mut self.backend
77-
}
7874
}
7975

8076
impl<B: Backend, Sch: Schema> Storage<B, Sch> {

0 commit comments

Comments
 (0)