@@ -16,10 +16,11 @@ mod commands;
16
16
mod v0;
17
17
18
18
use std:: collections:: HashSet ;
19
+ use std:: fs:: File ;
19
20
use std:: path:: PathBuf ;
20
21
use std:: sync:: atomic:: { AtomicBool , Ordering } ;
21
22
use std:: sync:: { Arc , Mutex } ;
22
- use std:: time:: { Duration , Instant } ;
23
+ use std:: time:: { Duration , Instant , SystemTime } ;
23
24
use std:: { env, thread} ;
24
25
25
26
use clarity:: boot_util:: boot_code_id;
@@ -30,6 +31,7 @@ use libsigner::v0::messages::{
30
31
} ;
31
32
use libsigner:: v0:: signer_state:: MinerState ;
32
33
use libsigner:: { BlockProposal , SignerEntries , SignerEventTrait } ;
34
+ use serde:: { Deserialize , Serialize } ;
33
35
use stacks:: chainstate:: coordinator:: comm:: CoordinatorChannels ;
34
36
use stacks:: chainstate:: nakamoto:: signer_set:: NakamotoSigners ;
35
37
use stacks:: chainstate:: nakamoto:: NakamotoBlock ;
@@ -44,9 +46,9 @@ use stacks::net::api::postblock_proposal::{
44
46
} ;
45
47
use stacks:: types:: chainstate:: { StacksAddress , StacksBlockId , StacksPublicKey } ;
46
48
use stacks:: types:: PrivateKey ;
49
+ use stacks:: util:: get_epoch_time_secs;
47
50
use stacks:: util:: hash:: MerkleHashFunc ;
48
51
use stacks:: util:: secp256k1:: { MessageSignature , Secp256k1PublicKey } ;
49
- use stacks:: util:: { get_epoch_time_secs, sleep_ms} ;
50
52
use stacks_common:: codec:: StacksMessageCodec ;
51
53
use stacks_common:: consts:: SIGNER_SLOTS_PER_USER ;
52
54
use stacks_common:: types:: StacksEpochId ;
@@ -64,7 +66,6 @@ use super::nakamoto_integrations::{
64
66
use super :: neon_integrations:: {
65
67
copy_dir_all, get_account, get_sortition_info_ch, submit_tx_fallible, Account ,
66
68
} ;
67
- use crate :: burnchains:: bitcoin_regtest_controller:: BitcoinRPCRequest ;
68
69
use crate :: neon:: Counters ;
69
70
use crate :: run_loop:: boot_nakamoto;
70
71
use crate :: tests:: bitcoin_regtest:: BitcoinCoreController ;
@@ -120,6 +121,11 @@ enum SetupSnapshotResult {
120
121
NoSnapshot ,
121
122
}
122
123
124
+ #[ derive( Serialize , Deserialize , Debug , Clone ) ]
125
+ struct SnapshotMetadata {
126
+ created_at : SystemTime ,
127
+ }
128
+
123
129
impl < S : Signer < T > + Send + ' static , T : SignerEventTrait + ' static > SignerTest < SpawnedSigner < S , T > > {
124
130
pub fn new ( num_signers : usize , initial_balances : Vec < ( StacksAddress , u64 ) > ) -> Self {
125
131
Self :: new_with_config_modifications (
@@ -244,36 +250,6 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
244
250
245
251
let snapshot_setup_result = Self :: setup_snapshot ( snapshot_name, & naka_conf) ;
246
252
247
- // let mut snapshot_exists = false;
248
-
249
- // let snapshot_path = snapshot_name.map(|name| {
250
- // let working_dir = naka_conf.get_working_dir();
251
-
252
- // let snapshot_path: PathBuf = format!("/tmp/stacks-node-tests/snapshots/{name}/")
253
- // .try_into()
254
- // .unwrap();
255
-
256
- // info!("Snapshot path: {}", snapshot_path.clone().display());
257
-
258
- // snapshot_exists = std::fs::metadata(snapshot_path.clone()).is_ok();
259
-
260
- // if snapshot_exists {
261
- // info!(
262
- // "Snapshot directory already exists, copying to working dir";
263
- // "snapshot_path" => %snapshot_path.display(),
264
- // "working_dir" => %working_dir.display()
265
- // );
266
- // let err_msg = format!(
267
- // "Failed to copy snapshot dir to working dir: {} -> {}",
268
- // snapshot_path.display(),
269
- // working_dir.display()
270
- // );
271
- // copy_dir_all(snapshot_path.clone(), working_dir).expect(&err_msg);
272
- // }
273
-
274
- // snapshot_path
275
- // });
276
-
277
253
let snapshot_exists = match & snapshot_setup_result {
278
254
SetupSnapshotResult :: WithSnapshot ( info) => info. snapshot_exists ,
279
255
SetupSnapshotResult :: NoSnapshot => false ,
@@ -304,13 +280,6 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
304
280
}
305
281
}
306
282
307
- pub fn snapshot_exists ( & self ) -> bool {
308
- self . snapshot_path
309
- . as_ref ( )
310
- . map ( |p| std:: fs:: metadata ( p) . is_ok ( ) )
311
- . unwrap_or ( false )
312
- }
313
-
314
283
/// Whether the snapshot needs to be created.
315
284
///
316
285
/// Returns `false` if not configured to snapshot.
@@ -345,6 +314,36 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
345
314
let snapshot_exists = std:: fs:: metadata ( snapshot_path. clone ( ) ) . is_ok ( ) ;
346
315
347
316
if snapshot_exists {
317
+ let metadata_path = snapshot_path. join ( "metadata.json" ) ;
318
+ if !metadata_path. clone ( ) . exists ( ) {
319
+ warn ! ( "Snapshot metadata file does not exist, not restoring snapshot" ) ;
320
+ return SetupSnapshotResult :: NoSnapshot ;
321
+ }
322
+ let Ok ( metadata) = serde_json:: from_reader :: < _ , SnapshotMetadata > (
323
+ File :: open ( metadata_path. clone ( ) ) . unwrap ( ) ,
324
+ ) else {
325
+ warn ! (
326
+ "Invalid snapshot metadata file: {}" ,
327
+ metadata_path. display( )
328
+ ) ;
329
+ return SetupSnapshotResult :: NoSnapshot ;
330
+ } ;
331
+
332
+ let now = SystemTime :: now ( ) ;
333
+ let created_at = metadata. created_at ;
334
+ let duration = now. duration_since ( created_at) . unwrap ( ) ;
335
+ // Regtest doesn't like if the last block is > 2 hours old, so
336
+ // don't use this snapshot.
337
+ if duration > Duration :: from_secs ( 3600 * 1 ) {
338
+ // Bitcoin regtest node is too old, act like no snapshot exists
339
+ warn ! ( "Bitcoin regtest node is too old, not restoring snapshot" ) ;
340
+ std:: fs:: remove_dir_all ( snapshot_path. clone ( ) ) . unwrap ( ) ;
341
+ return SetupSnapshotResult :: WithSnapshot ( SnapshotSetupInfo {
342
+ snapshot_path : snapshot_path. clone ( ) ,
343
+ snapshot_exists : false ,
344
+ } ) ;
345
+ }
346
+
348
347
info ! (
349
348
"Snapshot directory already exists, copying to working dir" ;
350
349
"snapshot_path" => %snapshot_path. display( ) ,
@@ -367,12 +366,12 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
367
366
/// Make a snapshot of the current working directory.
368
367
///
369
368
/// This will stop the bitcoind node and copy the working directory to the snapshot path.
370
- pub fn make_snapshot ( & self ) {
371
- let snapshot_path = self . snapshot_path . as_ref ( ) . unwrap ( ) ;
372
-
373
- let working_dir = self . running_nodes . conf . get_working_dir ( ) ;
369
+ pub fn make_snapshot ( working_dir : & PathBuf , snapshot_path : & Option < PathBuf > ) {
370
+ let Some ( snapshot_path) = snapshot_path else {
371
+ return ;
372
+ } ;
374
373
375
- let snapshot_dir_exists = self . snapshot_exists ( ) ;
374
+ let snapshot_dir_exists = std :: fs :: metadata ( snapshot_path ) . is_ok ( ) ;
376
375
377
376
if snapshot_dir_exists {
378
377
info ! ( "Snapshot directory already exists, skipping snapshot" ;
@@ -388,18 +387,20 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
388
387
"working_dir" => %working_dir. display( )
389
388
) ;
390
389
391
- Self :: stop_bitcoind ( & self . running_nodes . conf ) ;
392
-
393
- sleep_ms ( 5000 ) ;
394
-
395
390
let err_msg = format ! (
396
391
"Failed to copy working dir to snapshot path: {} -> {}" ,
397
392
working_dir. display( ) ,
398
393
snapshot_path. display( )
399
394
) ;
400
395
401
- // Copy the working dir to the snapshot path
402
396
copy_dir_all ( working_dir, snapshot_path) . expect ( & err_msg) ;
397
+
398
+ let metadata_path = snapshot_path. join ( "metadata.json" ) ;
399
+ let metadata = SnapshotMetadata {
400
+ created_at : SystemTime :: now ( ) ,
401
+ } ;
402
+ let metadata_file = File :: create ( metadata_path) . unwrap ( ) ;
403
+ serde_json:: to_writer_pretty ( metadata_file, & metadata) . unwrap ( ) ;
403
404
}
404
405
405
406
/// Send a status request to each spawned signer
@@ -1316,7 +1317,15 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
1316
1317
String :: new ( )
1317
1318
}
1318
1319
1320
+ pub fn shutdown_and_snapshot ( self ) {
1321
+ self . shutdown_and_make_snapshot ( true ) ;
1322
+ }
1323
+
1319
1324
pub fn shutdown ( self ) {
1325
+ self . shutdown_and_make_snapshot ( false ) ;
1326
+ }
1327
+
1328
+ fn shutdown_and_make_snapshot ( mut self , needs_snapshot : bool ) {
1320
1329
check_nakamoto_empty_block_heuristics ( ) ;
1321
1330
1322
1331
self . running_nodes
@@ -1325,34 +1334,25 @@ impl<S: Signer<T> + Send + 'static, T: SignerEventTrait + 'static> SignerTest<Sp
1325
1334
. expect ( "Mutex poisoned" )
1326
1335
. stop_chains_coordinator ( ) ;
1327
1336
1337
+ self . running_nodes . btcd_controller . stop_bitcoind ( ) . unwrap ( ) ;
1338
+
1328
1339
self . running_nodes
1329
1340
. run_loop_stopper
1330
1341
. store ( false , Ordering :: SeqCst ) ;
1331
1342
self . running_nodes . run_loop_thread . join ( ) . unwrap ( ) ;
1332
1343
1333
- Self :: stop_bitcoind ( & self . running_nodes . conf ) ;
1344
+ if needs_snapshot {
1345
+ Self :: make_snapshot (
1346
+ & self . running_nodes . conf . get_working_dir ( ) ,
1347
+ & self . snapshot_path ,
1348
+ ) ;
1349
+ }
1334
1350
1335
1351
for signer in self . spawned_signers {
1336
1352
assert ! ( signer. stop( ) . is_none( ) ) ;
1337
1353
}
1338
1354
}
1339
1355
1340
- fn stop_bitcoind ( config : & NeonConfig ) {
1341
- info ! ( "Stopping bitcoind..." ) ;
1342
- let _ = BitcoinRPCRequest :: send (
1343
- config,
1344
- BitcoinRPCRequest {
1345
- method : "stop" . to_string ( ) ,
1346
- params : vec ! [ ] ,
1347
- id : "stacks" . to_string ( ) ,
1348
- jsonrpc : "2.0" . to_string ( ) ,
1349
- } ,
1350
- )
1351
- . inspect_err ( |e| {
1352
- error ! ( "Failed to stop bitcoind: {e:?}" ) ;
1353
- } ) ;
1354
- }
1355
-
1356
1356
/// Get the latest block response from the given slot
1357
1357
pub fn get_latest_block_response ( & self , slot_id : u32 ) -> BlockResponse {
1358
1358
let mut stackerdb = StackerDB :: new_normal (
0 commit comments