From 4b1499d8637c129eab204d4069cb35455fb8f942 Mon Sep 17 00:00:00 2001 From: Hugo Caillard <911307+hugocaillard@users.noreply.github.com> Date: Tue, 24 Sep 2024 15:44:28 +0200 Subject: [PATCH] tests: add unit tests --- Cargo.lock | 2 +- components/clarity-repl/src/repl/datastore.rs | 24 ++- .../clarity-repl/src/repl/interpreter.rs | 190 ++++++++++++++++-- components/clarity-repl/src/repl/session.rs | 44 ++-- 4 files changed, 207 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a87925ce8..6ac2bf0e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -755,7 +755,7 @@ checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] name = "clar2wasm" version = "0.1.0" -source = "git+https://github.com/stacks-network/clarity-wasm.git?branch=main#6402754a71c3998b7febde69e06bbe582def9875" +source = "git+https://github.com/stacks-network/clarity-wasm.git?branch=main#1a1ab12dba3dfb243363fbf9ba955d7dab060936" dependencies = [ "chrono", "clap", diff --git a/components/clarity-repl/src/repl/datastore.rs b/components/clarity-repl/src/repl/datastore.rs index 771754a9c..f77667f63 100644 --- a/components/clarity-repl/src/repl/datastore.rs +++ b/components/clarity-repl/src/repl/datastore.rs @@ -504,16 +504,25 @@ impl Datastore { clarity_datastore .height_at_chain_tip .insert(id, self.stacks_chain_height); + clarity_datastore.open_chain_tip = height_to_id(self.stacks_chain_height); + clarity_datastore.current_chain_tip = clarity_datastore.open_chain_tip; } - clarity_datastore.open_chain_tip = height_to_id(self.stacks_chain_height); - clarity_datastore.current_chain_tip = clarity_datastore.open_chain_tip; self.stacks_chain_height } - pub fn set_current_epoch(&mut self, epoch: StacksEpochId) { + pub fn set_current_epoch( + &mut self, + clarity_datastore: &mut ClarityDatastore, + epoch: StacksEpochId, + ) { self.current_epoch = epoch; self.current_epoch_start_height = self.stacks_chain_height; + if epoch >= StacksEpochId::Epoch30 { + // ideally the burn chain tip should be advanced for each new epoch + // but this would introduce breaking changes to existing 2.x tests + self.advance_burn_chain_tip(clarity_datastore, 1); + } } } @@ -532,11 +541,9 @@ impl HeadersDB for Datastore { &self, id_bhh: &StacksBlockId, ) -> Option { - let hash = self - .stacks_blocks + self.stacks_blocks .get(id_bhh) - .map(|block| block.burn_block_header_hash); - hash + .map(|block| block.burn_block_header_hash) } fn get_consensus_hash_for_block( @@ -761,8 +768,9 @@ mod tests { #[test] fn test_set_current_epoch() { let mut datastore = Datastore::default(); + let mut clarity_datastore = ClarityDatastore::new(); let epoch_id = StacksEpochId::Epoch25; - datastore.set_current_epoch(epoch_id); + datastore.set_current_epoch(&mut clarity_datastore, epoch_id); assert_eq!(datastore.current_epoch, epoch_id); } diff --git a/components/clarity-repl/src/repl/interpreter.rs b/components/clarity-repl/src/repl/interpreter.rs index f19cfeca0..22df7047d 100644 --- a/components/clarity-repl/src/repl/interpreter.rs +++ b/components/clarity-repl/src/repl/interpreter.rs @@ -1092,7 +1092,8 @@ impl ClarityInterpreter { } pub fn set_current_epoch(&mut self, epoch: StacksEpochId) { - self.datastore.set_current_epoch(epoch); + self.datastore + .set_current_epoch(&mut self.clarity_datastore, epoch); } pub fn advance_burn_chain_tip(&mut self, count: u32) -> u32 { @@ -1238,6 +1239,24 @@ mod tests { assert_eq!(result.result, expected_value); } + #[track_caller] + fn run_snippet( + interpreter: &mut ClarityInterpreter, + snippet: &str, + clarity_version: ClarityVersion, + ) -> Value { + let contract = ClarityContractBuilder::new() + .code_source(snippet.to_string()) + .epoch(interpreter.datastore.get_current_epoch()) + .clarity_version(clarity_version) + .build(); + let deploy_result = deploy_contract(interpreter, &contract); + match deploy_result.unwrap().result { + EvaluationResult::Contract(_) => unreachable!(), + EvaluationResult::Snippet(res) => res.result, + } + } + #[test] fn test_get_tx_sender() { let mut interpreter = @@ -1277,9 +1296,7 @@ mod tests { fn test_advance_stacks_chain_tip_pre_epoch_3() { let mut interpreter = ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); - interpreter - .datastore - .set_current_epoch(StacksEpochId::Epoch2_05); + interpreter.set_current_epoch(StacksEpochId::Epoch2_05); let count = 5; let initial_block_height = interpreter.get_burn_block_height(); assert_ne!(interpreter.advance_stacks_chain_tip(count), Ok(count)); @@ -1296,14 +1313,13 @@ mod tests { }; let mut interpreter = ClarityInterpreter::new(StandardPrincipalData::transient(), wasm_settings); - interpreter - .datastore - .set_current_epoch(StacksEpochId::Epoch30); + interpreter.set_current_epoch(StacksEpochId::Epoch30); + interpreter.advance_burn_chain_tip(1); let count = 5; - let initial_block_height = interpreter.get_burn_block_height(); + let initial_block_height = interpreter.get_block_height(); let result = interpreter.advance_stacks_chain_tip(count); - assert_eq!(result, Ok(count)); + assert_eq!(result, Ok(initial_block_height + count)); assert_eq!(interpreter.get_burn_block_height(), initial_block_height); assert_eq!(interpreter.get_block_height(), initial_block_height + count); @@ -1313,9 +1329,7 @@ mod tests { fn test_advance_chain_tip_pre_epoch3() { let mut interpreter = ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); - interpreter - .datastore - .set_current_epoch(StacksEpochId::Epoch2_05); + interpreter.set_current_epoch(StacksEpochId::Epoch2_05); let count = 5; let initial_block_height = interpreter.get_block_height(); interpreter.advance_burn_chain_tip(count); @@ -1330,9 +1344,7 @@ mod tests { fn test_advance_chain_tip() { let mut interpreter = ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); - interpreter - .datastore - .set_current_epoch(StacksEpochId::Epoch30); + interpreter.set_current_epoch(StacksEpochId::Epoch30); let count = 5; let initial_block_height = interpreter.get_block_height(); interpreter.advance_burn_chain_tip(count); @@ -1973,6 +1985,154 @@ mod tests { assert!(interpreter.run(&call_contract, None, false, None).is_ok()); } + #[test] + fn burn_block_time_is_realistic_in_epoch_3_0() { + let mut interpreter = + ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + + interpreter.set_current_epoch(StacksEpochId::Epoch30); + interpreter.advance_burn_chain_tip(3); + + let snippet_1 = run_snippet( + &mut interpreter, + "(get-tenure-info? time u2)", + ClarityVersion::Clarity3, + ); + let time_block_1 = match snippet_1.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + + let snippet_2 = run_snippet( + &mut interpreter, + "(get-tenure-info? time u3)", + ClarityVersion::Clarity3, + ); + let time_block_2 = match snippet_2.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + assert_eq!(time_block_2 - time_block_1, 600); + } + + #[test] + fn first_stacks_block_time_in_a_tenure() { + let mut interpreter = + ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + + interpreter.set_current_epoch(StacksEpochId::Epoch30); + let _ = interpreter.advance_burn_chain_tip(2); + + let snippet_1 = run_snippet( + &mut interpreter, + "(get-tenure-info? time (- stacks-block-height u1))", + ClarityVersion::Clarity3, + ); + let last_tenure_time = match snippet_1.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + + let snippet_2 = run_snippet( + &mut interpreter, + "(get-stacks-block-info? time (- stacks-block-height u1))", + ClarityVersion::Clarity3, + ); + let last_stacks_block_time = match snippet_2.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + assert_eq!((last_stacks_block_time) - (last_tenure_time), 10); + } + + #[test] + fn stacks_block_time_is_realistic_in_epoch_3_0() { + let mut interpreter = + ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + + interpreter.set_current_epoch(StacksEpochId::Epoch30); + let _ = interpreter.advance_stacks_chain_tip(3); + + let snippet_1 = run_snippet( + &mut interpreter, + "(get-stacks-block-info? time u2)", + ClarityVersion::Clarity3, + ); + let time_block_1 = match snippet_1.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + + let snippet_2 = run_snippet( + &mut interpreter, + "(get-stacks-block-info? time u3)", + ClarityVersion::Clarity3, + ); + let time_block_2 = match snippet_2.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + assert_eq!(time_block_2 - time_block_1, 10); + } + + #[test] + fn burn_block_time_after_many_stacks_blocks_is_realistic_in_epoch_3_0() { + let mut interpreter = + ClarityInterpreter::new(StandardPrincipalData::transient(), Settings::default()); + + interpreter.set_current_epoch(StacksEpochId::Epoch30); + // by advancing stacks_chain_tip by 101, we are getting a tenure of more than 600 seconds + // the next burn block should happen after the last stacks block + let stacks_block_height = interpreter.advance_stacks_chain_tip(101).unwrap(); + assert_eq!(stacks_block_height, 102); + + let snippet_1 = run_snippet( + &mut interpreter, + "(get-stacks-block-info? time u1)", + ClarityVersion::Clarity3, + ); + let stacks_block_time_1 = match snippet_1.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + + let snippet_2 = run_snippet( + &mut interpreter, + "(get-stacks-block-info? time u101)", + ClarityVersion::Clarity3, + ); + let stacks_block_time_2 = match snippet_2.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + assert_eq!(stacks_block_time_2 - stacks_block_time_1, 1000); + + let _ = interpreter.advance_burn_chain_tip(1); + let _ = interpreter.advance_stacks_chain_tip(1); + + let snippet_3 = run_snippet( + &mut interpreter, + "(get-tenure-info? time u4)", + ClarityVersion::Clarity3, + ); + let tenure_height_1 = match snippet_3.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + + let snippet_4 = run_snippet( + &mut interpreter, + "(get-tenure-info? time (- stacks-block-height u1))", + ClarityVersion::Clarity3, + ); + let tenure_height_2 = match snippet_4.expect_optional() { + Ok(Some(Value::UInt(time))) => time, + _ => panic!("Unexpected result"), + }; + + assert_eq!(1030, tenure_height_2 - tenure_height_1); + } + #[test] fn can_call_a_public_function() { let mut interpreter = diff --git a/components/clarity-repl/src/repl/session.rs b/components/clarity-repl/src/repl/session.rs index e59ee9f47..30c910b71 100644 --- a/components/clarity-repl/src/repl/session.rs +++ b/components/clarity-repl/src/repl/session.rs @@ -1394,7 +1394,7 @@ mod tests { session.handle_command("::set_epoch 3.0"); let _ = session.handle_command("::advance_stacks_chain_tip 1"); let new_height = session.handle_command("::get_stacks_block_height"); - assert_eq!(new_height, "Current height: 1"); + assert_eq!(new_height, "Current height: 2"); } #[test] @@ -1426,24 +1426,25 @@ mod tests { let result = session.handle_command("::advance_burn_chain_tip 1"); assert_eq!( result, - "new burn height: 1\nnew stacks height: 1" + "new burn height: 2\nnew stacks height: 2" .to_string() .green() .to_string() ); let new_height = session.handle_command("::get_stacks_block_height"); - assert_eq!(new_height, "Current height: 1"); + assert_eq!(new_height, "Current height: 2"); // advance_chain_tip will only affect stacks height in epoch 3 or greater let _ = session.handle_command("::advance_chain_tip 1"); let new_height = session.handle_command("::get_stacks_block_height"); - assert_eq!(new_height, "Current height: 2"); + assert_eq!(new_height, "Current height: 3"); let new_height = session.handle_command("::get_burn_block_height"); - assert_eq!(new_height, "Current height: 1"); + assert_eq!(new_height, "Current height: 2"); } #[test] fn set_epoch_command() { let mut session = Session::new(SessionSettings::default()); + let initial_block_height = session.interpreter.get_block_height(); let initial_epoch = session.handle_command("::get_epoch"); // initial epoch is 2.05 assert_eq!(initial_epoch, "Current epoch: 2.05"); @@ -1459,9 +1460,18 @@ mod tests { let current_epoch = session.handle_command("::get_epoch"); assert_eq!(current_epoch, "Current epoch: 2.4"); + // changing epoch in 2.x does not impact the block height + assert_eq!(session.interpreter.get_block_height(), initial_block_height); + session.handle_command("::set_epoch 3.0"); let current_epoch = session.handle_command("::get_epoch"); assert_eq!(current_epoch, "Current epoch: 3.0"); + + // changing epoch in 3.x increments the block height + assert_eq!( + session.interpreter.get_block_height(), + initial_block_height + 1 + ); } #[test] @@ -1764,28 +1774,4 @@ mod tests { assert!(time_block_2 - time_block_1 == 600); } - - #[test] - fn block_time_is_realistic_in_epoch_3_0() { - let settings = SessionSettings::default(); - let mut session = Session::new(settings); - session.start().expect("session could not start"); - session.update_epoch(StacksEpochId::Epoch30); - - session.advance_burn_chain_tip(4); - - let result = run_session_snippet(&mut session, "(get-tenure-info? time u2)"); - let time_block_1 = match result.expect_optional() { - Ok(Some(Value::UInt(time))) => time, - _ => panic!("Unexpected result"), - }; - - let result = run_session_snippet(&mut session, "(get-tenure-info? time u3)"); - let time_block_2 = match result.expect_optional() { - Ok(Some(Value::UInt(time))) => time, - _ => panic!("Unexpected result"), - }; - - assert!(time_block_2 - time_block_1 == 600); - } }