diff --git a/.github/workflows/bitcoin-tests.yml b/.github/workflows/bitcoin-tests.yml index fee0d91639..66adcf4f94 100644 --- a/.github/workflows/bitcoin-tests.yml +++ b/.github/workflows/bitcoin-tests.yml @@ -27,6 +27,20 @@ jobs: - name: Checkout the latest code id: git_checkout uses: actions/checkout@v3 + + - name: Reclaim disk space + id: cleanup + run: | + sudo apt-get remove -y '^dotnet-.*' + sudo apt-get remove -y '^llvm-.*' + sudo apt-get remove -y 'php.*' + sudo apt-get remove -y '^mongodb-.*' + sudo apt-get remove -y '^mysql-.*' + sudo apt-get remove -y azure-cli google-cloud-sdk google-chrome-stable firefox powershell mono-devel libgl1-mesa-dri + sudo apt-get autoremove -y + sudo apt-get clean + docker system prune --force + - name: Build bitcoin integration testing image id: build_docker_image env: @@ -35,15 +49,17 @@ jobs: run: | rm .dockerignore docker build -f ./.github/actions/bitcoin-int-tests/Dockerfile.generic.bitcoin-tests -t stacks-blockchain:integrations . + - name: Export docker image as tarball id: export_docker_image - run: docker save -o integration-image.tar stacks-blockchain:integrations + run: docker save stacks-blockchain:integrations | gzip > integration-image.tar.gz + - name: Upload built docker image id: upload_docker_image uses: actions/upload-artifact@v3 with: - name: integration-image.tar - path: integration-image.tar + name: integration-image.tar.gz + path: integration-image.tar.gz # Run integration tests using sampled genesis block sampled-genesis: @@ -130,10 +146,10 @@ jobs: id: download_docker_image uses: actions/download-artifact@v3 with: - name: integration-image.tar + name: integration-image.tar.gz - name: Load docker image id: load_docker_image - run: docker load -i integration-image.tar && rm integration-image.tar + run: docker load -i integration-image.tar.gz && rm integration-image.tar.gz - name: All integration tests with sampled genesis id: bitcoin_integration_tests timeout-minutes: 30 @@ -169,10 +185,10 @@ jobs: id: download_docker_image uses: actions/download-artifact@v3 with: - name: integration-image.tar + name: integration-image.tar.gz - name: Load docker image id: load_docker_image - run: docker load -i integration-image.tar && rm integration-image.tar + run: docker load -i integration-image.tar.gz && rm integration-image.tar.gz - name: Atlas integration tests id: atlas_integration_tests timeout-minutes: 40 diff --git a/.github/workflows/stacks-blockchain-tests.yml b/.github/workflows/stacks-blockchain-tests.yml index 56ffccdab4..8c39de6e2d 100644 --- a/.github/workflows/stacks-blockchain-tests.yml +++ b/.github/workflows/stacks-blockchain-tests.yml @@ -44,18 +44,39 @@ jobs: run: | rustup component add llvm-tools-preview cargo install grcov + - name: Add nextest + run: | + cargo install cargo-nextest - name: Checkout the latest code id: git_checkout uses: actions/checkout@v3 - name: Run units tests (with coverage) id: unit_tests_codecov + # unset the coverage instrumentation flags. + # these slow tests down a lot locally (5-10x), and since + # grcov cannot collect this coverage data right now anyways, + # this is a speed win. + #env: + # RUSTFLAGS: -Cinstrument-coverage + # LLVM_PROFILE_FILE: stacks-blockchain-%p-%m.profraw + run: | + cargo nextest run --workspace + - name: Collate grcov + # grcov doesn't work with cargo nextest currently, getting + # that to work again will have to happen separately. Getting unit + # test run times below 2 hours is more important for now. + if: ${{ false }} + id: unit_tests_grcov env: RUSTFLAGS: -Cinstrument-coverage LLVM_PROFILE_FILE: stacks-blockchain-%p-%m.profraw run: | - cargo test --workspace grcov . --binary-path ./target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore "/*" -o lcov.info - name: Upload codecov results + # grcov doesn't work with cargo nextest currently, getting + # that to work again will have to happen separately. Getting unit + # test run times below 2 hours is more important for now. + if: ${{ false }} uses: codecov/codecov-action@v3 id: codedov with: diff --git a/Cargo.lock b/Cargo.lock index 9f587649c5..19108e3b9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2813,19 +2813,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tempfile" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" -dependencies = [ - "cfg-if 1.0.0", - "fastrand", - "redox_syscall", - "rustix", - "windows-sys 0.42.0", -] - [[package]] name = "term" version = "0.7.0" diff --git a/src/clarity_vm/tests/analysis_costs.rs b/src/clarity_vm/tests/analysis_costs.rs index 8483e3ba4b..397e556d05 100644 --- a/src/clarity_vm/tests/analysis_costs.rs +++ b/src/clarity_vm/tests/analysis_costs.rs @@ -26,7 +26,6 @@ use clarity::vm::contracts::Contract; use clarity::vm::costs::ExecutionCost; use clarity::vm::database::{ClarityDatabase, NULL_BURN_STATE_DB, NULL_HEADER_DB}; use clarity::vm::errors::{CheckErrors, Error, RuntimeErrorType}; -use clarity::vm::execute as vm_execute; use clarity::vm::functions::NativeFunctions; use clarity::vm::representations::SymbolicExpression; use clarity::vm::test_util::{TEST_BURN_STATE_DB, TEST_HEADER_DB}; @@ -34,6 +33,7 @@ use clarity::vm::tests::{execute, symbols_from_values, UnitTestBurnStateDB}; use clarity::vm::types::{ AssetIdentifier, PrincipalData, QualifiedContractIdentifier, ResponseData, Value, }; +use clarity::vm::{execute as vm_execute, ContractName}; use stacks_common::util::hash::hex_bytes; use crate::chainstate::stacks::index::ClarityMarfTrieId; @@ -44,12 +44,11 @@ use crate::types::StacksEpochId; use clarity::vm::tests::test_only_mainnet_to_chain_id; use clarity::vm::ClarityVersion; -pub fn test_tracked_costs( - prog: &str, +fn setup_tracked_cost_test( use_mainnet: bool, epoch: StacksEpochId, version: ClarityVersion, -) -> ExecutionCost { +) -> ClarityInstance { let marf = MarfedKV::temporary(); let chain_id = test_only_mainnet_to_chain_id(use_mainnet); let mut clarity_instance = ClarityInstance::new(use_mainnet, chain_id, marf); @@ -70,21 +69,6 @@ pub fn test_tracked_costs( (define-map map-foo { a: int } { b: int }) (define-public (foo-exec (a int)) (ok 1))"; - let contract_self = format!( - "(define-map map-foo {{ a: int }} {{ b: int }}) - (define-non-fungible-token nft-foo int) - (define-fungible-token ft-foo) - (define-data-var var-foo int 0) - (define-constant tuple-foo (tuple (a 1))) - (define-constant list-foo (list true)) - (define-constant list-bar (list 1)) - (define-constant str-foo \"foobar\") - (use-trait trait-1 .contract-trait.trait-1) - (define-public (execute (contract )) (ok {}))", - prog - ); - - let self_contract_id = QualifiedContractIdentifier::new(p1_principal.clone(), "self".into()); let other_contract_id = QualifiedContractIdentifier::new(p1_principal.clone(), "contract-other".into()); let trait_contract_id = @@ -192,10 +176,53 @@ pub fn test_tracked_costs( conn.commit_block(); } + clarity_instance +} + +fn test_tracked_costs( + prog: &str, + epoch: StacksEpochId, + version: ClarityVersion, + prog_id: usize, + clarity_instance: &mut ClarityInstance, +) -> ExecutionCost { + let contract_self = format!( + "(define-map map-foo {{ a: int }} {{ b: int }}) + (define-non-fungible-token nft-foo int) + (define-fungible-token ft-foo) + (define-data-var var-foo int 0) + (define-constant tuple-foo (tuple (a 1))) + (define-constant list-foo (list true)) + (define-constant list-bar (list 1)) + (define-constant str-foo \"foobar\") + (use-trait trait-1 .contract-trait.trait-1) + (define-public (execute (contract )) (ok {}))", + prog + ); + + let p1 = vm_execute("'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR") + .unwrap() + .unwrap(); + + let p1_principal = match p1 { + Value::Principal(PrincipalData::Standard(ref data)) => data.clone(), + _ => panic!(), + }; + + let self_contract_id = QualifiedContractIdentifier::new( + p1_principal.clone(), + ContractName::try_from(format!("self-{}", prog_id)).unwrap(), + ); + + let burn_state_db = UnitTestBurnStateDB { + epoch_id: epoch, + ast_rules: ASTRules::PrecheckSize, + }; + { let mut conn = clarity_instance.begin_block( &StacksBlockId([3 as u8; 32]), - &StacksBlockId([4 as u8; 32]), + &StacksBlockId([4 + prog_id as u8; 32]), &TEST_HEADER_DB, &burn_state_db, ); @@ -226,15 +253,17 @@ pub fn test_tracked_costs( } fn epoch_21_test_all(use_mainnet: bool, version: ClarityVersion) { - let baseline = test_tracked_costs("1", use_mainnet, StacksEpochId::Epoch21, version); + let mut instance = setup_tracked_cost_test(use_mainnet, StacksEpochId::Epoch21, version); + + let baseline = test_tracked_costs("1", StacksEpochId::Epoch21, version, 0, &mut instance); - for f in NativeFunctions::ALL.iter() { + for (ix, f) in NativeFunctions::ALL.iter().enumerate() { if version < f.get_version() { continue; } let test = get_simple_test(f); - let cost = test_tracked_costs(test, use_mainnet, StacksEpochId::Epoch21, version); + let cost = test_tracked_costs(test, StacksEpochId::Epoch21, version, ix + 1, &mut instance); assert!(cost.exceeds(&baseline)); } } @@ -252,21 +281,28 @@ fn epoch_21_test_all_testnet() { } fn epoch_205_test_all(use_mainnet: bool) { + let mut instance = setup_tracked_cost_test( + use_mainnet, + StacksEpochId::Epoch2_05, + ClarityVersion::Clarity1, + ); let baseline = test_tracked_costs( "1", - use_mainnet, StacksEpochId::Epoch2_05, ClarityVersion::Clarity1, + 0, + &mut instance, ); - for f in NativeFunctions::ALL.iter() { + for (ix, f) in NativeFunctions::ALL.iter().enumerate() { if f.get_version() == ClarityVersion::Clarity1 { let test = get_simple_test(f); let cost = test_tracked_costs( test, - use_mainnet, StacksEpochId::Epoch2_05, ClarityVersion::Clarity1, + ix + 1, + &mut instance, ); assert!(cost.exceeds(&baseline)); } diff --git a/src/clarity_vm/tests/costs.rs b/src/clarity_vm/tests/costs.rs index 19b43ba1f3..baa24d17dd 100644 --- a/src/clarity_vm/tests/costs.rs +++ b/src/clarity_vm/tests/costs.rs @@ -43,6 +43,7 @@ use clarity::vm::types::{ AssetIdentifier, OptionalData, PrincipalData, QualifiedContractIdentifier, ResponseData, Value, }; use clarity::vm::ClarityVersion; +use clarity::vm::ContractName; use stacks_common::util::hash::hex_bytes; use std::collections::HashMap; @@ -826,12 +827,11 @@ fn epoch205_nfts_testnet() { epoch205_nfts(false) } -fn test_tracked_costs( - prog: &str, +fn setup_cost_tracked_test( use_mainnet: bool, - epoch: StacksEpochId, version: ClarityVersion, -) -> ExecutionCost { + owned_env: &mut OwnedEnvironment, +) { let contract_trait = "(define-trait trait-1 ( (foo-exec (int) (response int int)) ))"; @@ -839,6 +839,49 @@ fn test_tracked_costs( (define-map map-foo { a: int } { b: int }) (define-public (foo-exec (a int)) (ok 1))"; + let p1 = execute("'SZ2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQ9H6DPR"); + let p2 = execute("'SM2J6ZY48GV1EZ5V2V5RB9MP66SW86PYKKQVX8X0G"); + + let p1_principal = match p1 { + Value::Principal(PrincipalData::Standard(ref data)) => data.clone(), + _ => panic!(), + }; + let p2_principal = match p2 { + Value::Principal(ref data) => data.clone(), + _ => panic!(), + }; + + let other_contract_id = + QualifiedContractIdentifier::new(p1_principal.clone(), "contract-other".into()); + let trait_contract_id = + QualifiedContractIdentifier::new(p1_principal.clone(), "contract-trait".into()); + + owned_env + .initialize_versioned_contract( + trait_contract_id.clone(), + version, + contract_trait, + None, + ASTRules::PrecheckSize, + ) + .unwrap(); + owned_env + .initialize_versioned_contract( + other_contract_id.clone(), + version, + contract_other, + None, + ASTRules::PrecheckSize, + ) + .unwrap(); +} + +fn test_program_cost( + prog: &str, + version: ClarityVersion, + owned_env: &mut OwnedEnvironment, + prog_id: usize, +) -> ExecutionCost { let contract_self = format!( "(define-map map-foo {{ a: int }} {{ b: int }}) (define-non-fungible-token nft-foo int) @@ -865,149 +908,96 @@ fn test_tracked_costs( _ => panic!(), }; - let self_contract_id = QualifiedContractIdentifier::new(p1_principal.clone(), "self".into()); + let self_contract_id = QualifiedContractIdentifier::new( + p1_principal.clone(), + ContractName::try_from(format!("self-{}", prog_id)).unwrap(), + ); let other_contract_id = QualifiedContractIdentifier::new(p1_principal.clone(), "contract-other".into()); - let trait_contract_id = - QualifiedContractIdentifier::new(p1_principal.clone(), "contract-trait".into()); - - with_owned_env(epoch, use_mainnet, |mut owned_env| { - owned_env - .initialize_versioned_contract( - trait_contract_id.clone(), - version, - contract_trait, - None, - ASTRules::PrecheckSize, - ) - .unwrap(); - owned_env - .initialize_versioned_contract( - other_contract_id.clone(), - version, - contract_other, - None, - ASTRules::PrecheckSize, - ) - .unwrap(); - owned_env - .initialize_versioned_contract( - self_contract_id.clone(), - version, - &contract_self, - None, - ASTRules::PrecheckSize, - ) - .unwrap(); - let target_contract = Value::from(PrincipalData::Contract(other_contract_id.clone())); - - eprintln!("{}", &contract_self); - execute_transaction( - &mut owned_env, - p2_principal.clone(), - &self_contract_id, - "execute", - &symbols_from_values(vec![target_contract]), + owned_env + .initialize_versioned_contract( + self_contract_id.clone(), + version, + &contract_self, + None, + ASTRules::PrecheckSize, ) .unwrap(); - let (_db, tracker) = owned_env.destruct().unwrap(); - tracker.get_total() - }) + let start = owned_env.get_cost_total(); + + let target_contract = Value::from(PrincipalData::Contract(other_contract_id.clone())); + eprintln!("{}", &contract_self); + execute_transaction( + owned_env, + p2_principal.clone(), + &self_contract_id, + "execute", + &symbols_from_values(vec![target_contract]), + ) + .unwrap(); + + let mut result = owned_env.get_cost_total(); + result.sub(&start).unwrap(); + result } // test each individual cost function can be correctly invoked as // Clarity code executes in Epoch 2.00 -fn epoch_20_test_all(use_mainnet: bool) { - let baseline = test_tracked_costs( - "1", - use_mainnet, - StacksEpochId::Epoch20, - ClarityVersion::Clarity1, - ); +fn epoch_20_205_test_all(use_mainnet: bool, epoch: StacksEpochId) { + with_owned_env(epoch, use_mainnet, |mut owned_env| { + setup_cost_tracked_test(use_mainnet, ClarityVersion::Clarity1, &mut owned_env); - for f in NativeFunctions::ALL.iter() { - // Note: The 2.05 test assumes Clarity1. - if f.get_version() == ClarityVersion::Clarity1 { - let test = get_simple_test(f); - let cost = test_tracked_costs( - test, - use_mainnet, - StacksEpochId::Epoch20, - ClarityVersion::Clarity1, - ); - assert!(cost.exceeds(&baseline)); + let baseline = test_program_cost("1", ClarityVersion::Clarity1, &mut owned_env, 0); + + for (ix, f) in NativeFunctions::ALL.iter().enumerate() { + // Note: The 2.0 and 2.05 test assumes Clarity1. + if f.get_version() == ClarityVersion::Clarity1 { + let test = get_simple_test(f); + let cost = + test_program_cost(test, ClarityVersion::Clarity1, &mut owned_env, ix + 1); + assert!(cost.exceeds(&baseline)); + } } - } + }) } #[test] fn epoch_20_test_all_mainnet() { - epoch_20_test_all(true) + epoch_20_205_test_all(true, StacksEpochId::Epoch20) } #[test] fn epoch_20_test_all_testnet() { - epoch_20_test_all(false) -} - -// test each individual cost function can be correctly invoked as -// Clarity code executes in Epoch 2.05 -fn epoch_205_test_all(use_mainnet: bool) { - let baseline = test_tracked_costs( - "1", - use_mainnet, - StacksEpochId::Epoch2_05, - ClarityVersion::Clarity1, - ); - - for f in NativeFunctions::ALL.iter() { - // Note: The 2.05 test assumes Clarity1. - if f.get_version() == ClarityVersion::Clarity1 { - let test = get_simple_test(f); - let cost = test_tracked_costs( - test, - use_mainnet, - StacksEpochId::Epoch2_05, - ClarityVersion::Clarity1, - ); - assert!(cost.exceeds(&baseline)); - } - } + epoch_20_205_test_all(false, StacksEpochId::Epoch20) } #[test] fn epoch_205_test_all_mainnet() { - epoch_205_test_all(true) + epoch_20_205_test_all(true, StacksEpochId::Epoch2_05) } #[test] fn epoch_205_test_all_testnet() { - epoch_205_test_all(false) + epoch_20_205_test_all(false, StacksEpochId::Epoch2_05) } // test each individual cost function can be correctly invoked as // Clarity code executes in Epoch 2.1 fn epoch_21_test_all(use_mainnet: bool) { - let baseline = test_tracked_costs( - "1", - use_mainnet, - StacksEpochId::Epoch21, - ClarityVersion::Clarity2, - ); + with_owned_env(StacksEpochId::Epoch21, use_mainnet, |mut owned_env| { + setup_cost_tracked_test(use_mainnet, ClarityVersion::Clarity2, &mut owned_env); - for f in NativeFunctions::ALL.iter() { - // Note: Include Clarity2 functions for Epoch21. - let test = get_simple_test(f); - let cost = test_tracked_costs( - test, - use_mainnet, - StacksEpochId::Epoch21, - ClarityVersion::Clarity2, - ); - assert!(cost.exceeds(&baseline)); - } + let baseline = test_program_cost("1", ClarityVersion::Clarity2, &mut owned_env, 0); + + for (ix, f) in NativeFunctions::ALL.iter().enumerate() { + // Note: Include Clarity2 functions for Epoch21. + let test = get_simple_test(f); + let cost = test_program_cost(test, ClarityVersion::Clarity2, &mut owned_env, ix + 1); + assert!(cost.exceeds(&baseline)); + } + }) } #[test]