diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bb010862f9..552196e4ce 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -148,6 +148,7 @@ jobs: ) || github.event_name == 'workflow_dispatch' || github.event_name == 'pull_request' || + github.event_name == 'merge_group' || ( contains(' refs/heads/master diff --git a/.github/workflows/pr-differences-mutants.yml b/.github/workflows/pr-differences-mutants.yml index 041db97591..fc4a725687 100644 --- a/.github/workflows/pr-differences-mutants.yml +++ b/.github/workflows/pr-differences-mutants.yml @@ -8,7 +8,7 @@ on: - synchronize - ready_for_review paths: - - "**.rs" + - '**.rs' concurrency: group: pr-differences-${{ github.head_ref || github.ref || github.run_id }} @@ -23,10 +23,13 @@ jobs: runs-on: ubuntu-latest outputs: - run_big_packages: ${{ steps.check_packages_and_shards.outputs.run_big_packages }} - big_packages_with_shards: ${{ steps.check_packages_and_shards.outputs.big_packages_with_shards }} + run_stackslib: ${{ steps.check_packages_and_shards.outputs.run_stackslib }} + stackslib_with_shards: ${{ steps.check_packages_and_shards.outputs.stackslib_with_shards }} + run_stacks_node: ${{ steps.check_packages_and_shards.outputs.run_stacks_node }} + stacks_node_with_shards: ${{ steps.check_packages_and_shards.outputs.stacks_node_with_shards }} run_small_packages: ${{ steps.check_packages_and_shards.outputs.run_small_packages }} small_packages_with_shards: ${{ steps.check_packages_and_shards.outputs.small_packages_with_shards }} + run_stacks_signer: ${{ steps.check_packages_and_shards.outputs.run_stacks_signer }} steps: - id: check_packages_and_shards @@ -38,7 +41,9 @@ jobs: needs: check-big-packages-and-shards - if: ${{ needs.check-big-packages-and-shards.outputs.run_small_packages == 'true' && needs.check-big-packages-and-shards.outputs.small_packages_with_shards == 'false' }} + if: | + needs.check-big-packages-and-shards.outputs.run_small_packages == 'true' && + needs.check-big-packages-and-shards.outputs.small_packages_with_shards == 'false' runs-on: ubuntu-latest @@ -46,7 +51,7 @@ jobs: - name: Run mutants on diffs uses: stacks-network/actions/stacks-core/mutation-testing/pr-differences@main with: - package-dimension: "small" + package: 'small' # Mutation testing - Execute on PR on small packages that have functions modified (run with strategy matrix shards) pr-differences-mutants-small-shards: @@ -54,7 +59,9 @@ jobs: needs: check-big-packages-and-shards - if: ${{ needs.check-big-packages-and-shards.outputs.run_small_packages == 'true' && needs.check-big-packages-and-shards.outputs.small_packages_with_shards == 'true' }} + if: | + needs.check-big-packages-and-shards.outputs.run_small_packages == 'true' && + needs.check-big-packages-and-shards.outputs.small_packages_with_shards == 'true' runs-on: ubuntu-latest @@ -68,15 +75,17 @@ jobs: uses: stacks-network/actions/stacks-core/mutation-testing/pr-differences@main with: shard: ${{ matrix.shard }} - package-dimension: "small" + package: 'small' - # Mutation testing - Execute on PR on big packages that have functions modified (normal run, no shards) - pr-differences-mutants-big-normal: - name: Mutation Testing - Normal, Big + # Mutation testing - Execute on PR on stackslib package (normal run, no shards) + pr-differences-mutants-stackslib-normal: + name: Mutation Testing - Normal, Stackslib needs: check-big-packages-and-shards - if: ${{ needs.check-big-packages-and-shards.outputs.run_big_packages == 'true' && needs.check-big-packages-and-shards.outputs.big_packages_with_shards == 'false' }} + if: | + needs.check-big-packages-and-shards.outputs.run_stackslib == 'true' && + needs.check-big-packages-and-shards.outputs.stackslib_with_shards == 'false' runs-on: ubuntu-latest @@ -87,15 +96,17 @@ jobs: RUST_BACKTRACE: full uses: stacks-network/actions/stacks-core/mutation-testing/pr-differences@main with: - package-dimension: "big" + package: 'stackslib' - # Mutation testing - Execute on PR on big packages that have functions modified (run with strategy matrix shards) - pr-differences-mutants-big-shards: - name: Mutation Testing - Shards, Big + # Mutation testing - Execute on PR on stackslib package (run with strategy matrix shards) + pr-differences-mutants-stackslib-shards: + name: Mutation Testing - Shards, Stackslib needs: check-big-packages-and-shards - if: ${{ needs.check-big-packages-and-shards.outputs.run_big_packages == 'true' && needs.check-big-packages-and-shards.outputs.big_packages_with_shards == 'true' }} + if: | + needs.check-big-packages-and-shards.outputs.run_stackslib == 'true' && + needs.check-big-packages-and-shards.outputs.stackslib_with_shards == 'true' runs-on: ubuntu-latest @@ -112,7 +123,72 @@ jobs: uses: stacks-network/actions/stacks-core/mutation-testing/pr-differences@main with: shard: ${{ matrix.shard }} - package-dimension: "big" + package: 'stackslib' + + # Mutation testing - Execute on PR on stacks-node package (normal run, no shards) + pr-differences-mutants-stacks-node-normal: + name: Mutation Testing - Normal, Stacks Node + + needs: check-big-packages-and-shards + + if: | + needs.check-big-packages-and-shards.outputs.run_stacks_node == 'true' && + needs.check-big-packages-and-shards.outputs.stacks_node_with_shards == 'false' + + runs-on: ubuntu-latest + + steps: + - name: Run Run mutants on diffs + env: + BITCOIND_TEST: 1 + RUST_BACKTRACE: full + uses: stacks-network/actions/stacks-core/mutation-testing/pr-differences@main + with: + package: 'stacks-node' + + # Mutation testing - Execute on PR on stacks-node package (run with strategy matrix shards) + pr-differences-mutants-stacks-node-shards: + name: Mutation Testing - Shards, Stacks Node + + needs: check-big-packages-and-shards + + if: | + needs.check-big-packages-and-shards.outputs.run_stacks_node == 'true' && + needs.check-big-packages-and-shards.outputs.stacks_node_with_shards == 'true' + + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + shard: [0, 1, 2, 3] + + steps: + - name: Run mutants on diffs + env: + BITCOIND_TEST: 1 + RUST_BACKTRACE: full + uses: stacks-network/actions/stacks-core/mutation-testing/pr-differences@main + with: + shard: ${{ matrix.shard }} + package: 'stacks-node' + + # Mutation testing - Execute on PR on stacks-signer package (normal run, no shards) + pr-differences-mutants-stacks-signer-normal: + name: Mutation Testing - Normal, Stacks Signer + + needs: check-big-packages-and-shards + + if: | + needs.check-big-packages-and-shards.outputs.run_stacks_signer == 'true' + + runs-on: ubuntu-latest + + steps: + - name: Run mutants on diffs + uses: stacks-network/actions/stacks-core/mutation-testing/pr-differences@main + with: + package: 'stacks-signer' # Output the mutants and fail the workflow if there are missed/timeout/unviable mutants output-mutants: @@ -120,20 +196,27 @@ jobs: runs-on: ubuntu-latest + if: always() needs: [ check-big-packages-and-shards, pr-differences-mutants-small-normal, pr-differences-mutants-small-shards, - pr-differences-mutants-big-normal, - pr-differences-mutants-big-shards, + pr-differences-mutants-stackslib-normal, + pr-differences-mutants-stackslib-shards, + pr-differences-mutants-stacks-node-normal, + pr-differences-mutants-stacks-node-shards, + pr-differences-mutants-stacks-signer-normal, ] steps: - name: Output Mutants uses: stacks-network/actions/stacks-core/mutation-testing/output-pr-mutants@main with: - big_packages: ${{ needs.check-big-packages-and-shards.outputs.run_big_packages }} - shards_for_big_packages: ${{ needs.check-big-packages-and-shards.outputs.big_packages_with_shards }} + stackslib_package: ${{ needs.check-big-packages-and-shards.outputs.run_stackslib }} + shards_for_stackslib_package: ${{ needs.check-big-packages-and-shards.outputs.stackslib_with_shards }} + stacks_node_package: ${{ needs.check-big-packages-and-shards.outputs.run_stacks_node }} + shards_for_stacks_node_package: ${{ needs.check-big-packages-and-shards.outputs.stacks_node_with_shards }} small_packages: ${{ needs.check-big-packages-and-shards.outputs.run_small_packages }} shards_for_small_packages: ${{ needs.check-big-packages-and-shards.outputs.small_packages_with_shards }} + stacks_signer: ${{ needs.check-big-packages-and-shards.outputs.run_stacks_signer }} diff --git a/.github/workflows/stacks-core-tests.yml b/.github/workflows/stacks-core-tests.yml index 7e16bd5b22..c2414a94a4 100644 --- a/.github/workflows/stacks-core-tests.yml +++ b/.github/workflows/stacks-core-tests.yml @@ -160,9 +160,10 @@ jobs: id: codecov uses: stacks-network/actions/codecov@main with: + fail_ci_if_error: true test-name: ${{ matrix.test-name }} upload-only: true - filename: ./lcov.info + filename: ./contrib/core-contract-tests/lcov.info # Core contract tests on Clarinet v1 # Check for false positives/negatives diff --git a/Cargo.lock b/Cargo.lock index aedba0ffaa..c831a5fbaf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -730,7 +730,6 @@ dependencies = [ "serde_derive", "serde_json", "serde_stacker", - "sha2-asm 0.5.5", "slog", "stacks-common", "time 0.2.27", @@ -989,19 +988,6 @@ dependencies = [ "syn 2.0.48", ] -[[package]] -name = "dashmap" -version = "5.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" -dependencies = [ - "cfg-if 1.0.0", - "hashbrown 0.14.3", - "lock_api", - "once_cell", - "parking_lot_core", -] - [[package]] name = "data-encoding" version = "2.5.0" @@ -1990,16 +1976,6 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.20" @@ -2279,29 +2255,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall", - "smallvec", - "windows-targets 0.48.5", -] - [[package]] name = "percent-encoding" version = "2.3.1" @@ -3068,12 +3021,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "sct" version = "0.7.1" @@ -3225,31 +3172,6 @@ dependencies = [ "serde", ] -[[package]] -name = "serial_test" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "953ad9342b3aaca7cb43c45c097dd008d4907070394bd0751a0aa8817e5a018d" -dependencies = [ - "dashmap", - "futures", - "lazy_static", - "log", - "parking_lot", - "serial_test_derive", -] - -[[package]] -name = "serial_test_derive" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93fb4adc70021ac1b47f7d45e8cc4169baaa7ea58483bc5b721d19a26202212" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.48", -] - [[package]] name = "sha1" version = "0.6.1" @@ -3298,16 +3220,7 @@ dependencies = [ "cfg-if 1.0.0", "cpufeatures", "digest 0.10.7", - "sha2-asm 0.6.3", -] - -[[package]] -name = "sha2-asm" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7c2f225be6502f2134e6bbb35bb5e2957e41ffa0495ed08bce2e2b4ca885da4" -dependencies = [ - "cc", + "sha2-asm", ] [[package]] @@ -3546,7 +3459,6 @@ dependencies = [ "serde_derive", "serde_json", "serde_stacker", - "serial_test", "slog", "slog-json", "slog-term", diff --git a/clarity/Cargo.toml b/clarity/Cargo.toml index f6a5c70cbf..4d51cf3e4e 100644 --- a/clarity/Cargo.toml +++ b/clarity/Cargo.toml @@ -56,6 +56,3 @@ developer-mode = [] slog_json = ["stacks_common/slog_json"] testing = [] -[target.'cfg(all(target_arch = "x86_64", not(any(target_os="windows"))))'.dependencies] -sha2-asm = "0.5.3" - diff --git a/clarity/src/libclarity.rs b/clarity/src/libclarity.rs index 4540d15e8c..daae7dcfd7 100644 --- a/clarity/src/libclarity.rs +++ b/clarity/src/libclarity.rs @@ -51,7 +51,6 @@ pub use stacks_common::{ pub mod vm; pub mod boot_util { - use std::convert::TryFrom; use stacks_common::types::chainstate::StacksAddress; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/mod.rs b/clarity/src/vm/analysis/type_checker/v2_05/mod.rs index 7c6b587556..286e2e11fa 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/mod.rs @@ -19,7 +19,6 @@ pub mod contexts; pub mod natives; use std::collections::BTreeMap; -use std::convert::TryInto; use hashbrown::HashMap; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs index 46830a8762..b38cfd0d11 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/mod.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; - use stacks_common::types::StacksEpochId; use super::{ diff --git a/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs b/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs index 81d52bb23d..bed885d147 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/natives/sequences.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{TryFrom, TryInto}; - use stacks_common::types::StacksEpochId; use super::{SimpleNativeFunction, TypedNativeFunction}; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/tests/assets.rs b/clarity/src/vm/analysis/type_checker/v2_05/tests/assets.rs index 918e099671..df9c35ed0e 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/tests/assets.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/tests/assets.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; - use stacks_common::types::StacksEpochId; use crate::vm::analysis::errors::CheckErrors; diff --git a/clarity/src/vm/analysis/type_checker/v2_05/tests/mod.rs b/clarity/src/vm/analysis/type_checker/v2_05/tests/mod.rs index 6529b859f5..1830caf7ce 100644 --- a/clarity/src/vm/analysis/type_checker/v2_05/tests/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_05/tests/mod.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; - use stacks_common::types::StacksEpochId; use crate::vm::analysis::errors::CheckErrors; diff --git a/clarity/src/vm/analysis/type_checker/v2_1/mod.rs b/clarity/src/vm/analysis/type_checker/v2_1/mod.rs index 44aab0b4f2..a0937e84c8 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/mod.rs @@ -18,7 +18,6 @@ pub mod contexts; pub mod natives; use std::collections::BTreeMap; -use std::convert::TryInto; use hashbrown::HashMap; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs index 1c30eb7795..9fdc8c704c 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/mod.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; - use stacks_common::types::StacksEpochId; use super::{ diff --git a/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs b/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs index 52ceca66b6..090b259a26 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/natives/sequences.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{TryFrom, TryInto}; - use stacks_common::types::StacksEpochId; use super::{SimpleNativeFunction, TypedNativeFunction}; diff --git a/clarity/src/vm/analysis/type_checker/v2_1/tests/assets.rs b/clarity/src/vm/analysis/type_checker/v2_1/tests/assets.rs index 8989fb295e..c870fdbab7 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/tests/assets.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/tests/assets.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; - #[cfg(test)] use rstest::rstest; #[cfg(test)] diff --git a/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs b/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs index 25e0eb0548..2f023dcf4f 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/tests/contracts.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; use std::fs::read_to_string; use assert_json_diff::assert_json_eq; diff --git a/clarity/src/vm/analysis/type_checker/v2_1/tests/mod.rs b/clarity/src/vm/analysis/type_checker/v2_1/tests/mod.rs index d8733cfab8..85a6b39ea9 100644 --- a/clarity/src/vm/analysis/type_checker/v2_1/tests/mod.rs +++ b/clarity/src/vm/analysis/type_checker/v2_1/tests/mod.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{TryFrom, TryInto}; - #[cfg(test)] use rstest::rstest; #[cfg(test)] diff --git a/clarity/src/vm/ast/definition_sorter/mod.rs b/clarity/src/vm/ast/definition_sorter/mod.rs index a48bfd0e34..eee6625310 100644 --- a/clarity/src/vm/ast/definition_sorter/mod.rs +++ b/clarity/src/vm/ast/definition_sorter/mod.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::iter::FromIterator; - use hashbrown::{HashMap, HashSet}; use crate::vm::ast::errors::{ParseError, ParseErrors, ParseResult}; diff --git a/clarity/src/vm/ast/parser/v1.rs b/clarity/src/vm/ast/parser/v1.rs index d4dbdcffc4..4cdea6e278 100644 --- a/clarity/src/vm/ast/parser/v1.rs +++ b/clarity/src/vm/ast/parser/v1.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::cmp; -use std::convert::TryInto; use lazy_static::lazy_static; use regex::{Captures, Regex}; diff --git a/clarity/src/vm/ast/parser/v2/mod.rs b/clarity/src/vm/ast/parser/v2/mod.rs index 6238af1383..addbba1c59 100644 --- a/clarity/src/vm/ast/parser/v2/mod.rs +++ b/clarity/src/vm/ast/parser/v2/mod.rs @@ -1,6 +1,5 @@ pub mod lexer; -use std::convert::TryFrom; use std::num::ParseIntError; use stacks_common::util::hash::hex_bytes; diff --git a/clarity/src/vm/ast/sugar_expander/mod.rs b/clarity/src/vm/ast/sugar_expander/mod.rs index cf070548a8..7fc6064b85 100644 --- a/clarity/src/vm/ast/sugar_expander/mod.rs +++ b/clarity/src/vm/ast/sugar_expander/mod.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; - use hashbrown::{HashMap, HashSet}; use crate::vm::ast::errors::{ParseError, ParseErrors, ParseResult}; diff --git a/clarity/src/vm/callables.rs b/clarity/src/vm/callables.rs index 597dbab358..32e7d05514 100644 --- a/clarity/src/vm/callables.rs +++ b/clarity/src/vm/callables.rs @@ -15,9 +15,7 @@ // along with this program. If not, see . use std::collections::BTreeMap; -use std::convert::TryInto; use std::fmt; -use std::iter::FromIterator; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/contexts.rs b/clarity/src/vm/contexts.rs index 104adbab13..4b2d6c46d2 100644 --- a/clarity/src/vm/contexts.rs +++ b/clarity/src/vm/contexts.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{BTreeMap, BTreeSet}; -use std::convert::TryInto; use std::fmt; use std::mem::replace; diff --git a/clarity/src/vm/contracts.rs b/clarity/src/vm/contracts.rs index 0019106ef1..1982665aee 100644 --- a/clarity/src/vm/contracts.rs +++ b/clarity/src/vm/contracts.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; - use stacks_common::types::StacksEpochId; use crate::vm::ast::ContractAST; diff --git a/clarity/src/vm/costs/mod.rs b/clarity/src/vm/costs/mod.rs index 2222aa4c12..e0a664ac64 100644 --- a/clarity/src/vm/costs/mod.rs +++ b/clarity/src/vm/costs/mod.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::BTreeMap; -use std::convert::{TryFrom, TryInto}; use std::{cmp, fmt}; use hashbrown::HashMap; diff --git a/clarity/src/vm/database/clarity_db.rs b/clarity/src/vm/database/clarity_db.rs index 5ef6c458d2..4388e88e58 100644 --- a/clarity/src/vm/database/clarity_db.rs +++ b/clarity/src/vm/database/clarity_db.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{TryFrom, TryInto}; - use serde_json; use stacks_common::address::AddressHashMode; use stacks_common::consts::{ diff --git a/clarity/src/vm/database/clarity_store.rs b/clarity/src/vm/database/clarity_store.rs index f3d9d2bb09..f093c5a3c8 100644 --- a/clarity/src/vm/database/clarity_store.rs +++ b/clarity/src/vm/database/clarity_store.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; use std::path::PathBuf; use rusqlite::Connection; diff --git a/clarity/src/vm/database/key_value_wrapper.rs b/clarity/src/vm/database/key_value_wrapper.rs index bc4b85a9b0..65de1adce4 100644 --- a/clarity/src/vm/database/key_value_wrapper.rs +++ b/clarity/src/vm/database/key_value_wrapper.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::clone::Clone; -use std::cmp::Eq; use std::hash::Hash; use hashbrown::HashMap; diff --git a/clarity/src/vm/database/structures.rs b/clarity/src/vm/database/structures.rs index 53c7fbd681..937eda2bdc 100644 --- a/clarity/src/vm/database/structures.rs +++ b/clarity/src/vm/database/structures.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; use std::io::Write; use serde::Deserialize; diff --git a/clarity/src/vm/docs/contracts.rs b/clarity/src/vm/docs/contracts.rs index 37d1452a1e..7426be7966 100644 --- a/clarity/src/vm/docs/contracts.rs +++ b/clarity/src/vm/docs/contracts.rs @@ -1,5 +1,4 @@ use std::collections::BTreeMap; -use std::iter::FromIterator; use hashbrown::{HashMap, HashSet}; use stacks_common::consts::CHAIN_ID_TESTNET; diff --git a/clarity/src/vm/functions/arithmetic.rs b/clarity/src/vm/functions/arithmetic.rs index bd0edbf5eb..1d52ae4390 100644 --- a/clarity/src/vm/functions/arithmetic.rs +++ b/clarity/src/vm/functions/arithmetic.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::cmp; -use std::convert::TryFrom; use integer_sqrt::IntegerSquareRoot; diff --git a/clarity/src/vm/functions/assets.rs b/clarity/src/vm/functions/assets.rs index 3e926f2cc7..0d004a846a 100644 --- a/clarity/src/vm/functions/assets.rs +++ b/clarity/src/vm/functions/assets.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{TryFrom, TryInto}; - use stacks_common::types::StacksEpochId; use crate::vm::costs::cost_functions::ClarityCostFunction; diff --git a/clarity/src/vm/functions/conversions.rs b/clarity/src/vm/functions/conversions.rs index b788455f9c..090f0d2107 100644 --- a/clarity/src/vm/functions/conversions.rs +++ b/clarity/src/vm/functions/conversions.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; - use stacks_common::codec::StacksMessageCodec; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/functions/database.rs b/clarity/src/vm/functions/database.rs index e52ac57b54..b047faf682 100644 --- a/clarity/src/vm/functions/database.rs +++ b/clarity/src/vm/functions/database.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::cmp; -use std::convert::{TryFrom, TryInto}; use stacks_common::types::chainstate::StacksBlockId; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/functions/principals.rs b/clarity/src/vm/functions/principals.rs index 426fa4f703..99246019da 100644 --- a/clarity/src/vm/functions/principals.rs +++ b/clarity/src/vm/functions/principals.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use stacks_common::address::{ C32_ADDRESS_VERSION_MAINNET_MULTISIG, C32_ADDRESS_VERSION_MAINNET_SINGLESIG, C32_ADDRESS_VERSION_TESTNET_MULTISIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG, diff --git a/clarity/src/vm/functions/sequences.rs b/clarity/src/vm/functions/sequences.rs index 029e62484a..60445f9632 100644 --- a/clarity/src/vm/functions/sequences.rs +++ b/clarity/src/vm/functions/sequences.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::cmp; -use std::convert::{TryFrom, TryInto}; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/mod.rs b/clarity/src/vm/mod.rs index 45d71cf222..9d74fae5d7 100644 --- a/clarity/src/vm/mod.rs +++ b/clarity/src/vm/mod.rs @@ -50,7 +50,6 @@ pub mod test_util; pub mod clarity; use std::collections::BTreeMap; -use std::convert::{TryFrom, TryInto}; use serde_json; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/representations.rs b/clarity/src/vm/representations.rs index 580e9a51c2..15e674eb13 100644 --- a/clarity/src/vm/representations.rs +++ b/clarity/src/vm/representations.rs @@ -16,7 +16,6 @@ use std::borrow::Borrow; use std::cmp::Ordering; -use std::convert::TryFrom; use std::fmt; use std::io::{Read, Write}; use std::ops::Deref; diff --git a/clarity/src/vm/tests/conversions.rs b/clarity/src/vm/tests/conversions.rs index 0d5e559758..dbe45eb724 100644 --- a/clarity/src/vm/tests/conversions.rs +++ b/clarity/src/vm/tests/conversions.rs @@ -15,15 +15,13 @@ // along with this program. If not, see . pub use crate::vm::analysis::errors::{CheckError, CheckErrors}; -use crate::vm::execute_v2; use crate::vm::types::SequenceSubtype::{BufferType, StringType}; use crate::vm::types::StringSubtype::ASCII; use crate::vm::types::TypeSignature::SequenceType; use crate::vm::types::{ ASCIIData, BuffData, BufferLength, CharType, SequenceData, TypeSignature, UTF8Data, Value, }; -use crate::vm::ClarityVersion; -use std::convert::TryFrom; +use crate::vm::{execute_v2, ClarityVersion}; #[test] fn test_simple_buff_to_int_le() { diff --git a/clarity/src/vm/tests/datamaps.rs b/clarity/src/vm/tests/datamaps.rs index 87f5dbcf30..828de608e7 100644 --- a/clarity/src/vm/tests/datamaps.rs +++ b/clarity/src/vm/tests/datamaps.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{From, TryFrom}; - use crate::vm::errors::{CheckErrors, Error, ShortReturnType}; use crate::vm::types::{ ListData, SequenceData, TupleData, TupleTypeSignature, TypeSignature, Value, diff --git a/clarity/src/vm/tests/sequences.rs b/clarity/src/vm/tests/sequences.rs index 51de0e4023..e252f917ee 100644 --- a/clarity/src/vm/tests/sequences.rs +++ b/clarity/src/vm/tests/sequences.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{TryFrom, TryInto}; - use rstest::rstest; use rstest_reuse::{self, *}; use stacks_common::types::StacksEpochId; diff --git a/clarity/src/vm/tests/traits.rs b/clarity/src/vm/tests/traits.rs index 250ebc3412..97c4292b0d 100644 --- a/clarity/src/vm/tests/traits.rs +++ b/clarity/src/vm/tests/traits.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; - use stacks_common::types::StacksEpochId; use super::MemoryEnvironmentGenerator; diff --git a/clarity/src/vm/types/mod.rs b/clarity/src/vm/types/mod.rs index cc3cbeeba0..1c25e1c380 100644 --- a/clarity/src/vm/types/mod.rs +++ b/clarity/src/vm/types/mod.rs @@ -20,7 +20,6 @@ pub mod serialization; pub mod signatures; use std::collections::BTreeMap; -use std::convert::{TryFrom, TryInto}; use std::{char, cmp, fmt, str}; use regex::Regex; diff --git a/clarity/src/vm/types/serialization.rs b/clarity/src/vm/types/serialization.rs index 69a662e6b7..c7a92203b4 100644 --- a/clarity/src/vm/types/serialization.rs +++ b/clarity/src/vm/types/serialization.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::borrow::Borrow; -use std::convert::{TryFrom, TryInto}; use std::io::{Read, Write}; use std::{cmp, error, fmt, str}; diff --git a/clarity/src/vm/types/signatures.rs b/clarity/src/vm/types/signatures.rs index c6838eb3fb..29445d2499 100644 --- a/clarity/src/vm/types/signatures.rs +++ b/clarity/src/vm/types/signatures.rs @@ -16,7 +16,6 @@ use std::collections::btree_map::Entry; use std::collections::BTreeMap; -use std::convert::{TryFrom, TryInto}; use std::hash::{Hash, Hasher}; use std::{cmp, fmt}; diff --git a/clarity/src/vm/variables.rs b/clarity/src/vm/variables.rs index 66de0f3b6e..539e14c39e 100644 --- a/clarity/src/vm/variables.rs +++ b/clarity/src/vm/variables.rs @@ -13,7 +13,6 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; use super::errors::InterpreterError; use crate::vm::contexts::{Environment, LocalContext}; diff --git a/contrib/core-contract-tests/.gitignore b/contrib/core-contract-tests/.gitignore index 39b70c2f7f..393158bd1c 100644 --- a/contrib/core-contract-tests/.gitignore +++ b/contrib/core-contract-tests/.gitignore @@ -4,4 +4,5 @@ npm-debug.log* coverage *.info costs-reports.json -node_modules \ No newline at end of file +node_modules +history.txt diff --git a/contrib/core-contract-tests/Clarinet.toml b/contrib/core-contract-tests/Clarinet.toml index 7bd70e4745..605ce3989f 100644 --- a/contrib/core-contract-tests/Clarinet.toml +++ b/contrib/core-contract-tests/Clarinet.toml @@ -10,12 +10,6 @@ path = "../../stackslib/src/chainstate/stacks/boot/bns.clar" depends_on = [] epoch = 2.4 -[contracts.pox-4] -path = "../../stackslib/src/chainstate/stacks/boot/pox-4.clar" -depends_on = [] -clarity = 2 -epoch = 2.4 - [contracts.signers] path = "../../stackslib/src/chainstate/stacks/boot/signers.clar" depends_on = [] @@ -27,3 +21,25 @@ path = "../../stackslib/src/chainstate/stacks/boot/signers-voting.clar" depends_on = [] clarity = 2 epoch = 2.4 + +[contracts.pox-4] +path = "../../stackslib/src/chainstate/stacks/boot/pox-4.clar" +clarity_version = 2 +epoch = 2.4 +depends_on = ["pox-mainnet"] + +[contracts.pox-mainnet] +path = "../../stackslib/src/chainstate/stacks/boot/pox-mainnet.clar" +clarity_version = 2 +epoch = 2.4 +depends_on = [] + +[contracts.bns_test] +path = "./tests/bns_test.clar" +clarity_version = 2 +epoch = 2.4 + +[contracts.pox_4_test] +path = "./tests/pox_4_test.clar" +clarity_version = 2 +epoch = 2.4 diff --git a/contrib/core-contract-tests/package-lock.json b/contrib/core-contract-tests/package-lock.json index e5c3e22e18..cb7cba8a42 100644 --- a/contrib/core-contract-tests/package-lock.json +++ b/contrib/core-contract-tests/package-lock.json @@ -10,63 +10,119 @@ "license": "ISC", "dependencies": { "@hirosystems/clarinet-sdk": "^1.1.0", - "@stacks/transactions": "^6.9.0", + "@stacks/clarunit": "0.0.1", + "@stacks/transactions": "^6.12.0", "chokidar-cli": "^3.0.0", + "fast-check": "^3.15.1", "typescript": "^5.2.2", "vite": "^4.4.9", "vitest": "^0.34.4", "vitest-environment-clarinet": "^1.0.0" } }, - "node_modules/@esbuild/android-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", - "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "android" - ], + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", "engines": { - "node": ">=12" + "node": ">=0.10.0" } }, - "node_modules/@esbuild/android-arm64": { + "node_modules/@esbuild/darwin-arm64": { "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", - "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", + "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", "cpu": [ "arm64" ], "optional": true, "os": [ - "android" + "darwin" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/android-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", - "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "android" - ], + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, "engines": { - "node": ">=12" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", - "integrity": "sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==", + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@hirosystems/clarinet-sdk": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@hirosystems/clarinet-sdk/-/clarinet-sdk-1.2.0.tgz", + "integrity": "sha512-O0Gyh3pwwOVJTbLlxHG6vSB/KXr+U/nZzd2kpubQO4Qqxjn5/vo8l8J+/fwKOxhzM4QOa42M1sCaVZSB/PkTFg==", + "dependencies": { + "@hirosystems/clarinet-sdk-wasm": "^1.2.0", + "@stacks/transactions": "^6.9.0", + "kolorist": "^1.8.0", + "prompts": "^2.4.2", + "vitest": "^1.0.4", + "yargs": "^17.7.2" + }, + "bin": { + "clarinet-sdk": "dist/cjs/bin/index.js" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@hirosystems/clarinet-sdk-wasm": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@hirosystems/clarinet-sdk-wasm/-/clarinet-sdk-wasm-1.2.0.tgz", + "integrity": "sha512-TnJ243lEgIqHSIeMdEHi1hJceFBJ5mWfjfXv86GKaoyVOS6yX1vGL2a6ZuVO9FfWPNxsiSvaQV/FndVuansAVQ==" + }, + "node_modules/@hirosystems/clarinet-sdk/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.10.tgz", + "integrity": "sha512-YSRRs2zOpwypck+6GL3wGXx2gNP7DXzetmo5pHXLrY/VIMsS59yKfjPizQ4lLt5vEI80M41gjm2BxrGZ5U+VMA==", "cpu": [ "arm64" ], @@ -78,378 +134,882 @@ "node": ">=12" } }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", - "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], + "node_modules/@hirosystems/clarinet-sdk/node_modules/@vitest/expect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.1.0.tgz", + "integrity": "sha512-9IE2WWkcJo2BR9eqtY5MIo3TPmS50Pnwpm66A6neb2hvk/QSLfPXBz2qdiwUOQkwyFuuXEUj5380CbwfzW4+/w==", + "dependencies": { + "@vitest/spy": "1.1.0", + "@vitest/utils": "1.1.0", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@hirosystems/clarinet-sdk/node_modules/@vitest/runner": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.1.0.tgz", + "integrity": "sha512-zdNLJ00pm5z/uhbWF6aeIJCGMSyTyWImy3Fcp9piRGvueERFlQFbUwCpzVce79OLm2UHk9iwaMSOaU9jVHgNVw==", + "dependencies": { + "@vitest/utils": "1.1.0", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@hirosystems/clarinet-sdk/node_modules/@vitest/snapshot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.1.0.tgz", + "integrity": "sha512-5O/wyZg09V5qmNmAlUgCBqflvn2ylgsWJRRuPrnHEfDNT6tQpQ8O1isNGgo+VxofISHqz961SG3iVvt3SPK/QQ==", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@hirosystems/clarinet-sdk/node_modules/@vitest/spy": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.1.0.tgz", + "integrity": "sha512-sNOVSU/GE+7+P76qYo+VXdXhXffzWZcYIPQfmkiRxaNCSPiLANvQx5Mx6ZURJ/ndtEkUJEpvKLXqAYTKEY+lTg==", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@hirosystems/clarinet-sdk/node_modules/@vitest/utils": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.1.0.tgz", + "integrity": "sha512-z+s510fKmYz4Y41XhNs3vcuFTFhcij2YF7F8VQfMEYAAUfqQh0Zfg7+w9xdgFGhPf3tX3TicAe+8BDITk6ampQ==", + "dependencies": { + "diff-sequences": "^29.6.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@hirosystems/clarinet-sdk/node_modules/esbuild": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.10.tgz", + "integrity": "sha512-S1Y27QGt/snkNYrRcswgRFqZjaTG5a5xM3EQo97uNBnH505pdzSNe/HLBq1v0RO7iK/ngdbhJB6mDAp0OK+iUA==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.10", + "@esbuild/android-arm": "0.19.10", + "@esbuild/android-arm64": "0.19.10", + "@esbuild/android-x64": "0.19.10", + "@esbuild/darwin-arm64": "0.19.10", + "@esbuild/darwin-x64": "0.19.10", + "@esbuild/freebsd-arm64": "0.19.10", + "@esbuild/freebsd-x64": "0.19.10", + "@esbuild/linux-arm": "0.19.10", + "@esbuild/linux-arm64": "0.19.10", + "@esbuild/linux-ia32": "0.19.10", + "@esbuild/linux-loong64": "0.19.10", + "@esbuild/linux-mips64el": "0.19.10", + "@esbuild/linux-ppc64": "0.19.10", + "@esbuild/linux-riscv64": "0.19.10", + "@esbuild/linux-s390x": "0.19.10", + "@esbuild/linux-x64": "0.19.10", + "@esbuild/netbsd-x64": "0.19.10", + "@esbuild/openbsd-x64": "0.19.10", + "@esbuild/sunos-x64": "0.19.10", + "@esbuild/win32-arm64": "0.19.10", + "@esbuild/win32-ia32": "0.19.10", + "@esbuild/win32-x64": "0.19.10" + } + }, + "node_modules/@hirosystems/clarinet-sdk/node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", - "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "freebsd" - ], + "node_modules/@hirosystems/clarinet-sdk/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, "engines": { - "node": ">=12" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", - "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], + "node_modules/@hirosystems/clarinet-sdk/node_modules/rollup": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.9.1.tgz", + "integrity": "sha512-pgPO9DWzLoW/vIhlSoDByCzcpX92bKEorbgXuZrqxByte3JFk2xSW2JEeAcyLc9Ru9pqcNNW+Ob7ntsk2oT/Xw==", + "bin": { + "rollup": "dist/bin/rollup" + }, "engines": { - "node": ">=12" + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.9.1", + "@rollup/rollup-android-arm64": "4.9.1", + "@rollup/rollup-darwin-arm64": "4.9.1", + "@rollup/rollup-darwin-x64": "4.9.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.9.1", + "@rollup/rollup-linux-arm64-gnu": "4.9.1", + "@rollup/rollup-linux-arm64-musl": "4.9.1", + "@rollup/rollup-linux-riscv64-gnu": "4.9.1", + "@rollup/rollup-linux-x64-gnu": "4.9.1", + "@rollup/rollup-linux-x64-musl": "4.9.1", + "@rollup/rollup-win32-arm64-msvc": "4.9.1", + "@rollup/rollup-win32-ia32-msvc": "4.9.1", + "@rollup/rollup-win32-x64-msvc": "4.9.1", + "fsevents": "~2.3.2" } }, - "node_modules/@esbuild/linux-arm": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", - "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@hirosystems/clarinet-sdk/node_modules/tinypool": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.1.tgz", + "integrity": "sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==", "engines": { - "node": ">=12" + "node": ">=14.0.0" } }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", - "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@hirosystems/clarinet-sdk/node_modules/vite": { + "version": "5.0.10", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.0.10.tgz", + "integrity": "sha512-2P8J7WWgmc355HUMlFrwofacvr98DAjoE52BfdbwQtyLH06XKwaL/FMnmKM2crF0iX4MpmMKoDlNCB1ok7zHCw==", + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.32", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, "engines": { - "node": ">=12" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", - "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@hirosystems/clarinet-sdk/node_modules/vite-node": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.1.0.tgz", + "integrity": "sha512-jV48DDUxGLEBdHCQvxL1mEh7+naVy+nhUUUaPAZLd3FJgXuxQiewHcfeZebbJ6onDqNGkP4r3MhQ342PRlG81Q==", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, "engines": { - "node": ">=12" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", - "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", - "cpu": [ - "loong64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@hirosystems/clarinet-sdk/node_modules/vitest": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.1.0.tgz", + "integrity": "sha512-oDFiCrw7dd3Jf06HoMtSRARivvyjHJaTxikFxuqJjO76U436PqlVw1uLn7a8OSPrhSfMGVaRakKpA2lePdw79A==", + "dependencies": { + "@vitest/expect": "1.1.0", + "@vitest/runner": "1.1.0", + "@vitest/snapshot": "1.1.0", + "@vitest/spy": "1.1.0", + "@vitest/utils": "1.1.0", + "acorn-walk": "^8.3.0", + "cac": "^6.7.14", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^1.3.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.1", + "vite": "^5.0.0", + "vite-node": "1.1.0", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, "engines": { - "node": ">=12" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "^1.0.0", + "@vitest/ui": "^1.0.0", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } } }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", - "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", - "cpu": [ - "mips64el" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, "engines": { - "node": ">=12" + "node": ">=10.10.0" } }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", - "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", - "cpu": [ - "ppc64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "engines": { - "node": ">=12" + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", - "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", - "cpu": [ - "riscv64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz", + "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==" + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, "engines": { - "node": ">=12" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", - "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + }, + "node_modules/@noble/hashes": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz", + "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, "engines": { - "node": ">=12" + "node": ">= 8" } }, - "node_modules/@esbuild/linux-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", - "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "engines": { - "node": ">=12" + "node": ">= 8" } }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", - "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "netbsd" - ], + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, "engines": { - "node": ">=12" + "node": ">= 8" } }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", - "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.9.1.tgz", + "integrity": "sha512-LtYcLNM+bhsaKAIGwVkh5IOWhaZhjTfNOkGzGqdHvhiCUVuJDalvDxEdSnhFzAn+g23wgsycmZk1vbnaibZwwA==", "cpu": [ - "x64" + "arm64" ], "optional": true, "os": [ - "openbsd" - ], - "engines": { - "node": ">=12" + "darwin" + ] + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + }, + "node_modules/@stacks/clarunit": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@stacks/clarunit/-/clarunit-0.0.1.tgz", + "integrity": "sha512-AKf14ycQJjyUWL6yfvXU+yMqvkCfUy2NarHbAmXx6tXfv/fyXueGkjTZTh8+0r20+XoxEvhJTnBfoAA74VLNtg==", + "dependencies": { + "@hirosystems/clarinet-sdk": "^1.2.0", + "@stacks/transactions": "^6.11.0", + "chokidar-cli": "^3.0.0", + "eslint": "^8.56.0", + "path": "^0.12.7", + "typescript": "^5.2.2", + "vite": "^4.4.9", + "vitest": "^1.1.0", + "vitest-environment-clarinet": "^1.0.0" + }, + "bin": { + "clarunit": "src/cli.ts" } }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", - "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "node_modules/@stacks/clarunit/node_modules/@esbuild/darwin-arm64": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", + "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", "cpu": [ - "x64" + "arm64" ], "optional": true, "os": [ - "sunos" + "darwin" ], "engines": { "node": ">=12" } }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", - "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "node_modules/@stacks/clarunit/node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz", + "integrity": "sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==", "cpu": [ "arm64" ], "optional": true, "os": [ - "win32" - ], + "darwin" + ] + }, + "node_modules/@stacks/clarunit/node_modules/@vitest/expect": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz", + "integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==", + "dependencies": { + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", + "chai": "^4.3.10" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@stacks/clarunit/node_modules/@vitest/runner": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz", + "integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==", + "dependencies": { + "@vitest/utils": "1.3.1", + "p-limit": "^5.0.0", + "pathe": "^1.1.1" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@stacks/clarunit/node_modules/@vitest/snapshot": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz", + "integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==", + "dependencies": { + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@stacks/clarunit/node_modules/@vitest/spy": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz", + "integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==", + "dependencies": { + "tinyspy": "^2.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@stacks/clarunit/node_modules/@vitest/utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz", + "integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==", + "dependencies": { + "diff-sequences": "^29.6.3", + "estree-walker": "^3.0.3", + "loupe": "^2.3.7", + "pretty-format": "^29.7.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@stacks/clarunit/node_modules/esbuild": { + "version": "0.19.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", + "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, "engines": { "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.19.12", + "@esbuild/android-arm": "0.19.12", + "@esbuild/android-arm64": "0.19.12", + "@esbuild/android-x64": "0.19.12", + "@esbuild/darwin-arm64": "0.19.12", + "@esbuild/darwin-x64": "0.19.12", + "@esbuild/freebsd-arm64": "0.19.12", + "@esbuild/freebsd-x64": "0.19.12", + "@esbuild/linux-arm": "0.19.12", + "@esbuild/linux-arm64": "0.19.12", + "@esbuild/linux-ia32": "0.19.12", + "@esbuild/linux-loong64": "0.19.12", + "@esbuild/linux-mips64el": "0.19.12", + "@esbuild/linux-ppc64": "0.19.12", + "@esbuild/linux-riscv64": "0.19.12", + "@esbuild/linux-s390x": "0.19.12", + "@esbuild/linux-x64": "0.19.12", + "@esbuild/netbsd-x64": "0.19.12", + "@esbuild/openbsd-x64": "0.19.12", + "@esbuild/sunos-x64": "0.19.12", + "@esbuild/win32-arm64": "0.19.12", + "@esbuild/win32-ia32": "0.19.12", + "@esbuild/win32-x64": "0.19.12" + } + }, + "node_modules/@stacks/clarunit/node_modules/local-pkg": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.5.0.tgz", + "integrity": "sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==", + "dependencies": { + "mlly": "^1.4.2", + "pkg-types": "^1.0.3" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@stacks/clarunit/node_modules/p-limit": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-5.0.0.tgz", + "integrity": "sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@stacks/clarunit/node_modules/rollup": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.12.0.tgz", + "integrity": "sha512-wz66wn4t1OHIJw3+XU7mJJQV/2NAfw5OAk6G6Hoo3zcvz/XOfQ52Vgi+AN4Uxoxi0KBBwk2g8zPrTDA4btSB/Q==", + "dependencies": { + "@types/estree": "1.0.5" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.12.0", + "@rollup/rollup-android-arm64": "4.12.0", + "@rollup/rollup-darwin-arm64": "4.12.0", + "@rollup/rollup-darwin-x64": "4.12.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.12.0", + "@rollup/rollup-linux-arm64-gnu": "4.12.0", + "@rollup/rollup-linux-arm64-musl": "4.12.0", + "@rollup/rollup-linux-riscv64-gnu": "4.12.0", + "@rollup/rollup-linux-x64-gnu": "4.12.0", + "@rollup/rollup-linux-x64-musl": "4.12.0", + "@rollup/rollup-win32-arm64-msvc": "4.12.0", + "@rollup/rollup-win32-ia32-msvc": "4.12.0", + "@rollup/rollup-win32-x64-msvc": "4.12.0", + "fsevents": "~2.3.2" } }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", - "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "node_modules/@stacks/clarunit/node_modules/strip-literal": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-2.0.0.tgz", + "integrity": "sha512-f9vHgsCWBq2ugHAkGMiiYY+AYG0D/cbloKKg0nhaaaSNsujdGIpVXCNsrJpCKr5M0f4aI31mr13UjY6GAuXCKA==", + "dependencies": { + "js-tokens": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "node_modules/@esbuild/win32-x64": { - "version": "0.18.20", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", - "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], + "node_modules/@stacks/clarunit/node_modules/tinypool": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz", + "integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==", "engines": { - "node": ">=12" + "node": ">=14.0.0" } }, - "node_modules/@hirosystems/clarinet-sdk": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@hirosystems/clarinet-sdk/-/clarinet-sdk-1.1.0.tgz", - "integrity": "sha512-O4iP+eqc2jtbCJcndC22l12ODIi8GxwUcWhWaltvnPBn+PXqCLxDqNU78C6iDCfPp/Ro2fcJy9z27KNqnu+A9g==", + "node_modules/@stacks/clarunit/node_modules/vite-node": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz", + "integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==", "dependencies": { - "@hirosystems/clarinet-sdk-wasm": "^1.1.0", - "@stacks/transactions": "^6.9.0", - "kolorist": "^1.8.0", - "prompts": "^2.4.2", - "vitest": "^0.34.5", - "yargs": "^17.7.2" + "cac": "^6.7.14", + "debug": "^4.3.4", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "vite": "^5.0.0" }, "bin": { - "clarinet-sdk": "dist/cjs/bin/index.js" + "vite-node": "vite-node.mjs" }, "engines": { - "node": ">=18.0.0" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/@hirosystems/clarinet-sdk-wasm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@hirosystems/clarinet-sdk-wasm/-/clarinet-sdk-wasm-1.1.0.tgz", - "integrity": "sha512-hGf2Ib6qYVnhV2+idW1GuOsh1Fom4fhp+QYjxHmfGQvx9ptSb037/4YVlep+jbO4hKXHHF2uQJgKMRPwVrtN2g==" - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "node_modules/@stacks/clarunit/node_modules/vite-node/node_modules/vite": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", + "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", "dependencies": { - "@sinclair/typebox": "^0.27.8" + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" - }, - "node_modules/@noble/hashes": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.5.tgz", - "integrity": "sha512-LTMZiiLc+V4v1Yi16TD6aX2gmtKszNye0pQgbaLqkvhIqP7nVsSaJsWloGQjJfJ8offaoP5GtX3yY5swbcJxxQ==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "node_modules/@stacks/clarunit/node_modules/vitest": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz", + "integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==", + "dependencies": { + "@vitest/expect": "1.3.1", + "@vitest/runner": "1.3.1", + "@vitest/snapshot": "1.3.1", + "@vitest/spy": "1.3.1", + "@vitest/utils": "1.3.1", + "acorn-walk": "^8.3.2", + "chai": "^4.3.10", + "debug": "^4.3.4", + "execa": "^8.0.1", + "local-pkg": "^0.5.0", + "magic-string": "^0.30.5", + "pathe": "^1.1.1", + "picocolors": "^1.0.0", + "std-env": "^3.5.0", + "strip-literal": "^2.0.0", + "tinybench": "^2.5.1", + "tinypool": "^0.8.2", + "vite": "^5.0.0", + "vite-node": "1.3.1", + "why-is-node-running": "^2.2.2" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "1.3.1", + "@vitest/ui": "1.3.1", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true } - ] + } }, - "node_modules/@noble/secp256k1": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", - "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" + "node_modules/@stacks/clarunit/node_modules/vitest/node_modules/vite": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.1.4.tgz", + "integrity": "sha512-n+MPqzq+d9nMVTKyewqw6kSt+R3CkvF9QAKY8obiQn8g1fwTscKxyfaYnC632HtBXAQGc1Yjomphwn1dtwGAHg==", + "dependencies": { + "esbuild": "^0.19.3", + "postcss": "^8.4.35", + "rollup": "^4.2.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true } - ] - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==" + } }, "node_modules/@stacks/common": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@stacks/common/-/common-6.8.1.tgz", - "integrity": "sha512-ewL9GLZNQYa5a/3K4xSHlHIgHkD4rwWW/QEaPId8zQIaL+1O9qCaF4LX9orNQeOmEk8kvG0x2xGV54fXKCZeWQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@stacks/common/-/common-6.10.0.tgz", + "integrity": "sha512-6x5Z7AKd9/kj3+DYE9xIDIkFLHihBH614i2wqrZIjN02WxVo063hWSjIlUxlx8P4gl6olVzlOy5LzhLJD9OP0A==", "dependencies": { "@types/bn.js": "^5.1.0", "@types/node": "^18.0.4" } }, "node_modules/@stacks/network": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/@stacks/network/-/network-6.8.1.tgz", - "integrity": "sha512-n8M25pPbLqpSBctabtsLOTBlmPvm9EPQpTI//x7HLdt5lEjDXxauEQt0XGSvDUZwecrmztqt9xNxlciiGApRBw==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/@stacks/network/-/network-6.11.3.tgz", + "integrity": "sha512-c4ClCU/QUwuu8NbHtDKPJNa0M5YxauLN3vYaR0+S4awbhVIKFQSxirm9Q9ckV1WBh7FtD6u2S0x+tDQGAODjNg==", "dependencies": { - "@stacks/common": "^6.8.1", + "@stacks/common": "^6.10.0", "cross-fetch": "^3.1.5" } }, "node_modules/@stacks/transactions": { - "version": "6.9.0", - "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.9.0.tgz", - "integrity": "sha512-hSs9+0Ew++GwMZMgPObOx0iVCQRxkiCqI+DHdPEikAmg2utpyLh2/txHOjfSIkQHvcBfJJ6O5KphmxDP4gUqiA==", + "version": "6.12.0", + "resolved": "https://registry.npmjs.org/@stacks/transactions/-/transactions-6.12.0.tgz", + "integrity": "sha512-gRP3SfTaAIoTdjMvOiLrMZb/senqB8JQlT5Y4C3/CiHhiprYwTx7TbOCSa7WsNOU99H4aNfHvatmymuggXQVkA==", "dependencies": { "@noble/hashes": "1.1.5", "@noble/secp256k1": "1.7.1", - "@stacks/common": "^6.8.1", - "@stacks/network": "^6.8.1", + "@stacks/common": "^6.10.0", + "@stacks/network": "^6.11.3", "c32check": "^2.0.0", "lodash.clonedeep": "^4.5.0" } }, "node_modules/@types/bn.js": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.4.tgz", - "integrity": "sha512-ZtBd9L8hVtoBpPMSWfbwjC4dhQtJdlPS+e1A0Rydb7vg7bDcUwiRklPx24sMYtXcmAMST/k0Wze7JLbNU/5SkA==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", "dependencies": { "@types/node": "*" } @@ -467,6 +1027,11 @@ "@types/chai": "*" } }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==" + }, "node_modules/@types/node": { "version": "18.18.8", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.18.8.tgz", @@ -475,6 +1040,11 @@ "undici-types": "~5.26.4" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==" + }, "node_modules/@vitest/expect": { "version": "0.34.6", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.34.6.tgz", @@ -549,14 +1119,37 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.0.tgz", - "integrity": "sha512-FS7hV565M5l1R08MXqo8odwMTB02C2UqzB17RVgu9EyuYFBqJZ3/ZY97sQD5FewVu1UyDFc1yztUDrAwT0EypA==", + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", "engines": { "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -588,6 +1181,11 @@ "node": ">= 8" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -596,6 +1194,11 @@ "node": "*" } }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, "node_modules/base-x": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/base-x/-/base-x-4.0.0.tgz", @@ -609,6 +1212,15 @@ "node": ">=8" } }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/braces": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", @@ -640,6 +1252,14 @@ "node": ">=8" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -653,18 +1273,63 @@ "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.0.8" + "assertion-error": "^1.1.0", + "check-error": "^1.0.3", + "deep-eql": "^4.1.3", + "get-func-name": "^2.0.2", + "loupe": "^2.3.6", + "pathval": "^1.1.1", + "type-detect": "^4.0.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" }, "engines": { - "node": ">=4" + "node": ">=7.0.0" } }, + "node_modules/chalk/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", @@ -855,6 +1520,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, "node_modules/cross-fetch": { "version": "3.1.8", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.1.8.tgz", @@ -863,6 +1533,19 @@ "node-fetch": "^2.6.12" } }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -898,6 +1581,11 @@ "node": ">=6" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==" + }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", @@ -906,6 +1594,17 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -955,6 +1654,323 @@ "node": ">=6" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/fast-check": { + "version": "3.15.1", + "resolved": "https://registry.npmjs.org/fast-check/-/fast-check-3.15.1.tgz", + "integrity": "sha512-GutOXZ+SCxGaFWfHe0Pbeq8PrkpGtPxA9/hdkI3s9YzqeMlrq5RdJ+QfYZ/S93jMX+tAyqgW0z5c9ppD+vkGUw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "dependencies": { + "pure-rand": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, "node_modules/fill-range": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", @@ -977,6 +1993,29 @@ "node": ">=6" } }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -995,28 +2034,138 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "engines": { + "node": "*" + } + }, + "node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/ignore": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "engines": { - "node": "*" + "node": ">=0.8.19" } }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" + "once": "^1.3.0", + "wrappy": "1" } }, + "node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -1063,11 +2212,74 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + }, + "node_modules/js-tokens": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-8.0.3.tgz", + "integrity": "sha512-UfJMcSJc+SEXEl9lH/VLHSZbThQyLpw1vLO1Lb+j4RWDvG3N2f7yj3PVQA3cmkTBNldJ9eFnM+xEXxHIXrYiJw==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==" + }, "node_modules/jsonc-parser": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", @@ -1081,6 +2293,18 @@ "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz", "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==" }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/local-pkg": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", @@ -1114,6 +2338,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==" + }, "node_modules/lodash.throttle": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", @@ -1138,6 +2367,33 @@ "node": ">=12" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==" + }, + "node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/mlly": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.4.2.tgz", @@ -1155,9 +2411,9 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", "funding": [ { "type": "github", @@ -1171,6 +2427,11 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -1198,6 +2459,69 @@ "node": ">=0.10.0" } }, + "node_modules/npm-run-path": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz", + "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", @@ -1245,6 +2569,26 @@ "node": ">=6" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path": { + "version": "0.12.7", + "resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz", + "integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==", + "dependencies": { + "process": "^0.11.1", + "util": "^0.10.3" + } + }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -1253,6 +2597,22 @@ "node": ">=4" } }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "engines": { + "node": ">=8" + } + }, "node_modules/pathe": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.1.tgz", @@ -1293,9 +2653,9 @@ } }, "node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "version": "8.4.35", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", + "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", "funding": [ { "type": "opencollective", @@ -1311,7 +2671,7 @@ } ], "dependencies": { - "nanoid": "^3.3.6", + "nanoid": "^3.3.7", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" }, @@ -1319,6 +2679,14 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -1332,6 +2700,14 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -1344,6 +2720,48 @@ "node": ">= 6" } }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", + "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/react-is": { "version": "18.2.0", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", @@ -1373,6 +2791,37 @@ "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==" }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/rollup": { "version": "3.29.4", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.29.4.tgz", @@ -1388,16 +2837,68 @@ "fsevents": "~2.3.2" } }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/set-blocking": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "engines": { + "node": ">=8" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==" }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -1417,9 +2918,9 @@ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==" }, "node_modules/std-env": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.4.3.tgz", - "integrity": "sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==" + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==" }, "node_modules/string-width": { "version": "4.2.3", @@ -1445,6 +2946,28 @@ "node": ">=8" } }, + "node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-literal": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", @@ -1456,6 +2979,22 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==" + }, "node_modules/tinybench": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.5.1.tgz", @@ -1493,6 +3032,17 @@ "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", @@ -1501,6 +3051,17 @@ "node": ">=4" } }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/typescript": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", @@ -1523,6 +3084,22 @@ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz", + "integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==", + "dependencies": { + "inherits": "2.0.3" + } + }, "node_modules/vite": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.0.tgz", @@ -1698,6 +3275,20 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-module": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", @@ -1764,6 +3355,11 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/contrib/core-contract-tests/package.json b/contrib/core-contract-tests/package.json index 2f11d87369..774870d8f9 100644 --- a/contrib/core-contract-tests/package.json +++ b/contrib/core-contract-tests/package.json @@ -4,17 +4,20 @@ "description": "Run unit tests on this project.", "private": true, "scripts": { - "test": "vitest run -- --coverage" + "test": "vitest run -- --coverage", + "genhtml": "genhtml lcov.info --branch-coverage -o coverage/" }, "author": "", "license": "ISC", "dependencies": { "@hirosystems/clarinet-sdk": "^1.1.0", - "@stacks/transactions": "^6.9.0", + "@stacks/transactions": "^6.12.0", "chokidar-cli": "^3.0.0", + "@stacks/clarunit": "0.0.1", + "fast-check": "^3.15.1", "typescript": "^5.2.2", "vite": "^4.4.9", "vitest": "^0.34.4", "vitest-environment-clarinet": "^1.0.0" } -} +} \ No newline at end of file diff --git a/contrib/core-contract-tests/tests/bns_test.clar b/contrib/core-contract-tests/tests/bns_test.clar new file mode 100644 index 0000000000..8f0d125891 --- /dev/null +++ b/contrib/core-contract-tests/tests/bns_test.clar @@ -0,0 +1,9 @@ +(define-public (test-can-receive-name-none) + (begin + (asserts! + (is-eq (ok true) (contract-call? .bns can-receive-name tx-sender)) + (err "Should be able to receive a name") + ) + (ok true) + ) +) \ No newline at end of file diff --git a/contrib/core-contract-tests/tests/clarunit.test.ts b/contrib/core-contract-tests/tests/clarunit.test.ts new file mode 100644 index 0000000000..5e4fa0da43 --- /dev/null +++ b/contrib/core-contract-tests/tests/clarunit.test.ts @@ -0,0 +1,2 @@ +import { clarunit } from "@stacks/clarunit"; +clarunit(simnet); diff --git a/contrib/core-contract-tests/tests/pox-4/signers-voting.prop.test.ts b/contrib/core-contract-tests/tests/pox-4/signers-voting.prop.test.ts new file mode 100644 index 0000000000..0dc8ea2171 --- /dev/null +++ b/contrib/core-contract-tests/tests/pox-4/signers-voting.prop.test.ts @@ -0,0 +1,130 @@ +import fc from "fast-check"; +import { assert, expect, it } from "vitest"; +import { Cl, ClarityType, isClarityType } from "@stacks/transactions"; + +it("should return correct reward-cycle-to-burn-height", () => { + fc.assert( + fc.property( + fc.constantFrom(...simnet.getAccounts().values()), + fc.nat(), + (account: string, reward_cycle: number) => { + // Arrange + const { result: pox_4_info } = simnet.callReadOnlyFn( + "pox-4", + "get-pox-info", + [], + account, + ); + assert(isClarityType(pox_4_info, ClarityType.ResponseOk)); + assert(isClarityType(pox_4_info.value, ClarityType.Tuple)); + const first_burnchain_block_height = + pox_4_info.value.data["first-burnchain-block-height"]; + const reward_cycle_length = + pox_4_info.value.data["reward-cycle-length"]; + + // Act + const { result: actual } = simnet.callReadOnlyFn( + "signers-voting", + "reward-cycle-to-burn-height", + [Cl.uint(reward_cycle)], + account, + ); + + // Assert + assert(isClarityType(reward_cycle_length, ClarityType.UInt)); + assert(isClarityType(first_burnchain_block_height, ClarityType.UInt)); + const expected = (reward_cycle * Number(reward_cycle_length.value)) + + Number(first_burnchain_block_height.value); + expect(actual).toBeUint(expected); + }, + ), + { numRuns: 250 }, + ); +}); + +it("should return correct burn-height-to-reward-cycle", () => { + fc.assert( + fc.property( + fc.constantFrom(...simnet.getAccounts().values()), + fc.nat(), + (account: string, height: number) => { + // Arrange + const { result: pox_4_info } = simnet.callReadOnlyFn( + "pox-4", + "get-pox-info", + [], + account, + ); + assert(isClarityType(pox_4_info, ClarityType.ResponseOk)); + assert(isClarityType(pox_4_info.value, ClarityType.Tuple)); + const first_burnchain_block_height = + pox_4_info.value.data["first-burnchain-block-height"]; + const reward_cycle_length = + pox_4_info.value.data["reward-cycle-length"]; + + // Act + const { result: actual } = simnet.callReadOnlyFn( + "signers-voting", + "burn-height-to-reward-cycle", + [Cl.uint(height)], + account, + ); + + // Assert + assert(isClarityType(first_burnchain_block_height, ClarityType.UInt)); + assert(isClarityType(reward_cycle_length, ClarityType.UInt)); + const expected = Math.floor( + (height - Number(first_burnchain_block_height.value)) / + Number(reward_cycle_length.value), + ); + expect(actual).toBeUint(expected); + }, + ), + { numRuns: 250 }, + ); +}); + +it("should return correct is-in-prepare-phase", () => { + fc.assert( + fc.property( + fc.constantFrom(...simnet.getAccounts().values()), + fc.nat(), + (account: string, height: number) => { + // Arrange + const { result: pox_4_info } = simnet.callReadOnlyFn( + "pox-4", + "get-pox-info", + [], + account, + ); + assert(isClarityType(pox_4_info, ClarityType.ResponseOk)); + assert(isClarityType(pox_4_info.value, ClarityType.Tuple)); + const first_burnchain_block_height = + pox_4_info.value.data["first-burnchain-block-height"]; + const prepare_cycle_length = + pox_4_info.value.data["prepare-cycle-length"]; + const reward_cycle_length = + pox_4_info.value.data["reward-cycle-length"]; + + // Act + const { result: actual } = simnet.callReadOnlyFn( + "signers-voting", + "is-in-prepare-phase", + [Cl.uint(height)], + account, + ); + + // Assert + assert(isClarityType(first_burnchain_block_height, ClarityType.UInt)); + assert(isClarityType(prepare_cycle_length, ClarityType.UInt)); + assert(isClarityType(reward_cycle_length, ClarityType.UInt)); + const expected = ((height - Number(first_burnchain_block_height.value) + + Number(prepare_cycle_length.value)) % + Number(reward_cycle_length.value)) < + Number(prepare_cycle_length.value); + expect(actual).toBeBool(expected); + }, + ), + { numRuns: 250 }, + ); +}); diff --git a/contrib/core-contract-tests/tests/pox_4_test.clar b/contrib/core-contract-tests/tests/pox_4_test.clar new file mode 100644 index 0000000000..a158d9033e --- /dev/null +++ b/contrib/core-contract-tests/tests/pox_4_test.clar @@ -0,0 +1,135 @@ +(define-public (test-burn-height-to-reward-cycle) + (begin + (asserts! (is-eq u2 (contract-call? .pox-4 burn-height-to-reward-cycle u2100)) (err "Burn height 2100 should have been reward cycle 2")) + (asserts! (is-eq u3 (contract-call? .pox-4 burn-height-to-reward-cycle u3150)) (err "Burn height 3150 should have been reward cycle 2")) + (ok true) + ) +) + +(define-public (test-reward-cycle-to-burn-height) + (begin + (asserts! (is-eq u10500 (contract-call? .pox-4 reward-cycle-to-burn-height u10)) (err "Cycle 10 height should have been at burn height 10500")) + (asserts! (is-eq u18900 (contract-call? .pox-4 reward-cycle-to-burn-height u18)) (err "Cycle 18 height should have been at burn height 18900")) + (ok true) + ) +) + +(define-public (test-get-stacker-info-none) + (begin + (asserts! (is-none (contract-call? .pox-4 get-stacker-info tx-sender)) (err "By default, tx-sender should not have stacker info")) + (ok true) + ) +) + + +(define-private (check-pox-addr-version-iter (input (buff 1))) + (contract-call? .pox-4 check-pox-addr-version input) +) + +(define-public (test-check-pox-addr-version) + (begin + (asserts! (is-eq (map check-pox-addr-version-iter byte-list) + (list + true true true true true true true false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false + )) + (err "Only the first 6 versions should be valid") + ) + (ok true) + ) +) + +(define-private (check-pox-addr-hashbytes-iter (test-length uint) (version (buff 1))) + (contract-call? .pox-4 check-pox-addr-hashbytes version (unwrap-panic (as-max-len? (unwrap-panic (slice? byte-list u0 test-length)) u32))) +) + +(define-public (test-invalid-pox-addr-hashbytes-length) + (let ( + (test-lengths (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11 u12 u13 u14 u15 u16 u17 u18 u19 u20 u21 u22 u23 u24 u25 u26 u27 u28 u29 u30 u31 u32)) + (length-20-valid (list + false false false false false false false false false false false false false false false false + false false false false true false false false false false false false false false false false false + )) + (length-32-valid (list + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false true + )) + (length-all-invalid (list + false false false false false false false false false false false false false false false false + false false false false false false false false false false false false false false false false false + )) + ) + (asserts! (is-eq (map check-pox-addr-hashbytes-iter test-lengths (buff-repeat 0x00 (len test-lengths))) length-20-valid) + (err "Only length 20 should be valid for version 0x00") + ) + (asserts! (is-eq (map check-pox-addr-hashbytes-iter test-lengths (buff-repeat 0x01 (len test-lengths))) length-20-valid) + (err "Only length 20 should be valid for version 0x01") + ) + (asserts! (is-eq (map check-pox-addr-hashbytes-iter test-lengths (buff-repeat 0x02 (len test-lengths))) length-20-valid) + (err "Only length 20 should be valid for version 0x02") + ) + (asserts! (is-eq (map check-pox-addr-hashbytes-iter test-lengths (buff-repeat 0x03 (len test-lengths))) length-20-valid) + (err "Only length 20 should be valid for version 0x03") + ) + (asserts! (is-eq (map check-pox-addr-hashbytes-iter test-lengths (buff-repeat 0x04 (len test-lengths))) length-20-valid) + (err "Only length 20 should be valid for version 0x04") + ) + (asserts! (is-eq (map check-pox-addr-hashbytes-iter test-lengths (buff-repeat 0x05 (len test-lengths))) length-32-valid) + (err "Only length 32 should be valid for version 0x05") + ) + (asserts! (is-eq (map check-pox-addr-hashbytes-iter test-lengths (buff-repeat 0x06 (len test-lengths))) length-32-valid) + (err "Only length 32 should be valid for version 0x06") + ) + (asserts! (is-eq (map check-pox-addr-hashbytes-iter test-lengths (buff-repeat 0x07 (len test-lengths))) length-all-invalid) + (err "No length should be valid for version 0x07") + ) + (ok true) + ) +) + +(define-private (check-pox-lock-period-iter (period uint)) + (contract-call? .pox-4 check-pox-lock-period period) +) + +(define-public (test-check-pox-lock-period) + (let ((actual (map check-pox-lock-period-iter (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11 u12 u13)))) + (asserts! (is-eq + actual + (list false true true true true true true true true true true true true false)) + (err {err: "Expected only lock periods 1 to 12 to be valid", actual: actual}) + ) + (ok true) + ) +) + +(define-public (test-get-total-ustx-stacked) + (begin + (asserts! (is-eq (contract-call? .pox-4 get-total-ustx-stacked u1) u0) (err "Total ustx stacked should be 0")) + (ok true) + ) +) + + +(define-private (repeat-iter (a (buff 1)) (repeat {i: (buff 1), o: (buff 33)})) + {i: (get i repeat), o: (unwrap-panic (as-max-len? (concat (get i repeat) (get o repeat)) u33))} +) + +(define-read-only (buff-repeat (repeat (buff 1)) (times uint)) + (get o (fold repeat-iter (unwrap-panic (slice? byte-list u0 times)) {i: repeat, o: 0x})) +) + +(define-constant byte-list 0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff) \ No newline at end of file diff --git a/stacks-common/src/address/c32.rs b/stacks-common/src/address/c32.rs index 67a0504b33..a8c26632f8 100644 --- a/stacks-common/src/address/c32.rs +++ b/stacks-common/src/address/c32.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; - use sha2::{Digest, Sha256}; use super::Error; diff --git a/stacks-common/src/address/mod.rs b/stacks-common/src/address/mod.rs index b4bcb936c9..381456f661 100644 --- a/stacks-common/src/address/mod.rs +++ b/stacks-common/src/address/mod.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; use std::{error, fmt}; use sha2::{Digest, Sha256}; diff --git a/stacks-common/src/deps_common/bitcoin/blockdata/constants.rs b/stacks-common/src/deps_common/bitcoin/blockdata/constants.rs index 4a9cfddef7..7d3c7d1b4f 100644 --- a/stacks-common/src/deps_common/bitcoin/blockdata/constants.rs +++ b/stacks-common/src/deps_common/bitcoin/blockdata/constants.rs @@ -19,8 +19,6 @@ //! single transaction //! -use std::default::Default; - use crate::deps_common::bitcoin::blockdata::block::{Block, BlockHeader}; use crate::deps_common::bitcoin::blockdata::transaction::{OutPoint, Transaction, TxIn, TxOut}; use crate::deps_common::bitcoin::blockdata::{opcodes, script}; @@ -139,7 +137,6 @@ pub fn genesis_block(network: Network) -> Block { #[cfg(test)] mod test { - use std::default::Default; use crate::deps_common::bitcoin::blockdata::constants::{ bitcoin_genesis_tx, genesis_block, COIN_VALUE, MAX_SEQUENCE, diff --git a/stacks-common/src/deps_common/bitcoin/blockdata/script.rs b/stacks-common/src/deps_common/bitcoin/blockdata/script.rs index f055316f25..8b08ab998a 100644 --- a/stacks-common/src/deps_common/bitcoin/blockdata/script.rs +++ b/stacks-common/src/deps_common/bitcoin/blockdata/script.rs @@ -24,7 +24,6 @@ //! This module provides the structures and functions needed to support scripts. //! -use std::default::Default; use std::mem::size_of; use std::{error, fmt}; diff --git a/stacks-common/src/deps_common/bitcoin/blockdata/transaction.rs b/stacks-common/src/deps_common/bitcoin/blockdata/transaction.rs index f92a11cd1b..1ece07c511 100644 --- a/stacks-common/src/deps_common/bitcoin/blockdata/transaction.rs +++ b/stacks-common/src/deps_common/bitcoin/blockdata/transaction.rs @@ -23,7 +23,6 @@ //! This module provides the structures and functions needed to support transactions. //! -use std::default::Default; use std::fmt; use std::io::Write; diff --git a/stacks-common/src/deps_common/bitcoin/network/message_blockdata.rs b/stacks-common/src/deps_common/bitcoin/network/message_blockdata.rs index 099acca559..419b405a17 100644 --- a/stacks-common/src/deps_common/bitcoin/network/message_blockdata.rs +++ b/stacks-common/src/deps_common/bitcoin/network/message_blockdata.rs @@ -135,7 +135,6 @@ impl ConsensusDecodable for Inventory { #[cfg(test)] mod tests { - use std::default::Default; use super::{GetBlocksMessage, GetHeadersMessage}; use crate::deps_common::bitcoin::network::serialize::{deserialize, serialize}; diff --git a/stacks-common/src/deps_common/bitcoin/util/hash.rs b/stacks-common/src/deps_common/bitcoin/util/hash.rs index 364b5f609b..3e9186bd92 100644 --- a/stacks-common/src/deps_common/bitcoin/util/hash.rs +++ b/stacks-common/src/deps_common/bitcoin/util/hash.rs @@ -17,7 +17,6 @@ use std::char::from_digit; use std::cmp::min; -use std::default::Default; use std::io::{Cursor, Write}; use std::{error, fmt, mem}; diff --git a/stacks-common/src/types/mod.rs b/stacks-common/src/types/mod.rs index 998edda48e..cf5603dba9 100644 --- a/stacks-common/src/types/mod.rs +++ b/stacks-common/src/types/mod.rs @@ -1,5 +1,4 @@ use std::cmp::Ordering; -use std::convert::TryFrom; use std::fmt; use crate::address::c32::{c32_address, c32_address_decode}; diff --git a/stacks-common/src/util/hash.rs b/stacks-common/src/util/hash.rs index 634cbf485b..a5e4341b60 100644 --- a/stacks-common/src/util/hash.rs +++ b/stacks-common/src/util/hash.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::char::from_digit; -use std::convert::TryInto; use std::fmt::Write; use std::{fmt, mem}; diff --git a/stacks-common/src/util/vrf.rs b/stacks-common/src/util/vrf.rs index 410c4a07e2..ddfdedfaa8 100644 --- a/stacks-common/src/util/vrf.rs +++ b/stacks-common/src/util/vrf.rs @@ -17,8 +17,7 @@ #![allow(non_camel_case_types)] #![allow(non_snake_case)] -use std::clone::Clone; -use std::cmp::{Eq, Ord, Ordering, PartialEq}; +use std::cmp::Ordering; use std::fmt::Debug; use std::hash::{Hash, Hasher}; /// This codebase is based on routines defined in the IETF draft for verifiable random functions diff --git a/stacks-signer/Cargo.toml b/stacks-signer/Cargo.toml index 8944c10342..99b02761c2 100644 --- a/stacks-signer/Cargo.toml +++ b/stacks-signer/Cargo.toml @@ -44,7 +44,6 @@ wsts = { workspace = true } rand = { workspace = true } [dev-dependencies] -serial_test = "3.0.0" clarity = { path = "../clarity", features = ["testing"] } [dependencies.serde_json] diff --git a/stacks-signer/src/client/mod.rs b/stacks-signer/src/client/mod.rs index 959ce56dbf..63dc0e9a1f 100644 --- a/stacks-signer/src/client/mod.rs +++ b/stacks-signer/src/client/mod.rs @@ -99,9 +99,6 @@ pub enum ClientError { /// No reward set exists for the given reward cycle #[error("No reward set exists for reward cycle {0}")] NoRewardSet(u64), - /// Reward set contained corrupted data - #[error("{0}")] - CorruptedRewardSet(String), /// Stacks node does not support a feature we need #[error("Stacks node does not support a required feature: {0}")] UnsupportedStacksFeature(String), @@ -156,7 +153,8 @@ pub(crate) mod tests { use wsts::state_machine::PublicKeys; use super::*; - use crate::config::{GlobalConfig, RegisteredSignersInfo, SignerConfig}; + use crate::config::{GlobalConfig, ParsedSignerEntries, SignerConfig}; + use crate::signer::SignerSlotID; pub struct MockServerClient { pub server: TcpListener, @@ -207,15 +205,6 @@ pub(crate) mod tests { TcpListener::bind(config.node_host).unwrap() } - /// Create a mock server on the same port as the config and write a response to it - pub fn mock_server_from_config_and_write_response( - config: &GlobalConfig, - bytes: &[u8], - ) -> [u8; 1024] { - let mock_server = mock_server_from_config(config); - write_response(mock_server, bytes) - } - /// Write a response to the mock server and return the request bytes pub fn write_response(mock_server: TcpListener, bytes: &[u8]) -> [u8; 1024] { debug!("Writing a response..."); @@ -434,7 +423,7 @@ pub(crate) mod tests { let mut start_key_id = 1u32; let mut end_key_id = start_key_id; let mut signer_public_keys = HashMap::new(); - let mut signer_slot_ids = HashMap::new(); + let mut signer_slot_ids = vec![]; let ecdsa_private_key = config.ecdsa_private_key; let ecdsa_public_key = ecdsa::PublicKey::new(&ecdsa_private_key).expect("Failed to create ecdsa public key"); @@ -468,7 +457,7 @@ pub(crate) mod tests { &StacksPublicKey::from_slice(ecdsa_public_key.to_bytes().as_slice()) .expect("Failed to create stacks public key"), ); - signer_slot_ids.insert(address, signer_id); // Note in a real world situation, these would not always match + signer_slot_ids.push(SignerSlotID(signer_id)); signer_ids.insert(address, signer_id); continue; @@ -495,23 +484,23 @@ pub(crate) mod tests { &StacksPublicKey::from_slice(public_key.to_bytes().as_slice()) .expect("Failed to create stacks public key"), ); - signer_slot_ids.insert(address, signer_id); // Note in a real world situation, these would not always match + signer_slot_ids.push(SignerSlotID(signer_id)); signer_ids.insert(address, signer_id); start_key_id = end_key_id; } SignerConfig { reward_cycle, signer_id: 0, - signer_slot_id: 0, + signer_slot_id: SignerSlotID(rand::thread_rng().gen_range(0..num_signers)), // Give a random signer slot id between 0 and num_signers key_ids: signer_key_ids.get(&0).cloned().unwrap_or_default(), - registered_signers: RegisteredSignersInfo { - signer_slot_ids, + signer_entries: ParsedSignerEntries { public_keys, coordinator_key_ids, signer_key_ids, signer_ids, signer_public_keys, }, + signer_slot_ids, ecdsa_private_key: config.ecdsa_private_key, stacks_private_key: config.stacks_private_key, node_host: config.node_host, diff --git a/stacks-signer/src/client/stackerdb.rs b/stacks-signer/src/client/stackerdb.rs index affd43ab2a..1e9e2e30f6 100644 --- a/stacks-signer/src/client/stackerdb.rs +++ b/stacks-signer/src/client/stackerdb.rs @@ -33,6 +33,7 @@ use stacks_common::{debug, warn}; use super::ClientError; use crate::client::retry_with_exponential_backoff; use crate::config::SignerConfig; +use crate::signer::SignerSlotID; /// The StackerDB client for communicating with the .signers contract pub struct StackerDB { @@ -42,9 +43,9 @@ pub struct StackerDB { /// The private key used in all stacks node communications stacks_private_key: StacksPrivateKey, /// A map of a message ID to last chunk version for each session - slot_versions: HashMap>, + slot_versions: HashMap>, /// The signer slot ID -- the index into the signer list for this signer daemon's signing key. - signer_slot_id: u32, + signer_slot_id: SignerSlotID, /// The reward cycle of the connecting signer reward_cycle: u64, /// The stacker-db transaction msg session for the NEXT reward cycle @@ -69,7 +70,7 @@ impl StackerDB { stacks_private_key: StacksPrivateKey, is_mainnet: bool, reward_cycle: u64, - signer_slot_id: u32, + signer_slot_id: SignerSlotID, ) -> Self { let mut signers_message_stackerdb_sessions = HashMap::new(); let stackerdb_issuer = boot_code_addr(is_mainnet); @@ -134,7 +135,7 @@ impl StackerDB { 1 }; - let mut chunk = StackerDBChunkData::new(slot_id, slot_version, message_bytes.clone()); + let mut chunk = StackerDBChunkData::new(slot_id.0, slot_version, message_bytes.clone()); chunk.sign(&self.stacks_private_key)?; let Some(session) = self.signers_message_stackerdb_sessions.get_mut(&msg_id) else { @@ -184,11 +185,11 @@ impl StackerDB { /// Get the transactions from stackerdb for the signers fn get_transactions( transactions_session: &mut StackerDBSession, - signer_ids: &[u32], + signer_ids: &[SignerSlotID], ) -> Result, ClientError> { let send_request = || { transactions_session - .get_latest_chunks(signer_ids) + .get_latest_chunks(&signer_ids.iter().map(|id| id.0).collect::>()) .map_err(backoff::Error::transient) }; let chunk_ack = retry_with_exponential_backoff(send_request)?; @@ -225,25 +226,23 @@ impl StackerDB { Ok(transactions) } - /// Get the latest signer transactions from signer ids for the current reward cycle + /// Get this signer's latest transactions from stackerdb pub fn get_current_transactions_with_retry( &mut self, - signer_id: u32, ) -> Result, ClientError> { - debug!("Signer #{signer_id}: Getting latest transactions from stacker db",); let Some(transactions_session) = self .signers_message_stackerdb_sessions .get_mut(&TRANSACTIONS_MSG_ID) else { return Err(ClientError::NotConnected); }; - Self::get_transactions(transactions_session, &[signer_id]) + Self::get_transactions(transactions_session, &[self.signer_slot_id]) } /// Get the latest signer transactions from signer ids for the next reward cycle pub fn get_next_transactions_with_retry( &mut self, - signer_ids: &[u32], + signer_ids: &[SignerSlotID], ) -> Result, ClientError> { debug!("Getting latest chunks from stackerdb for the following signers: {signer_ids:?}",); Self::get_transactions(&mut self.next_transaction_session, signer_ids) @@ -255,7 +254,7 @@ impl StackerDB { } /// Retrieve the signer slot ID - pub fn get_signer_slot_id(&mut self) -> u32 { + pub fn get_signer_slot_id(&mut self) -> SignerSlotID { self.signer_slot_id } } @@ -270,14 +269,12 @@ mod tests { TransactionSmartContract, TransactionVersion, }; use blockstack_lib::util_lib::strings::StacksString; - use serial_test::serial; use super::*; use crate::client::tests::{generate_signer_config, mock_server_from_config, write_response}; use crate::config::GlobalConfig; #[test] - #[serial] fn get_signer_transactions_with_retry_should_succeed() { let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); let signer_config = generate_signer_config(&config, 5, 20); @@ -302,8 +299,8 @@ mod tests { let signer_message = SignerMessage::Transactions(vec![tx.clone()]); let message = signer_message.serialize_to_vec(); - let signer_ids = vec![0, 1]; - let h = spawn(move || stackerdb.get_next_transactions_with_retry(&signer_ids)); + let signer_slot_ids = vec![SignerSlotID(0), SignerSlotID(1)]; + let h = spawn(move || stackerdb.get_next_transactions_with_retry(&signer_slot_ids)); let mut response_bytes = b"HTTP/1.1 200 OK\n\n".to_vec(); response_bytes.extend(message); let mock_server = mock_server_from_config(&config); @@ -321,9 +318,8 @@ mod tests { } #[test] - #[serial] fn send_signer_message_with_retry_should_succeed() { - let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); + let config = GlobalConfig::load_from_file("./src/tests/conf/signer-1.toml").unwrap(); let signer_config = generate_signer_config(&config, 5, 20); let mut stackerdb = StackerDB::from(&signer_config); diff --git a/stacks-signer/src/client/stacks_client.rs b/stacks-signer/src/client/stacks_client.rs index caa5c7018a..054e4fb374 100644 --- a/stacks-signer/src/client/stacks_client.rs +++ b/stacks-signer/src/client/stacks_client.rs @@ -17,7 +17,9 @@ use std::net::SocketAddr; use blockstack_lib::burnchains::Txid; use blockstack_lib::chainstate::nakamoto::NakamotoBlock; -use blockstack_lib::chainstate::stacks::boot::{RewardSet, SIGNERS_NAME, SIGNERS_VOTING_NAME}; +use blockstack_lib::chainstate::stacks::boot::{ + NakamotoSignerEntry, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME, +}; use blockstack_lib::chainstate::stacks::{ StacksTransaction, StacksTransactionSigner, TransactionAnchorMode, TransactionAuth, TransactionContractCall, TransactionPayload, TransactionPostConditionMode, @@ -32,23 +34,17 @@ use blockstack_lib::net::api::postblock_proposal::NakamotoBlockProposal; use blockstack_lib::util_lib::boot::{boot_code_addr, boot_code_id}; use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier}; use clarity::vm::{ClarityName, ContractName, Value as ClarityValue}; -use hashbrown::{HashMap, HashSet}; use serde_json::json; -use slog::{slog_debug, slog_warn}; +use slog::slog_debug; use stacks_common::codec::StacksMessageCodec; use stacks_common::consts::{CHAIN_ID_MAINNET, CHAIN_ID_TESTNET}; +use stacks_common::debug; use stacks_common::types::chainstate::{StacksAddress, StacksPrivateKey, StacksPublicKey}; use stacks_common::types::StacksEpochId; -use stacks_common::{debug, warn}; -use wsts::curve::ecdsa; use wsts::curve::point::{Compressed, Point}; -use wsts::state_machine::PublicKeys; use crate::client::{retry_with_exponential_backoff, ClientError}; -use crate::config::{GlobalConfig, RegisteredSignersInfo}; - -/// The name of the function for casting a DKG result to signer vote contract -pub const VOTE_FUNCTION_NAME: &str = "vote-for-aggregate-public-key"; +use crate::config::GlobalConfig; /// The Stacks signer client used to communicate with the stacks node #[derive(Clone, Debug)] @@ -297,8 +293,11 @@ impl StacksClient { Ok(round) } - /// Get the reward set from the stacks node for the given reward cycle - pub fn get_reward_set(&self, reward_cycle: u64) -> Result { + /// Get the reward set signers from the stacks node for the given reward cycle + pub fn get_reward_set_signers( + &self, + reward_cycle: u64, + ) -> Result>, ClientError> { debug!("Getting reward set for reward cycle {reward_cycle}..."); let send_request = || { self.stacks_node_client @@ -311,104 +310,7 @@ impl StacksClient { return Err(ClientError::RequestFailure(response.status())); } let stackers_response = response.json::()?; - Ok(stackers_response.stacker_set) - } - - /// Get the registered signers for a specific reward cycle - /// Returns None if no signers are registered or its not Nakamoto cycle - pub fn get_registered_signers_info( - &self, - reward_cycle: u64, - ) -> Result, ClientError> { - debug!("Getting registered signers for reward cycle {reward_cycle}..."); - let reward_set = self.get_reward_set(reward_cycle)?; - let Some(reward_set_signers) = reward_set.signers else { - warn!("No reward set signers found for reward cycle {reward_cycle}."); - return Ok(None); - }; - if reward_set_signers.is_empty() { - warn!("No registered signers found for reward cycle {reward_cycle}."); - return Ok(None); - } - // signer uses a Vec for its key_ids, but coordinator uses a HashSet for each signer since it needs to do lots of lookups - let mut weight_end = 1; - let mut coordinator_key_ids = HashMap::with_capacity(4000); - let mut signer_key_ids = HashMap::with_capacity(reward_set_signers.len()); - let mut signer_ids = HashMap::with_capacity(reward_set_signers.len()); - let mut public_keys = PublicKeys { - signers: HashMap::with_capacity(reward_set_signers.len()), - key_ids: HashMap::with_capacity(4000), - }; - let mut signer_public_keys = HashMap::with_capacity(reward_set_signers.len()); - for (i, entry) in reward_set_signers.iter().enumerate() { - let signer_id = u32::try_from(i).expect("FATAL: number of signers exceeds u32::MAX"); - let ecdsa_public_key = ecdsa::PublicKey::try_from(entry.signing_key.as_slice()).map_err(|e| { - ClientError::CorruptedRewardSet(format!( - "Reward cycle {reward_cycle} failed to convert signing key to ecdsa::PublicKey: {e}" - )) - })?; - let signer_public_key = Point::try_from(&Compressed::from(ecdsa_public_key.to_bytes())) - .map_err(|e| { - ClientError::CorruptedRewardSet(format!( - "Reward cycle {reward_cycle} failed to convert signing key to Point: {e}" - )) - })?; - let stacks_public_key = StacksPublicKey::from_slice(entry.signing_key.as_slice()).map_err(|e| { - ClientError::CorruptedRewardSet(format!( - "Reward cycle {reward_cycle} failed to convert signing key to StacksPublicKey: {e}" - )) - })?; - - let stacks_address = StacksAddress::p2pkh(self.mainnet, &stacks_public_key); - - signer_ids.insert(stacks_address, signer_id); - signer_public_keys.insert(signer_id, signer_public_key); - let weight_start = weight_end; - weight_end = weight_start + entry.weight; - for key_id in weight_start..weight_end { - public_keys.key_ids.insert(key_id, ecdsa_public_key); - public_keys.signers.insert(signer_id, ecdsa_public_key); - coordinator_key_ids - .entry(signer_id) - .or_insert(HashSet::with_capacity(entry.weight as usize)) - .insert(key_id); - signer_key_ids - .entry(signer_id) - .or_insert(Vec::with_capacity(entry.weight as usize)) - .push(key_id); - } - } - - let signer_set = - u32::try_from(reward_cycle % 2).expect("FATAL: reward_cycle % 2 exceeds u32::MAX"); - let signer_stackerdb_contract_id = boot_code_id(SIGNERS_NAME, self.mainnet); - // Get the signer writers from the stacker-db to find the signer slot id - let signer_slots_weights = self - .get_stackerdb_signer_slots(&signer_stackerdb_contract_id, signer_set) - .unwrap(); - let mut signer_slot_ids = HashMap::with_capacity(signer_slots_weights.len()); - for (index, (address, _)) in signer_slots_weights.into_iter().enumerate() { - signer_slot_ids.insert( - address, - u32::try_from(index).expect("FATAL: number of signers exceeds u32::MAX"), - ); - } - - for address in signer_ids.keys() { - if !signer_slot_ids.contains_key(address) { - debug!("Signer {address} does not have a slot id in the stackerdb"); - return Ok(None); - } - } - - Ok(Some(RegisteredSignersInfo { - public_keys, - signer_key_ids, - signer_ids, - signer_slot_ids, - signer_public_keys, - coordinator_key_ids, - })) + Ok(stackers_response.stacker_set.signers) } /// Retreive the current pox data from the stacks node @@ -484,12 +386,12 @@ impl StacksClient { "Failed to convert aggregate public key to compressed data: {e}" )) })?; - let point = Point::try_from(&compressed_data).map_err(|e| { + let dkg_public_key = Point::try_from(&compressed_data).map_err(|e| { ClientError::MalformedClarityValue(format!( "Failed to convert aggregate public key to a point: {e}" )) })?; - Ok(Some(point)) + Ok(Some(dkg_public_key)) } /// Helper function to create a stacks transaction for a modifying contract call @@ -497,18 +399,18 @@ impl StacksClient { &self, signer_index: u32, round: u64, - point: Point, + dkg_public_key: Point, reward_cycle: u64, tx_fee: Option, nonce: u64, ) -> Result { - debug!("Building {VOTE_FUNCTION_NAME} transaction..."); + debug!("Building {SIGNERS_VOTING_FUNCTION_NAME} transaction..."); let contract_address = boot_code_addr(self.mainnet); let contract_name = ContractName::from(SIGNERS_VOTING_NAME); - let function_name = ClarityName::from(VOTE_FUNCTION_NAME); + let function_name = ClarityName::from(SIGNERS_VOTING_FUNCTION_NAME); let function_args = vec![ ClarityValue::UInt(signer_index as u128), - ClarityValue::buff_from(point.compress().data.to_vec())?, + ClarityValue::buff_from(dkg_public_key.compress().data.to_vec())?, ClarityValue::UInt(round as u128), ClarityValue::UInt(reward_cycle as u128), ]; @@ -688,11 +590,12 @@ mod tests { use blockstack_lib::chainstate::nakamoto::NakamotoBlockHeader; use blockstack_lib::chainstate::stacks::address::PoxAddress; - use blockstack_lib::chainstate::stacks::boot::{NakamotoSignerEntry, PoxStartCycleInfo}; + use blockstack_lib::chainstate::stacks::boot::{ + NakamotoSignerEntry, PoxStartCycleInfo, RewardSet, + }; use blockstack_lib::chainstate::stacks::ThresholdSignature; use rand::thread_rng; use rand_core::RngCore; - use serial_test::serial; use stacks_common::bitvec::BitVec; use stacks_common::consts::{CHAIN_ID_TESTNET, SIGNER_SLOTS_PER_USER}; use stacks_common::types::chainstate::{ConsensusHash, StacksBlockId, TrieHash}; @@ -933,7 +836,6 @@ mod tests { #[ignore] #[test] - #[serial] fn build_vote_for_aggregate_public_key_should_succeed() { let mock = MockServerClient::new(); let point = Point::from(Scalar::random(&mut rand::thread_rng())); @@ -957,7 +859,6 @@ mod tests { #[ignore] #[test] - #[serial] fn broadcast_vote_for_aggregate_public_key_should_succeed() { let mock = MockServerClient::new(); let point = Point::from(Scalar::random(&mut rand::thread_rng())); @@ -1233,9 +1134,9 @@ mod tests { let stackers_response_json = serde_json::to_string(&stackers_response) .expect("Failed to serialize get stacker response"); let response = format!("HTTP/1.1 200 OK\n\n{stackers_response_json}"); - let h = spawn(move || mock.client.get_reward_set(0)); + let h = spawn(move || mock.client.get_reward_set_signers(0)); write_response(mock.server, response.as_bytes()); - assert_eq!(h.join().unwrap().unwrap(), stacker_set); + assert_eq!(h.join().unwrap().unwrap(), stacker_set.signers); } #[test] diff --git a/stacks-signer/src/config.rs b/stacks-signer/src/config.rs index df9e2db404..d8c7b4a8e9 100644 --- a/stacks-signer/src/config.rs +++ b/stacks-signer/src/config.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; use std::fs; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::PathBuf; @@ -33,6 +32,8 @@ use wsts::curve::point::Point; use wsts::curve::scalar::Scalar; use wsts::state_machine::PublicKeys; +use crate::signer::SignerSlotID; + const EVENT_TIMEOUT_MS: u64 = 5000; // Default transaction fee in microstacks (if unspecificed in the config file) // TODO: Use the fee estimation endpoint to get the default fee. @@ -111,22 +112,20 @@ impl Network { } } -/// The registered signer information for a specific reward cycle +/// Parsed Reward Set #[derive(Debug, Clone)] -pub struct RegisteredSignersInfo { - /// The signer to key ids mapping for the coordinator - pub coordinator_key_ids: HashMap>, - /// The signer to key ids mapping for the signers - pub signer_key_ids: HashMap>, - /// The signer ids to wsts pubilc keys mapping - pub signer_public_keys: HashMap, - /// The signer addresses mapped to their signer ids +pub struct ParsedSignerEntries { + /// The signer addresses mapped to signer id pub signer_ids: HashMap, - /// The signer slot id for a signer address registered in stackerdb - /// This corresponds to their unique index when voting in a reward cycle - pub signer_slot_ids: HashMap, - /// The public keys for the reward cycle + /// The signer ids mapped to public key and key ids mapped to public keys pub public_keys: PublicKeys, + /// The signer ids mapped to key ids + pub signer_key_ids: HashMap>, + /// The signer ids mapped to wsts public keys + pub signer_public_keys: HashMap, + /// The signer ids mapped to a hash set of key ids + /// The wsts coordinator uses a hash set for each signer since it needs to do lots of lookups + pub coordinator_key_ids: HashMap>, } /// The Configuration info needed for an individual signer per reward cycle @@ -134,14 +133,16 @@ pub struct RegisteredSignersInfo { pub struct SignerConfig { /// The reward cycle of the configuration pub reward_cycle: u64, - /// The signer ID assigned to this signer + /// The signer ID assigned to this signer to be used in DKG and Sign rounds pub signer_id: u32, - /// The index into the signers list of this signer's key (may be different from signer_id) - pub signer_slot_id: u32, + /// The signer stackerdb slot id (may be different from signer_id) + pub signer_slot_id: SignerSlotID, /// This signer's key ids pub key_ids: Vec, /// The registered signers for this reward cycle - pub registered_signers: RegisteredSignersInfo, + pub signer_entries: ParsedSignerEntries, + /// The signer slot ids of all signers registered for this reward cycle + pub signer_slot_ids: Vec, /// The Scalar representation of the private key for signer communication pub ecdsa_private_key: Scalar, /// The private key for this signer diff --git a/stacks-signer/src/coordinator.rs b/stacks-signer/src/coordinator.rs index 2c23fd0b32..234d1ade84 100644 --- a/stacks-signer/src/coordinator.rs +++ b/stacks-signer/src/coordinator.rs @@ -174,7 +174,7 @@ mod tests { let number_of_tests = 5; let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); let public_keys = generate_signer_config(&config, 10, 4000) - .registered_signers + .signer_entries .public_keys; let mut results = Vec::new(); @@ -197,7 +197,7 @@ mod tests { ) -> Vec> { let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); let public_keys = generate_signer_config(&config, 10, 4000) - .registered_signers + .signer_entries .public_keys; let mut results = Vec::new(); let same_hash = generate_random_consensus_hash(); diff --git a/stacks-signer/src/runloop.rs b/stacks-signer/src/runloop.rs index 0b6a0c05a3..c0ec846acd 100644 --- a/stacks-signer/src/runloop.rs +++ b/stacks-signer/src/runloop.rs @@ -18,17 +18,21 @@ use std::sync::mpsc::Sender; use std::time::Duration; use blockstack_lib::chainstate::burn::ConsensusHashExtensions; -use hashbrown::HashMap; +use blockstack_lib::chainstate::stacks::boot::{NakamotoSignerEntry, SIGNERS_NAME}; +use blockstack_lib::util_lib::boot::boot_code_id; +use hashbrown::{HashMap, HashSet}; use libsigner::{SignerEvent, SignerRunLoop}; use slog::{slog_debug, slog_error, slog_info, slog_warn}; -use stacks_common::types::chainstate::ConsensusHash; +use stacks_common::types::chainstate::{ConsensusHash, StacksAddress, StacksPublicKey}; use stacks_common::{debug, error, info, warn}; +use wsts::curve::ecdsa; +use wsts::curve::point::{Compressed, Point}; use wsts::state_machine::coordinator::State as CoordinatorState; -use wsts::state_machine::OperationResult; +use wsts::state_machine::{OperationResult, PublicKeys}; use crate::client::{retry_with_exponential_backoff, ClientError, StacksClient}; -use crate::config::{GlobalConfig, SignerConfig}; -use crate::signer::{Command as SignerCommand, Signer, State as SignerState}; +use crate::config::{GlobalConfig, ParsedSignerEntries, SignerConfig}; +use crate::signer::{Command as SignerCommand, Signer, SignerSlotID, State as SignerState}; /// Which operation to perform #[derive(PartialEq, Clone, Debug)] @@ -78,27 +82,118 @@ impl From for RunLoop { } impl RunLoop { + /// Parse Nakamoto signer entries into relevant signer information + pub fn parse_nakamoto_signer_entries( + signers: &[NakamotoSignerEntry], + is_mainnet: bool, + ) -> ParsedSignerEntries { + let mut weight_end = 1; + let mut coordinator_key_ids = HashMap::with_capacity(4000); + let mut signer_key_ids = HashMap::with_capacity(signers.len()); + let mut signer_ids = HashMap::with_capacity(signers.len()); + let mut public_keys = PublicKeys { + signers: HashMap::with_capacity(signers.len()), + key_ids: HashMap::with_capacity(4000), + }; + let mut signer_public_keys = HashMap::with_capacity(signers.len()); + for (i, entry) in signers.iter().enumerate() { + // TODO: track these signer ids as non participating if any of the conversions fail + let signer_id = u32::try_from(i).expect("FATAL: number of signers exceeds u32::MAX"); + let ecdsa_public_key = ecdsa::PublicKey::try_from(entry.signing_key.as_slice()) + .expect("FATAL: corrupted signing key"); + let signer_public_key = Point::try_from(&Compressed::from(ecdsa_public_key.to_bytes())) + .expect("FATAL: corrupted signing key"); + let stacks_public_key = StacksPublicKey::from_slice(entry.signing_key.as_slice()) + .expect("FATAL: Corrupted signing key"); + + let stacks_address = StacksAddress::p2pkh(is_mainnet, &stacks_public_key); + signer_ids.insert(stacks_address, signer_id); + signer_public_keys.insert(signer_id, signer_public_key); + let weight_start = weight_end; + weight_end = weight_start + entry.weight; + for key_id in weight_start..weight_end { + public_keys.key_ids.insert(key_id, ecdsa_public_key); + public_keys.signers.insert(signer_id, ecdsa_public_key); + coordinator_key_ids + .entry(signer_id) + .or_insert(HashSet::with_capacity(entry.weight as usize)) + .insert(key_id); + signer_key_ids + .entry(signer_id) + .or_insert(Vec::with_capacity(entry.weight as usize)) + .push(key_id); + } + } + ParsedSignerEntries { + signer_ids, + public_keys, + signer_key_ids, + signer_public_keys, + coordinator_key_ids, + } + } + + /// Get the registered signers for a specific reward cycle + /// Returns None if no signers are registered or its not Nakamoto cycle + pub fn get_parsed_reward_set( + &self, + reward_cycle: u64, + ) -> Result, ClientError> { + debug!("Getting registered signers for reward cycle {reward_cycle}..."); + let Some(signers) = self.stacks_client.get_reward_set_signers(reward_cycle)? else { + warn!("No reward set signers found for reward cycle {reward_cycle}."); + return Ok(None); + }; + if signers.is_empty() { + warn!("No registered signers found for reward cycle {reward_cycle}."); + return Ok(None); + } + Ok(Some(Self::parse_nakamoto_signer_entries( + &signers, + self.config.network.is_mainnet(), + ))) + } + + /// Get the stackerdb signer slots for a specific reward cycle + pub fn get_parsed_signer_slots( + &self, + stacks_client: &StacksClient, + reward_cycle: u64, + ) -> Result, ClientError> { + let signer_set = + u32::try_from(reward_cycle % 2).expect("FATAL: reward_cycle % 2 exceeds u32::MAX"); + let signer_stackerdb_contract_id = + boot_code_id(SIGNERS_NAME, self.config.network.is_mainnet()); + // Get the signer writers from the stacker-db to find the signer slot id + let stackerdb_signer_slots = + stacks_client.get_stackerdb_signer_slots(&signer_stackerdb_contract_id, signer_set)?; + let mut signer_slot_ids = HashMap::with_capacity(stackerdb_signer_slots.len()); + for (index, (address, _)) in stackerdb_signer_slots.into_iter().enumerate() { + signer_slot_ids.insert( + address, + SignerSlotID( + u32::try_from(index).expect("FATAL: number of signers exceeds u32::MAX"), + ), + ); + } + Ok(signer_slot_ids) + } /// Get a signer configuration for a specific reward cycle from the stacks node fn get_signer_config(&mut self, reward_cycle: u64) -> Option { // We can only register for a reward cycle if a reward set exists. - let registered_signers = self - .stacks_client - .get_registered_signers_info(reward_cycle).map_err(|e| { - error!( - "Failed to retrieve registered signers info for reward cycle {reward_cycle}: {e}" - ); - e - }).ok()??; - + let signer_entries = self.get_parsed_reward_set(reward_cycle).ok()??; + let signer_slot_ids = self + .get_parsed_signer_slots(&self.stacks_client, reward_cycle) + .ok()?; let current_addr = self.stacks_client.get_signer_address(); - let Some(signer_slot_id) = registered_signers.signer_slot_ids.get(current_addr) else { + let Some(signer_slot_id) = signer_slot_ids.get(current_addr) else { warn!( "Signer {current_addr} was not found in stacker db. Must not be registered for this reward cycle {reward_cycle}." ); return None; }; - let Some(signer_id) = registered_signers.signer_ids.get(current_addr) else { + let Some(signer_id) = signer_entries.signer_ids.get(current_addr) else { warn!( "Signer {current_addr} was found in stacker db but not the reward set for reward cycle {reward_cycle}." ); @@ -107,7 +202,7 @@ impl RunLoop { info!( "Signer #{signer_id} ({current_addr}) is registered for reward cycle {reward_cycle}." ); - let key_ids = registered_signers + let key_ids = signer_entries .signer_key_ids .get(signer_id) .cloned() @@ -117,7 +212,8 @@ impl RunLoop { signer_id: *signer_id, signer_slot_id: *signer_slot_id, key_ids, - registered_signers, + signer_entries, + signer_slot_ids: signer_slot_ids.into_values().collect(), ecdsa_private_key: self.config.ecdsa_private_key, stacks_private_key: self.config.stacks_private_key, node_host: self.config.node_host, @@ -156,14 +252,13 @@ impl RunLoop { if signer.reward_cycle == prior_reward_cycle { // The signers have been calculated for the next reward cycle. Update the current one debug!("Signer #{}: Next reward cycle ({reward_cycle}) signer set calculated. Updating current reward cycle ({prior_reward_cycle}) signer.", signer.signer_id); - signer.next_signer_ids = new_signer_config - .registered_signers + signer.next_signer_addresses = new_signer_config + .signer_entries .signer_ids - .values() + .keys() .copied() .collect(); - signer.next_signer_slot_ids = - new_signer_config.registered_signers.signer_slot_ids.clone(); + signer.next_signer_slot_ids = new_signer_config.signer_slot_ids.clone(); } } self.stacks_signers @@ -201,7 +296,7 @@ impl RunLoop { if signer.approved_aggregate_public_key.is_none() { retry_with_exponential_backoff(|| { signer - .update_dkg(&self.stacks_client, current_reward_cycle) + .update_dkg(&self.stacks_client) .map_err(backoff::Error::transient) })?; } @@ -295,3 +390,36 @@ impl SignerRunLoop, RunLoopCommand> for RunLoop { None } } +#[cfg(test)] +mod tests { + use blockstack_lib::chainstate::stacks::boot::NakamotoSignerEntry; + use stacks_common::types::chainstate::{StacksPrivateKey, StacksPublicKey}; + + use super::RunLoop; + + #[test] + fn parse_nakamoto_signer_entries_test() { + let nmb_signers = 10; + let weight = 10; + let mut signer_entries = Vec::with_capacity(nmb_signers); + for _ in 0..nmb_signers { + let key = StacksPublicKey::from_private(&StacksPrivateKey::new()).to_bytes_compressed(); + let mut signing_key = [0u8; 33]; + signing_key.copy_from_slice(&key); + signer_entries.push(NakamotoSignerEntry { + signing_key, + stacked_amt: 0, + weight, + }); + } + + let parsed_entries = RunLoop::parse_nakamoto_signer_entries(&signer_entries, false); + assert_eq!(parsed_entries.signer_ids.len(), nmb_signers); + let mut signer_ids = parsed_entries.signer_ids.into_values().collect::>(); + signer_ids.sort(); + assert_eq!( + signer_ids, + (0..nmb_signers).map(|id| id as u32).collect::>() + ); + } +} diff --git a/stacks-signer/src/signer.rs b/stacks-signer/src/signer.rs index b92ed0e1de..59962e5ae5 100644 --- a/stacks-signer/src/signer.rs +++ b/stacks-signer/src/signer.rs @@ -17,11 +17,11 @@ use std::collections::VecDeque; use std::sync::mpsc::Sender; use std::time::Instant; +use blockstack_lib::chainstate::nakamoto::signer_set::NakamotoSigners; use blockstack_lib::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockVote}; -use blockstack_lib::chainstate::stacks::boot::SIGNERS_VOTING_NAME; -use blockstack_lib::chainstate::stacks::{StacksTransaction, TransactionPayload}; +use blockstack_lib::chainstate::stacks::boot::SIGNERS_VOTING_FUNCTION_NAME; +use blockstack_lib::chainstate::stacks::StacksTransaction; use blockstack_lib::net::api::postblock_proposal::BlockValidateResponse; -use blockstack_lib::util_lib::boot::boot_code_id; use hashbrown::{HashMap, HashSet}; use libsigner::{BlockRejection, BlockResponse, RejectCode, SignerEvent, SignerMessage}; use slog::{slog_debug, slog_error, slog_info, slog_warn}; @@ -32,7 +32,7 @@ use stacks_common::util::hash::Sha512Trunc256Sum; use stacks_common::{debug, error, info, warn}; use wsts::common::{MerkleRoot, Signature}; use wsts::curve::keys::PublicKey; -use wsts::curve::point::{Compressed, Point}; +use wsts::curve::point::Point; use wsts::net::{Message, NonceRequest, Packet, SignatureShareRequest}; use wsts::state_machine::coordinator::fire::Coordinator as FireCoordinator; use wsts::state_machine::coordinator::{ @@ -42,12 +42,20 @@ use wsts::state_machine::signer::Signer as WSTSSigner; use wsts::state_machine::{OperationResult, SignError}; use wsts::v2; -use crate::client::{ - retry_with_exponential_backoff, ClientError, StackerDB, StacksClient, VOTE_FUNCTION_NAME, -}; +use crate::client::{retry_with_exponential_backoff, ClientError, StackerDB, StacksClient}; use crate::config::SignerConfig; use crate::coordinator::CoordinatorSelector; +/// The signer StackerDB slot ID, purposefully wrapped to prevent conflation with SignerID +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)] +pub struct SignerSlotID(pub u32); + +impl std::fmt::Display for SignerSlotID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + /// Additional Info about a proposed block pub struct BlockInfo { /// The block we are considering @@ -130,14 +138,14 @@ pub struct Signer { pub mainnet: bool, /// The signer id pub signer_id: u32, - /// The other signer ids for this signer's reward cycle - pub signer_ids: Vec, - /// The addresses of other signers mapped to their signer slot ID - pub signer_slot_ids: HashMap, - /// The other signer ids for the NEXT reward cycle's signers - pub next_signer_ids: Vec, - /// The signer addresses mapped to slot ID for the NEXT reward cycle's signers - pub next_signer_slot_ids: HashMap, + /// The signer slot ids for the signers in the reward cycle + pub signer_slot_ids: Vec, + /// The addresses of other signers + pub signer_addresses: Vec, + /// The signer slot ids for the signers in the NEXT reward cycle + pub next_signer_slot_ids: Vec, + /// The addresses of the signers for the NEXT reward cycle + pub next_signer_addresses: Vec, /// The reward cycle this signer belongs to pub reward_cycle: u64, /// The tx fee in uSTX to use if the epoch is pre Nakamoto (Epoch 3.0) @@ -152,12 +160,12 @@ impl From for Signer { fn from(signer_config: SignerConfig) -> Self { let stackerdb = StackerDB::from(&signer_config); - let num_signers = u32::try_from(signer_config.registered_signers.public_keys.signers.len()) + let num_signers = u32::try_from(signer_config.signer_entries.public_keys.signers.len()) .expect("FATAL: Too many registered signers to fit in a u32"); - let num_keys = u32::try_from(signer_config.registered_signers.public_keys.key_ids.len()) + let num_keys = u32::try_from(signer_config.signer_entries.public_keys.key_ids.len()) .expect("FATAL: Too many key ids to fit in a u32"); - let threshold = num_keys * 7 / 10; - let dkg_threshold = num_keys * 9 / 10; + let threshold = (num_keys as f64 * 7_f64 / 10_f64).ceil() as u32; + let dkg_threshold = (num_keys as f64 * 9_f64 / 10_f64).ceil() as u32; let coordinator_config = CoordinatorConfig { threshold, @@ -170,8 +178,8 @@ impl From for Signer { dkg_end_timeout: signer_config.dkg_end_timeout, nonce_timeout: signer_config.nonce_timeout, sign_timeout: signer_config.sign_timeout, - signer_key_ids: signer_config.registered_signers.coordinator_key_ids, - signer_public_keys: signer_config.registered_signers.signer_public_keys, + signer_key_ids: signer_config.signer_entries.coordinator_key_ids, + signer_public_keys: signer_config.signer_entries.signer_public_keys, }; let coordinator = FireCoordinator::new(coordinator_config); @@ -182,10 +190,10 @@ impl From for Signer { signer_config.signer_id, signer_config.key_ids, signer_config.ecdsa_private_key, - signer_config.registered_signers.public_keys.clone(), + signer_config.signer_entries.public_keys.clone(), ); let coordinator_selector = - CoordinatorSelector::from(signer_config.registered_signers.public_keys); + CoordinatorSelector::from(signer_config.signer_entries.public_keys); debug!( "Signer #{}: initial coordinator is signer {}", @@ -202,15 +210,14 @@ impl From for Signer { stackerdb, mainnet: signer_config.mainnet, signer_id: signer_config.signer_id, - signer_ids: signer_config - .registered_signers + signer_addresses: signer_config + .signer_entries .signer_ids - .values() - .copied() + .into_keys() .collect(), - signer_slot_ids: signer_config.registered_signers.signer_slot_ids, - next_signer_ids: vec![], - next_signer_slot_ids: HashMap::new(), + signer_slot_ids: signer_config.signer_slot_ids.clone(), + next_signer_slot_ids: vec![], + next_signer_addresses: vec![], reward_cycle: signer_config.reward_cycle, tx_fee_ustx: signer_config.tx_fee_ustx, coordinator_selector, @@ -353,7 +360,6 @@ impl Signer { stacks_client: &StacksClient, block_validate_response: &BlockValidateResponse, res: Sender>, - current_reward_cycle: u64, ) { let block_info = match block_validate_response { BlockValidateResponse::Ok(block_validate_ok) => { @@ -364,11 +370,7 @@ impl Signer { debug!("Signer #{}: Received a block validate response for a block we have not seen before. Ignoring...", self.signer_id); return; }; - let is_valid = self.verify_block_transactions( - stacks_client, - &block_info.block, - current_reward_cycle, - ); + let is_valid = self.verify_block_transactions(stacks_client, &block_info.block); block_info.valid = Some(is_valid); info!( "Signer #{}: Treating block validation for block {} as valid: {:?}", @@ -413,7 +415,7 @@ impl Signer { msg: Message::NonceRequest(nonce_request), sig: vec![], }; - self.handle_packets(stacks_client, res, &[packet], current_reward_cycle); + self.handle_packets(stacks_client, res, &[packet]); } else { let coordinator_id = self.coordinator_selector.get_coordinator().0; if block_info.valid.unwrap_or(false) @@ -449,7 +451,6 @@ impl Signer { stacks_client: &StacksClient, res: Sender>, messages: &[SignerMessage], - current_reward_cycle: u64, ) { let coordinator_pubkey = self.coordinator_selector.get_coordinator().1; let packets: Vec = messages @@ -462,7 +463,7 @@ impl Signer { } }) .collect(); - self.handle_packets(stacks_client, res, &packets, current_reward_cycle); + self.handle_packets(stacks_client, res, &packets); } /// Handle proposed blocks submitted by the miners to stackerdb @@ -492,7 +493,6 @@ impl Signer { stacks_client: &StacksClient, res: Sender>, packets: &[Packet], - current_reward_cycle: u64, ) { let signer_outbound_messages = self .signing_round @@ -520,7 +520,7 @@ impl Signer { if !operation_results.is_empty() { // We have finished a signing or DKG round, either successfully or due to error. // Regardless of the why, update our state to Idle as we should not expect the operation to continue. - self.process_operation_results(stacks_client, &operation_results, current_reward_cycle); + self.process_operation_results(stacks_client, &operation_results); self.send_operation_results(res, operation_results); self.finish_operation(); } else if !packets.is_empty() && self.coordinator.state != CoordinatorState::Idle { @@ -631,7 +631,6 @@ impl Signer { &mut self, stacks_client: &StacksClient, block: &NakamotoBlock, - current_reward_cycle: u64, ) -> bool { if self.approved_aggregate_public_key.is_some() { // We do not enforce a block contain any transactions except the aggregate votes when it is NOT already set @@ -639,9 +638,7 @@ impl Signer { debug!("Signer #{}: Already have an aggregate key for reward cycle {}. Skipping transaction verification...", self.signer_id, self.reward_cycle); return true; } - if let Ok(expected_transactions) = - self.get_expected_transactions(stacks_client, current_reward_cycle) - { + if let Ok(expected_transactions) = self.get_expected_transactions(stacks_client) { //It might be worth building a hashset of the blocks' txids and checking that against the expected transaction's txid. let block_tx_hashset = block.txs.iter().map(|tx| tx.txid()).collect::>(); // Ensure the block contains the transactions we expect @@ -708,144 +705,20 @@ impl Signer { } } - /// Filter out transactions from the stackerdb that are not valid - /// i.e. not valid vote-for-aggregate-public-key transactions from registered signers - fn filter_invalid_transactions( - &self, - stacks_client: &StacksClient, - current_reward_cycle: u64, - signer_slot_ids: &HashMap, - transaction: StacksTransaction, - ) -> Option { - // Filter out transactions that have already been confirmed (can happen if a signer did not update stacker db since the last block was processed) - let origin_address = transaction.origin_address(); - let origin_nonce = transaction.get_origin_nonce(); - let Some(origin_signer_id) = signer_slot_ids.get(&origin_address) else { - debug!( - "Signer #{}: Unrecognized origin address ({origin_address}). Filtering ({}).", - self.signer_id, - transaction.txid() - ); - return None; - }; - let Ok(account_nonce) = retry_with_exponential_backoff(|| { - stacks_client - .get_account_nonce(&origin_address) - .map_err(backoff::Error::transient) - }) else { - warn!( - "Signer #{}: Unable to get account for transaction origin address: {origin_address}. Filtering ({}).", - self.signer_id, - transaction.txid() - ); - return None; - }; - // TODO: add a check that we don't have two conflicting transactions in the same block from the same signer. This is a potential attack vector (will result in an invalid block) - if origin_nonce < account_nonce { - debug!("Signer #{}: Received a transaction with an outdated nonce ({account_nonce} < {origin_nonce}). Filtering ({}).", self.signer_id, transaction.txid()); - return None; - } - if transaction.is_mainnet() != self.mainnet { - debug!( - "Signer #{}: Received a transaction with an unexpected network. Filtering ({}).", - self.signer_id, - transaction.txid() - ); - return None; - } - let Ok(valid) = retry_with_exponential_backoff(|| { - self.verify_payload( - stacks_client, - &transaction, - *origin_signer_id, - current_reward_cycle, - ) - .map_err(backoff::Error::transient) - }) else { - warn!( - "Signer #{}: Unable to validate transaction payload. Filtering ({}).", - self.signer_id, - transaction.txid() - ); - return None; - }; - if !valid { - debug!( - "Signer #{}: Received a transaction with an invalid payload. Filtering ({}).", - self.signer_id, - transaction.txid() - ); - return None; - } - debug!( - "Signer #{}: Expect transaction {} ({transaction:?})", - self.signer_id, - transaction.txid() - ); - Some(transaction) - } - - ///Helper function to verify the payload contents of a transaction are as expected - fn verify_payload( - &self, - stacks_client: &StacksClient, - transaction: &StacksTransaction, - origin_signer_id: u32, - current_reward_cycle: u64, - ) -> Result { - let Some((index, _point, round, reward_cycle)) = - Self::parse_vote_for_aggregate_public_key(transaction) - else { - // The transaction is not a valid vote-for-aggregate-public-key transaction - return Ok(false); - }; - if index != origin_signer_id as u64 { - // The signer is attempting to vote for another signer id than their own - return Ok(false); - } - let next_reward_cycle = current_reward_cycle.wrapping_add(1); - if reward_cycle != next_reward_cycle { - // The signer is attempting to vote for a reward cycle that is not the next reward cycle - return Ok(false); - } - - let vote = stacks_client.get_vote_for_aggregate_public_key( - round, - reward_cycle, - transaction.origin_address(), - )?; - if vote.is_some() { - // The signer has already voted for this round and reward cycle - return Ok(false); - } - - let last_round = stacks_client.get_last_round(reward_cycle)?; - // TODO: should we impose a limit on the number of special cased transactions allowed for a single signer at any given time?? In theory only 1 would be required per dkg round i.e. per block - if last_round.unwrap_or(0).saturating_add(1) < round { - // Do not allow future votes. This is to prevent signers sending a bazillion votes for a future round and clogging the block space - // The signer is attempting to vote for a round that is greater than one past the last round - return Ok(false); - } - Ok(true) - } - - /// Get this signer's transactions from stackerdb, filtering out any invalid transactions + /// Get transactions from stackerdb for the given addresses and account nonces, filtering out any malformed transactions fn get_signer_transactions( &mut self, - stacks_client: &StacksClient, - current_reward_cycle: u64, + nonces: &std::collections::HashMap, ) -> Result, ClientError> { let transactions: Vec<_> = self .stackerdb - .get_current_transactions_with_retry(self.signer_id)? + .get_current_transactions_with_retry()? .into_iter() .filter_map(|tx| { - self.filter_invalid_transactions( - stacks_client, - current_reward_cycle, - &self.signer_slot_ids, - tx, - ) + if !NakamotoSigners::valid_vote_transaction(nonces, &tx, self.mainnet) { + return None; + } + Some(tx) }) .collect(); Ok(transactions) @@ -855,29 +728,28 @@ impl Signer { fn get_expected_transactions( &mut self, stacks_client: &StacksClient, - current_reward_cycle: u64, ) -> Result, ClientError> { - if self.next_signer_ids.is_empty() { + if self.next_signer_slot_ids.is_empty() { debug!( "Signer #{}: No next signers. Skipping transaction retrieval.", self.signer_id ); return Ok(vec![]); } + // Get all the account nonces for the next signers + let account_nonces = self.get_account_nonces(stacks_client, &self.next_signer_addresses); let transactions: Vec<_> = self .stackerdb - .get_next_transactions_with_retry(&self.next_signer_ids)? - .into_iter() - .filter_map(|tx| { - self.filter_invalid_transactions( - stacks_client, - current_reward_cycle, - &self.next_signer_slot_ids, - tx, - ) - }) - .collect(); - Ok(transactions) + .get_next_transactions_with_retry(&self.next_signer_slot_ids)?; + let mut filtered_transactions = std::collections::HashMap::new(); + NakamotoSigners::update_filtered_transactions( + &mut filtered_transactions, + &account_nonces, + self.mainnet, + transactions, + ); + // We only allow enforcement of one special cased transaction per signer address per block + Ok(filtered_transactions.into_values().collect()) } /// Determine the vote for a block and update the block info and nonce request accordingly @@ -954,7 +826,6 @@ impl Signer { &mut self, stacks_client: &StacksClient, operation_results: &[OperationResult], - current_reward_cycle: u64, ) { for operation_result in operation_results { // Signers only every trigger non-taproot signing rounds over blocks. Ignore SignTaproot results @@ -966,8 +837,8 @@ impl Signer { OperationResult::SignTaproot(_) => { debug!("Signer #{}: Received a signature result for a taproot signature. Nothing to broadcast as we currently sign blocks with a FROST signature.", self.signer_id); } - OperationResult::Dkg(point) => { - self.process_dkg(stacks_client, point, current_reward_cycle); + OperationResult::Dkg(dkg_public_key) => { + self.process_dkg(stacks_client, dkg_public_key); } OperationResult::SignError(e) => { warn!("Signer #{}: Received a Sign error: {e:?}", self.signer_id); @@ -982,12 +853,7 @@ impl Signer { } /// Process a dkg result by broadcasting a vote to the stacks node - fn process_dkg( - &mut self, - stacks_client: &StacksClient, - point: &Point, - current_reward_cycle: u64, - ) { + fn process_dkg(&mut self, stacks_client: &StacksClient, dkg_public_key: &Point) { let epoch = retry_with_exponential_backoff(|| { stacks_client .get_node_epoch() @@ -1004,70 +870,97 @@ impl Signer { None }; // Get our current nonce from the stacks node and compare it against what we have sitting in the stackerdb instance - let nonce = self.get_next_nonce(stacks_client, current_reward_cycle); + let signer_address = stacks_client.get_signer_address(); + // Retreieve ALL account nonces as we may have transactions from other signers in our stackerdb slot that we care about + let account_nonces = self.get_account_nonces(stacks_client, &self.signer_addresses); + let account_nonce = account_nonces.get(signer_address).unwrap_or(&0); + let signer_transactions = retry_with_exponential_backoff(|| { + self.get_signer_transactions(&account_nonces) + .map_err(backoff::Error::transient) + }) + .map_err(|e| { + warn!( + "Signer #{}: Unable to get signer transactions: {e:?}", + self.signer_id + ); + }) + .unwrap_or_default(); + // If we have a transaction in the stackerdb slot, we need to increment the nonce hence the +1, else should use the account nonce + let next_nonce = signer_transactions + .first() + .map(|tx| tx.get_origin_nonce().wrapping_add(1)) + .unwrap_or(*account_nonce); match stacks_client.build_vote_for_aggregate_public_key( - self.stackerdb.get_signer_slot_id(), + self.stackerdb.get_signer_slot_id().0, self.coordinator.current_dkg_id, - *point, + *dkg_public_key, self.reward_cycle, tx_fee, - nonce, + next_nonce, ) { - Ok(transaction) => { - if let Err(e) = - self.broadcast_dkg_vote(stacks_client, transaction, epoch, current_reward_cycle) - { + Ok(new_transaction) => { + if let Err(e) = self.broadcast_dkg_vote( + stacks_client, + epoch, + signer_transactions, + new_transaction, + ) { warn!( - "Signer #{}: Failed to broadcast DKG vote ({point:?}): {e:?}", + "Signer #{}: Failed to broadcast DKG public key vote ({dkg_public_key:?}): {e:?}", self.signer_id ); } } Err(e) => { warn!( - "Signer #{}: Failed to build DKG vote ({point:?}) transaction: {e:?}.", + "Signer #{}: Failed to build DKG public key vote ({dkg_public_key:?}) transaction: {e:?}.", self.signer_id ); } } } - /// Get the next available nonce, taking into consideration the nonce we have sitting in stackerdb as well as the account nonce - fn get_next_nonce(&mut self, stacks_client: &StacksClient, current_reward_cycle: u64) -> u64 { - let signer_address = stacks_client.get_signer_address(); - let mut next_nonce = stacks_client - .get_account_nonce(signer_address) - .map_err(|e| { + // Get the account nonces for the provided list of signer addresses + fn get_account_nonces( + &self, + stacks_client: &StacksClient, + signer_addresses: &[StacksAddress], + ) -> std::collections::HashMap { + let mut account_nonces = std::collections::HashMap::with_capacity(signer_addresses.len()); + for address in signer_addresses { + let Ok(account_nonce) = retry_with_exponential_backoff(|| { + stacks_client + .get_account_nonce(address) + .map_err(backoff::Error::transient) + }) else { warn!( - "Signer #{}: Failed to get account nonce for signer: {e:?}", + "Signer #{}: Unable to get account nonce for address: {address}.", self.signer_id ); - }) - .unwrap_or(0); - - let current_transactions = self.get_signer_transactions(stacks_client, current_reward_cycle).map_err(|e| { - warn!("Signer #{}: Failed to get old transactions: {e:?}. Defaulting to account nonce.", self.signer_id); - }).unwrap_or_default(); - - for transaction in current_transactions { - let origin_nonce = transaction.get_origin_nonce(); - let origin_address = transaction.origin_address(); - if origin_address == *signer_address && origin_nonce >= next_nonce { - next_nonce = origin_nonce.wrapping_add(1); - } + continue; + }; + account_nonces.insert(*address, account_nonce); } - next_nonce + account_nonces } /// broadcast the dkg vote transaction according to the current epoch fn broadcast_dkg_vote( &mut self, stacks_client: &StacksClient, - new_transaction: StacksTransaction, epoch: StacksEpochId, - current_reward_cycle: u64, + mut signer_transactions: Vec, + new_transaction: StacksTransaction, ) -> Result<(), ClientError> { let txid = new_transaction.txid(); + if self.approved_aggregate_public_key.is_some() { + // We do not enforce a block contain any transactions except the aggregate votes when it is NOT already set + info!( + "Signer #{}: Already has an aggregate key for reward cycle {}. Do not broadcast the transaction ({txid:?}).", + self.signer_id, self.reward_cycle + ); + return Ok(()); + } if epoch >= StacksEpochId::Epoch30 { debug!("Signer #{}: Received a DKG result while in epoch 3.0. Broadcast the transaction only to stackerDB.", self.signer_id); } else if epoch == StacksEpochId::Epoch25 { @@ -1082,23 +975,8 @@ impl Signer { return Ok(()); } // For all Pox-4 epochs onwards, broadcast the results also to stackerDB for other signers/miners to observe - // TODO: Should we even store transactions if not in prepare phase? Should the miner just ignore all signer transactions if not in prepare phase? - let txid = new_transaction.txid(); - let new_transactions = if self.approved_aggregate_public_key.is_some() { - // We do not enforce a block contain any transactions except the aggregate votes when it is NOT already set - info!( - "Signer #{}: Already has an aggregate key for reward cycle {}. Do not broadcast the transaction ({txid:?}).", - self.signer_id, self.reward_cycle - ); - vec![] - } else { - let mut new_transactions = self.get_signer_transactions(stacks_client, current_reward_cycle).map_err(|e| { - warn!("Signer #{}: Failed to get old transactions: {e:?}. Potentially overwriting our existing stackerDB transactions", self.signer_id); - }).unwrap_or_default(); - new_transactions.push(new_transaction); - new_transactions - }; - let signer_message = SignerMessage::Transactions(new_transactions); + signer_transactions.push(new_transaction); + let signer_message = SignerMessage::Transactions(signer_transactions); self.stackerdb.send_message_with_retry(signer_message)?; info!( "Signer #{}: Broadcasted DKG vote transaction ({txid}) to stacker DB", @@ -1225,11 +1103,7 @@ impl Signer { } /// Update the DKG for the provided signer info, triggering it if required - pub fn update_dkg( - &mut self, - stacks_client: &StacksClient, - current_reward_cycle: u64, - ) -> Result<(), ClientError> { + pub fn update_dkg(&mut self, stacks_client: &StacksClient) -> Result<(), ClientError> { let reward_cycle = self.reward_cycle; self.approved_aggregate_public_key = stacks_client.get_approved_aggregate_key(reward_cycle)?; @@ -1249,30 +1123,29 @@ impl Signer { let coordinator_id = self.coordinator_selector.get_coordinator().0; if self.signer_id == coordinator_id && self.state == State::Idle { debug!( - "Signer #{}: Checking if old transactions exist", + "Signer #{}: Checking if old vote transaction exists in StackerDB...", self.signer_id ); // Have I already voted and have a pending transaction? Check stackerdb for the same round number and reward cycle vote transaction - let old_transactions = self.get_signer_transactions(stacks_client, current_reward_cycle).map_err(|e| { - warn!("Signer #{}: Failed to get old transactions: {e:?}. Potentially overwriting our existing transactions", self.signer_id); + // Only get the account nonce of THIS signer as we only care about our own votes, not other signer votes + let signer_address = stacks_client.get_signer_address(); + let account_nonces = self.get_account_nonces(stacks_client, &[*signer_address]); + let old_transactions = self.get_signer_transactions(&account_nonces).map_err(|e| { + warn!("Signer #{}: Failed to get old signer transactions: {e:?}. May trigger DKG unnecessarily", self.signer_id); }).unwrap_or_default(); // Check if we have an existing vote transaction for the same round and reward cycle for transaction in old_transactions.iter() { - let origin_address = transaction.origin_address(); - if &origin_address != stacks_client.get_signer_address() { - continue; - } - let Some((_index, point, round, _reward_cycle)) = - Self::parse_vote_for_aggregate_public_key(transaction) - else { - // The transaction is not a valid vote-for-aggregate-public-key transaction - error!("BUG: Signer #{}: Received an unrecognized transaction ({}) in an already filtered list: {transaction:?}", self.signer_id, transaction.txid()); - continue; - }; - if Some(point) == self.coordinator.aggregate_public_key - && round == self.coordinator.current_dkg_id + let params = + NakamotoSigners::parse_vote_for_aggregate_public_key(transaction).unwrap_or_else(|| panic!("BUG: Signer #{}: Received an invalid {SIGNERS_VOTING_FUNCTION_NAME} transaction in an already filtered list: {transaction:?}", self.signer_id)); + if Some(params.aggregate_key) == self.coordinator.aggregate_public_key + && params.voting_round == self.coordinator.current_dkg_id + && reward_cycle == self.reward_cycle { - debug!("Signer #{}: Not triggering a DKG round. Already have a pending vote transaction for aggregate public key {point:?} for round {round}...", self.signer_id); + debug!("Signer #{}: Not triggering a DKG round. Already have a pending vote transaction.", self.signer_id; + "txid" => %transaction.txid(), + "aggregate_key" => %params.aggregate_key, + "voting_round" => params.voting_round + ); return Ok(()); } } @@ -1312,12 +1185,7 @@ impl Signer { "Signer #{}: Received a block proposal result from the stacks node...", self.signer_id ); - self.handle_block_validate_response( - stacks_client, - block_validate_response, - res, - current_reward_cycle, - ) + self.handle_block_validate_response(stacks_client, block_validate_response, res) } Some(SignerEvent::SignerMessages(signer_set, messages)) => { if *signer_set != self.stackerdb.get_signer_set() { @@ -1329,7 +1197,7 @@ impl Signer { self.signer_id, messages.len() ); - self.handle_signer_messages(stacks_client, res, messages, current_reward_cycle); + self.handle_signer_messages(stacks_client, res, messages); } Some(SignerEvent::ProposedBlocks(blocks)) => { if current_reward_cycle != self.reward_cycle { @@ -1354,582 +1222,4 @@ impl Signer { } Ok(()) } - - fn parse_vote_for_aggregate_public_key( - transaction: &StacksTransaction, - ) -> Option<(u64, Point, u64, u64)> { - let TransactionPayload::ContractCall(payload) = &transaction.payload else { - // Not a contract call so not a special cased vote for aggregate public key transaction - return None; - }; - if payload.contract_identifier() - != boot_code_id(SIGNERS_VOTING_NAME, transaction.is_mainnet()) - || payload.function_name != VOTE_FUNCTION_NAME.into() - { - // This is not a special cased transaction. - return None; - } - if payload.function_args.len() != 4 { - return None; - } - let signer_index_value = payload.function_args.first()?; - let signer_index = u64::try_from(signer_index_value.clone().expect_u128().ok()?).ok()?; - let point_value = payload.function_args.get(1)?; - let point_bytes = point_value.clone().expect_buff(33).ok()?; - let compressed_data = Compressed::try_from(point_bytes.as_slice()).ok()?; - let point = Point::try_from(&compressed_data).ok()?; - let round_value = payload.function_args.get(2)?; - let round = u64::try_from(round_value.clone().expect_u128().ok()?).ok()?; - let reward_cycle = - u64::try_from(payload.function_args.get(3)?.clone().expect_u128().ok()?).ok()?; - Some((signer_index, point, round, reward_cycle)) - } -} - -#[cfg(test)] -mod tests { - use std::thread::spawn; - - use blockstack_lib::chainstate::stacks::boot::SIGNERS_VOTING_NAME; - use blockstack_lib::chainstate::stacks::{ - StacksTransaction, TransactionAnchorMode, TransactionAuth, TransactionPayload, - TransactionPostConditionMode, TransactionSmartContract, TransactionVersion, - }; - use blockstack_lib::util_lib::boot::boot_code_id; - use blockstack_lib::util_lib::strings::StacksString; - use clarity::vm::Value; - use rand::thread_rng; - use rand_core::RngCore; - use serial_test::serial; - use stacks_common::consts::CHAIN_ID_TESTNET; - use stacks_common::types::chainstate::StacksPrivateKey; - use wsts::curve::point::Point; - use wsts::curve::scalar::Scalar; - - use crate::client::tests::{ - build_account_nonce_response, build_get_approved_aggregate_key_response, - build_get_last_round_response, generate_signer_config, mock_server_from_config, - mock_server_from_config_and_write_response, write_response, - }; - use crate::client::{StacksClient, VOTE_FUNCTION_NAME}; - use crate::config::GlobalConfig; - use crate::signer::Signer; - - #[test] - fn filter_invalid_transaction_bad_origin_id() { - let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); - let signer_config = generate_signer_config(&config, 2, 20); - let signer = Signer::from(signer_config.clone()); - let stacks_client = StacksClient::from(&config); - let signer_private_key = StacksPrivateKey::new(); - let invalid_tx = StacksTransaction { - version: TransactionVersion::Testnet, - chain_id: CHAIN_ID_TESTNET, - auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), - anchor_mode: TransactionAnchorMode::Any, - post_condition_mode: TransactionPostConditionMode::Allow, - post_conditions: vec![], - payload: TransactionPayload::SmartContract( - TransactionSmartContract { - name: "test-contract".into(), - code_body: StacksString::from_str("(/ 1 0)").unwrap(), - }, - None, - ), - }; - assert!(signer - .filter_invalid_transactions(&stacks_client, 0, &signer.signer_slot_ids, invalid_tx) - .is_none()); - } - - #[test] - #[serial] - fn filter_invalid_transaction_bad_nonce() { - let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); - let signer_config = generate_signer_config(&config, 2, 20); - let signer = Signer::from(signer_config.clone()); - let stacks_client = StacksClient::from(&config); - let signer_private_key = config.stacks_private_key; - let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, signer.mainnet); - let contract_addr = vote_contract_id.issuer.into(); - let contract_name = vote_contract_id.name.clone(); - let signer_index = Value::UInt(signer.signer_id as u128); - let point = Point::from(Scalar::random(&mut thread_rng())); - let point_arg = - Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); - let round = thread_rng().next_u64(); - let round_arg = Value::UInt(round as u128); - let reward_cycle_arg = Value::UInt(signer.reward_cycle as u128); - let valid_function_args = vec![ - signer_index.clone(), - point_arg.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ]; - let invalid_tx = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &valid_function_args, - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 0, // Old nonce - 10, - ) - .unwrap(); - - let h = spawn(move || { - signer.filter_invalid_transactions( - &stacks_client, - 0, - &signer.signer_slot_ids, - invalid_tx, - ) - }); - - let response = build_account_nonce_response(1); - let mock_server = mock_server_from_config(&config); - write_response(mock_server, response.as_bytes()); - assert!(h.join().unwrap().is_none()); - } - - #[test] - #[serial] - fn verify_valid_transaction() { - // Create a runloop of a valid signer - let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); - let mut signer_config = generate_signer_config(&config, 5, 20); - signer_config.reward_cycle = 1; - - // valid transaction - let signer = Signer::from(signer_config.clone()); - let stacks_client = StacksClient::from(&config); - - let signer_private_key = config.stacks_private_key; - let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, signer.mainnet); - let contract_addr = vote_contract_id.issuer.into(); - let contract_name = vote_contract_id.name.clone(); - let signer_index = Value::UInt(signer.signer_id as u128); - let point = Point::from(Scalar::random(&mut thread_rng())); - let point_arg = - Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); - let round = thread_rng().next_u64(); - let round_arg = Value::UInt(round as u128); - let reward_cycle_arg = Value::UInt(signer.reward_cycle as u128); - let valid_function_args = vec![ - signer_index.clone(), - point_arg.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ]; - let valid_transaction = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &valid_function_args, - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - let vote_response = build_get_approved_aggregate_key_response(None); - let last_round_response = build_get_last_round_response(round); - - let h = spawn(move || { - assert!(signer - .verify_payload( - &stacks_client, - &valid_transaction, - signer.signer_id, - signer.reward_cycle.saturating_sub(1) - ) - .unwrap()) - }); - - let mock_server = mock_server_from_config(&config); - write_response(mock_server, vote_response.as_bytes()); - - let mock_server = mock_server_from_config(&config); - write_response(mock_server, last_round_response.as_bytes()); - - h.join().unwrap(); - } - - #[test] - #[serial] - fn verify_transaction_filters_malformed_contract_calls() { - // Create a runloop of a valid signer - let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); - let mut signer_config = generate_signer_config(&config, 5, 20); - signer_config.reward_cycle = 1; - - let signer = Signer::from(signer_config.clone()); - - let signer_private_key = config.stacks_private_key; - let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, signer.mainnet); - let contract_addr = vote_contract_id.issuer.into(); - let contract_name = vote_contract_id.name.clone(); - let signer_index = Value::UInt(signer.signer_id as u128); - let point = Point::from(Scalar::random(&mut thread_rng())); - let point_arg = - Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); - let round = thread_rng().next_u64(); - let round_arg = Value::UInt(round as u128); - let reward_cycle_arg = Value::UInt(signer.reward_cycle as u128); - let valid_function_args = vec![ - signer_index.clone(), - point_arg.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ]; - - let signer = Signer::from(signer_config.clone()); - // Create a invalid transaction that is not a contract call - let invalid_not_contract_call = StacksTransaction { - version: TransactionVersion::Testnet, - chain_id: CHAIN_ID_TESTNET, - auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), - anchor_mode: TransactionAnchorMode::Any, - post_condition_mode: TransactionPostConditionMode::Allow, - post_conditions: vec![], - payload: TransactionPayload::SmartContract( - TransactionSmartContract { - name: "test-contract".into(), - code_body: StacksString::from_str("(/ 1 0)").unwrap(), - }, - None, - ), - }; - let invalid_signers_contract_addr = StacksClient::build_signed_contract_call_transaction( - &config.stacks_address, // Not the signers contract address - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &valid_function_args, - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - let invalid_signers_contract_name = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - "bad-signers-contract-name".into(), - VOTE_FUNCTION_NAME.into(), - &valid_function_args, - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - let invalid_signers_vote_function = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - "some-other-function".into(), - &valid_function_args, - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - let invalid_signer_id_argument = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &[ - Value::UInt(signer.signer_id.wrapping_add(1) as u128), // Not the signers id - point_arg.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ], - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - let invalid_function_arg_signer_index = - StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &[ - point_arg.clone(), - point_arg.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ], - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - let invalid_function_arg_key = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &[ - signer_index.clone(), - signer_index.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ], - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - let invalid_function_arg_round = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &[ - signer_index.clone(), - point_arg.clone(), - point_arg.clone(), - reward_cycle_arg.clone(), - ], - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - let invalid_function_arg_reward_cycle = - StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &[ - signer_index.clone(), - point_arg.clone(), - round_arg.clone(), - point_arg.clone(), - ], - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - let stacks_client = StacksClient::from(&config); - for tx in vec![ - invalid_not_contract_call, - invalid_signers_contract_addr, - invalid_signers_contract_name, - invalid_signers_vote_function, - invalid_signer_id_argument, - invalid_function_arg_signer_index, - invalid_function_arg_key, - invalid_function_arg_round, - invalid_function_arg_reward_cycle, - ] { - let result = signer - .verify_payload( - &stacks_client, - &tx, - signer.signer_id, - signer.reward_cycle.saturating_sub(1), - ) - .unwrap(); - assert!(!result); - } - } - - #[test] - #[serial] - fn verify_transaction_filters_invalid_reward_cycle() { - // Create a runloop of a valid signer - let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); - let mut signer_config = generate_signer_config(&config, 5, 20); - signer_config.reward_cycle = 1; - - let signer = Signer::from(signer_config.clone()); - - let stacks_client = StacksClient::from(&config); - let signer_private_key = config.stacks_private_key; - let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, signer.mainnet); - let contract_addr = vote_contract_id.issuer.into(); - let contract_name = vote_contract_id.name.clone(); - let signer_index = Value::UInt(signer.signer_id as u128); - let point = Point::from(Scalar::random(&mut thread_rng())); - let point_arg = - Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); - let round = thread_rng().next_u64(); - let round_arg = Value::UInt(round as u128); - let reward_cycle_arg = Value::UInt(signer.reward_cycle as u128); - let valid_function_args = vec![ - signer_index.clone(), - point_arg.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ]; - // Invalid reward cycle (voting for the current is not allowed. only the next) - let signer = Signer::from(signer_config.clone()); - let invalid_reward_cycle = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &valid_function_args, - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - let h = spawn(move || { - assert!(!signer - .verify_payload( - &stacks_client, - &invalid_reward_cycle, - signer.signer_id, - signer.reward_cycle - ) - .unwrap()) - }); - h.join().unwrap(); - } - - #[test] - #[serial] - fn verify_transaction_filters_already_voted() { - // Create a runloop of a valid signer - let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); - let mut signer_config = generate_signer_config(&config, 5, 20); - signer_config.reward_cycle = 1; - - let signer = Signer::from(signer_config.clone()); - - let signer_private_key = config.stacks_private_key; - let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, signer.mainnet); - let contract_addr = vote_contract_id.issuer.into(); - let contract_name = vote_contract_id.name.clone(); - let signer_index = Value::UInt(signer.signer_id as u128); - let point = Point::from(Scalar::random(&mut thread_rng())); - let point_arg = - Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); - let round = thread_rng().next_u64(); - let round_arg = Value::UInt(round as u128); - let reward_cycle_arg = Value::UInt(signer.reward_cycle as u128); - let valid_function_args = vec![ - signer_index.clone(), - point_arg.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ]; - - // Already voted - let signer = Signer::from(signer_config.clone()); - let stacks_client = StacksClient::from(&config); - let invalid_already_voted = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &valid_function_args, - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - let vote_response = build_get_approved_aggregate_key_response(Some(point)); - - let h = spawn(move || { - assert!(!signer - .verify_payload( - &stacks_client, - &invalid_already_voted, - signer.signer_id, - signer.reward_cycle.saturating_sub(1) - ) - .unwrap()) - }); - mock_server_from_config_and_write_response(&config, vote_response.as_bytes()); - h.join().unwrap(); - } - - #[test] - #[serial] - fn verify_transaction_filters_ivalid_round_number() { - // Create a runloop of a valid signer - let config = GlobalConfig::load_from_file("./src/tests/conf/signer-0.toml").unwrap(); - let mut signer_config = generate_signer_config(&config, 5, 20); - signer_config.reward_cycle = 1; - - let signer = Signer::from(signer_config.clone()); - - let signer_private_key = config.stacks_private_key; - let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, signer.mainnet); - let contract_addr = vote_contract_id.issuer.into(); - let contract_name = vote_contract_id.name.clone(); - let signer_index = Value::UInt(signer.signer_id as u128); - let point = Point::from(Scalar::random(&mut thread_rng())); - let point_arg = - Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); - let round = thread_rng().next_u64(); - let round_arg = Value::UInt(round as u128); - let reward_cycle_arg = Value::UInt(signer.reward_cycle as u128); - let valid_function_args = vec![ - signer_index.clone(), - point_arg.clone(), - round_arg.clone(), - reward_cycle_arg.clone(), - ]; - let signer = Signer::from(signer_config.clone()); - let stacks_client = StacksClient::from(&config); - let invalid_round_number = StacksClient::build_signed_contract_call_transaction( - &contract_addr, - contract_name.clone(), - VOTE_FUNCTION_NAME.into(), - &valid_function_args, - &signer_private_key, - TransactionVersion::Testnet, - config.network.to_chain_id(), - 1, - 10, - ) - .unwrap(); - - // invalid round number - let vote_response = build_get_approved_aggregate_key_response(None); - let last_round_response = build_get_last_round_response(0); - - let h = spawn(move || { - assert!(!signer - .verify_payload( - &stacks_client, - &invalid_round_number, - signer.signer_id, - signer.reward_cycle.saturating_sub(1) - ) - .unwrap()) - }); - mock_server_from_config_and_write_response(&config, vote_response.as_bytes()); - mock_server_from_config_and_write_response(&config, last_round_response.as_bytes()); - h.join().unwrap(); - } } diff --git a/stacks-signer/src/tests/conf/signer-0.toml b/stacks-signer/src/tests/conf/signer-0.toml index 42083c30a7..2c30d82326 100644 --- a/stacks-signer/src/tests/conf/signer-0.toml +++ b/stacks-signer/src/tests/conf/signer-0.toml @@ -1,4 +1,3 @@ - stacks_private_key = "6a1fc1a3183018c6d79a4e11e154d2bdad2d89ac8bc1b0a021de8b4d28774fbb01" node_host = "127.0.0.1:20443" endpoint = "localhost:30000" diff --git a/stacks-signer/src/tests/conf/signer-1.toml b/stacks-signer/src/tests/conf/signer-1.toml index 38897ae48c..99facfc1d2 100644 --- a/stacks-signer/src/tests/conf/signer-1.toml +++ b/stacks-signer/src/tests/conf/signer-1.toml @@ -1,5 +1,4 @@ - stacks_private_key = "126e916e77359ccf521e168feea1fcb9626c59dc375cae00c7464303381c7dff01" -node_host = "127.0.0.1:20443" +node_host = "127.0.0.1:20444" endpoint = "localhost:30001" network = "testnet" diff --git a/stacks-signer/src/tests/conf/signer-2.toml b/stacks-signer/src/tests/conf/signer-2.toml deleted file mode 100644 index 9235b2e076..0000000000 --- a/stacks-signer/src/tests/conf/signer-2.toml +++ /dev/null @@ -1,5 +0,0 @@ - -stacks_private_key = "b169d0d1408f66d16beb321857f525f9014dfc289f1aeedbcf96e78afeb8eb4001" -node_host = "127.0.0.1:20443" -endpoint = "localhost:30002" -network = "testnet" diff --git a/stacks-signer/src/tests/conf/signer-3.toml b/stacks-signer/src/tests/conf/signer-3.toml deleted file mode 100644 index b96eef0098..0000000000 --- a/stacks-signer/src/tests/conf/signer-3.toml +++ /dev/null @@ -1,5 +0,0 @@ - -stacks_private_key = "63cef3cd8880969b7f2450ca13b9ca57fd3cd3f7ee57ec6ed7654a84d39181e401" -node_host = "127.0.0.1:20443" -endpoint = "localhost:30003" -network = "testnet" diff --git a/stackslib/src/blockstack_cli.rs b/stackslib/src/blockstack_cli.rs index d708383f14..6fb9f45ed6 100644 --- a/stackslib/src/blockstack_cli.rs +++ b/stackslib/src/blockstack_cli.rs @@ -23,7 +23,6 @@ extern crate blockstack_lib; extern crate clarity; extern crate stacks_common; -use std::convert::TryFrom; use std::io::prelude::*; use std::io::Read; use std::{env, fs, io}; diff --git a/stackslib/src/burnchains/affirmation.rs b/stackslib/src/burnchains/affirmation.rs index bfe59ed274..1eb4874e42 100644 --- a/stackslib/src/burnchains/affirmation.rs +++ b/stackslib/src/burnchains/affirmation.rs @@ -232,7 +232,6 @@ /// use std::cmp; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use std::fmt; use std::fmt::Write; use std::sync::mpsc::SyncSender; diff --git a/stackslib/src/burnchains/bitcoin/indexer.rs b/stackslib/src/burnchains/bitcoin/indexer.rs index 2d3e981e27..51de78a53f 100644 --- a/stackslib/src/burnchains/bitcoin/indexer.rs +++ b/stackslib/src/burnchains/bitcoin/indexer.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; use std::net::Shutdown; use std::ops::{Deref, DerefMut}; use std::path::PathBuf; diff --git a/stackslib/src/burnchains/burnchain.rs b/stackslib/src/burnchains/burnchain.rs index d5f33a5ea7..e3947bd5a8 100644 --- a/stackslib/src/burnchains/burnchain.rs +++ b/stackslib/src/burnchains/burnchain.rs @@ -15,8 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; -use std::marker::Send; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; use std::sync::mpsc::sync_channel; diff --git a/stackslib/src/burnchains/mod.rs b/stackslib/src/burnchains/mod.rs index 8734d605d8..c6e4204542 100644 --- a/stackslib/src/burnchains/mod.rs +++ b/stackslib/src/burnchains/mod.rs @@ -15,8 +15,6 @@ // along with this program. If not, see . use std::collections::HashMap; -use std::convert::TryFrom; -use std::default::Default; use std::marker::PhantomData; use std::{error, fmt, io}; diff --git a/stackslib/src/burnchains/tests/db.rs b/stackslib/src/burnchains/tests/db.rs index 13db19bcf7..7b2a87be4c 100644 --- a/stackslib/src/burnchains/tests/db.rs +++ b/stackslib/src/burnchains/tests/db.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::cmp; -use std::convert::TryInto; use stacks_common::address::AddressHashMode; use stacks_common::deps_common::bitcoin::blockdata::transaction::Transaction as BtcTx; diff --git a/stackslib/src/chainstate/burn/db/sortdb.rs b/stackslib/src/chainstate/burn/db/sortdb.rs index 86dffd5723..d027f6ffd9 100644 --- a/stackslib/src/chainstate/burn/db/sortdb.rs +++ b/stackslib/src/chainstate/burn/db/sortdb.rs @@ -14,9 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::cmp::{Ord, Ordering}; +use std::cmp::Ordering; use std::collections::{HashMap, HashSet}; -use std::convert::{From, TryFrom, TryInto}; use std::io::{ErrorKind, Write}; use std::ops::{Deref, DerefMut}; use std::str::FromStr; diff --git a/stackslib/src/chainstate/burn/distribution.rs b/stackslib/src/chainstate/burn/distribution.rs index b9685a9a99..2a16897100 100644 --- a/stackslib/src/chainstate/burn/distribution.rs +++ b/stackslib/src/chainstate/burn/distribution.rs @@ -16,7 +16,6 @@ use std::cmp; use std::collections::{BTreeMap, HashMap}; -use std::convert::TryInto; use stacks_common::address::AddressHashMode; use stacks_common::util::hash::Hash160; diff --git a/stackslib/src/chainstate/burn/mod.rs b/stackslib/src/chainstate/burn/mod.rs index 8031762355..13f290d93b 100644 --- a/stackslib/src/chainstate/burn/mod.rs +++ b/stackslib/src/chainstate/burn/mod.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; use std::fmt; use std::io::Write; diff --git a/stackslib/src/chainstate/burn/operations/mod.rs b/stackslib/src/chainstate/burn/operations/mod.rs index e7c48cb1cb..e51a20f630 100644 --- a/stackslib/src/chainstate/burn/operations/mod.rs +++ b/stackslib/src/chainstate/burn/operations/mod.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{From, TryInto}; use std::{error, fmt, fs, io}; use clarity::vm::types::PrincipalData; diff --git a/stackslib/src/chainstate/coordinator/mod.rs b/stackslib/src/chainstate/coordinator/mod.rs index 6211c74d97..324d42f483 100644 --- a/stackslib/src/chainstate/coordinator/mod.rs +++ b/stackslib/src/chainstate/coordinator/mod.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use std::path::PathBuf; use std::sync::atomic::AtomicBool; use std::sync::mpsc::SyncSender; diff --git a/stackslib/src/chainstate/nakamoto/signer_set.rs b/stackslib/src/chainstate/nakamoto/signer_set.rs index c0d6b23717..5049286908 100644 --- a/stackslib/src/chainstate/nakamoto/signer_set.rs +++ b/stackslib/src/chainstate/nakamoto/signer_set.rs @@ -46,7 +46,7 @@ use stacks_common::util::hash::{to_hex, Hash160, MerkleHashFunc, MerkleTree, Sha use stacks_common::util::retry::BoundReader; use stacks_common::util::secp256k1::MessageSignature; use stacks_common::util::vrf::{VRFProof, VRFPublicKey, VRF}; -use wsts::curve::point::Point; +use wsts::curve::point::{Compressed, Point}; use crate::burnchains::{Burnchain, PoxConstants, Txid}; use crate::chainstate::burn::db::sortdb::{ @@ -63,7 +63,7 @@ use crate::chainstate::stacks::address::PoxAddress; use crate::chainstate::stacks::boot::{ PoxVersions, RawRewardSetEntry, RewardSet, BOOT_TEST_POX_4_AGG_KEY_CONTRACT, BOOT_TEST_POX_4_AGG_KEY_FNAME, POX_4_NAME, SIGNERS_MAX_LIST_SIZE, SIGNERS_NAME, SIGNERS_PK_LEN, - SIGNERS_UPDATE_STATE, + SIGNERS_UPDATE_STATE, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME, }; use crate::chainstate::stacks::db::{ ChainstateTx, ClarityTx, DBConfig as ChainstateConfig, MinerPaymentSchedule, @@ -99,6 +99,13 @@ pub struct SignerCalculation { pub events: Vec, } +pub struct AggregateKeyVoteParams { + pub signer_index: u64, + pub aggregate_key: Point, + pub voting_round: u64, + pub reward_cycle: u64, +} + impl RawRewardSetEntry { pub fn from_pox_4_tuple(is_mainnet: bool, tuple: TupleData) -> Result { let mut tuple_data = tuple.data_map; @@ -487,4 +494,89 @@ impl NakamotoSigners { } Ok(signers) } + + /// Verify that the transaction is a valid vote for the aggregate public key + /// Note: it does not verify the function arguments, only that the transaction is validly formed + /// and has a valid nonce from an expected address + pub fn valid_vote_transaction( + account_nonces: &HashMap, + transaction: &StacksTransaction, + is_mainnet: bool, + ) -> bool { + let origin_address = transaction.origin_address(); + let origin_nonce = transaction.get_origin_nonce(); + let Some(account_nonce) = account_nonces.get(&origin_address) else { + debug!("valid_vote_transaction: Unrecognized origin address ({origin_address}).",); + return false; + }; + if transaction.is_mainnet() != is_mainnet { + debug!("valid_vote_transaction: Received a transaction for an unexpected network.",); + return false; + } + if origin_nonce < *account_nonce { + debug!("valid_vote_transaction: Received a transaction with an outdated nonce ({account_nonce} < {origin_nonce})."); + return false; + } + Self::parse_vote_for_aggregate_public_key(transaction).is_some() + } + + pub fn parse_vote_for_aggregate_public_key( + transaction: &StacksTransaction, + ) -> Option { + let TransactionPayload::ContractCall(payload) = &transaction.payload else { + // Not a contract call so not a special cased vote for aggregate public key transaction + return None; + }; + if payload.contract_identifier() + != boot_code_id(SIGNERS_VOTING_NAME, transaction.is_mainnet()) + || payload.function_name != SIGNERS_VOTING_FUNCTION_NAME.into() + { + // This is not a special cased transaction. + return None; + } + if payload.function_args.len() != 4 { + return None; + } + let signer_index_value = payload.function_args.first()?; + let signer_index = u64::try_from(signer_index_value.clone().expect_u128().ok()?).ok()?; + let point_value = payload.function_args.get(1)?; + let point_bytes = point_value.clone().expect_buff(33).ok()?; + let compressed_data = Compressed::try_from(point_bytes.as_slice()).ok()?; + let aggregate_key = Point::try_from(&compressed_data).ok()?; + let round_value = payload.function_args.get(2)?; + let voting_round = u64::try_from(round_value.clone().expect_u128().ok()?).ok()?; + let reward_cycle = + u64::try_from(payload.function_args.get(3)?.clone().expect_u128().ok()?).ok()?; + Some(AggregateKeyVoteParams { + signer_index, + aggregate_key, + voting_round, + reward_cycle, + }) + } + + /// Update the map of filtered valid transactions, selecting one per address based first on lowest nonce, then txid + pub fn update_filtered_transactions( + filtered_transactions: &mut HashMap, + account_nonces: &HashMap, + mainnet: bool, + transactions: Vec, + ) { + for transaction in transactions { + if NakamotoSigners::valid_vote_transaction(&account_nonces, &transaction, mainnet) { + let origin_address = transaction.origin_address(); + let origin_nonce = transaction.get_origin_nonce(); + if let Some(entry) = filtered_transactions.get_mut(&origin_address) { + let entry_nonce = entry.get_origin_nonce(); + if entry_nonce > origin_nonce + || (entry_nonce == origin_nonce && entry.txid() > transaction.txid()) + { + *entry = transaction; + } + } else { + filtered_transactions.insert(origin_address, transaction); + } + } + } + } } diff --git a/stackslib/src/chainstate/nakamoto/tests/mod.rs b/stackslib/src/chainstate/nakamoto/tests/mod.rs index 284a0af64d..28d620b814 100644 --- a/stackslib/src/chainstate/nakamoto/tests/mod.rs +++ b/stackslib/src/chainstate/nakamoto/tests/mod.rs @@ -15,17 +15,22 @@ // along with this program. If not, see . use std::borrow::BorrowMut; +use std::collections::HashMap; use std::fs; use clarity::types::chainstate::{PoxId, SortitionId, StacksBlockId}; use clarity::vm::clarity::ClarityConnection; use clarity::vm::costs::ExecutionCost; use clarity::vm::types::StacksAddressExtensions; +use clarity::vm::Value; +use rand::{thread_rng, RngCore}; use rusqlite::Connection; use stacks_common::address::AddressHashMode; use stacks_common::bitvec::BitVec; use stacks_common::codec::StacksMessageCodec; -use stacks_common::consts::{FIRST_BURNCHAIN_CONSENSUS_HASH, FIRST_STACKS_BLOCK_HASH}; +use stacks_common::consts::{ + CHAIN_ID_MAINNET, CHAIN_ID_TESTNET, FIRST_BURNCHAIN_CONSENSUS_HASH, FIRST_STACKS_BLOCK_HASH, +}; use stacks_common::types::chainstate::{ BlockHeaderHash, BurnchainHeaderHash, ConsensusHash, StacksAddress, StacksPrivateKey, StacksPublicKey, StacksWorkScore, TrieHash, VRFSeed, @@ -37,6 +42,8 @@ use stacks_common::util::secp256k1::{MessageSignature, Secp256k1PublicKey}; use stacks_common::util::vrf::{VRFPrivateKey, VRFProof, VRFPublicKey, VRF}; use stdext::prelude::Integer; use stx_genesis::GenesisData; +use wsts::curve::point::Point; +use wsts::curve::scalar::Scalar; use crate::burnchains::{BurnchainSigner, PoxConstants, Txid}; use crate::chainstate::burn::db::sortdb::tests::make_fork_run; @@ -52,13 +59,16 @@ use crate::chainstate::coordinator::tests::{ }; use crate::chainstate::nakamoto::coordinator::tests::boot_nakamoto; use crate::chainstate::nakamoto::miner::NakamotoBlockBuilder; +use crate::chainstate::nakamoto::signer_set::NakamotoSigners; use crate::chainstate::nakamoto::tenure::NakamotoTenure; use crate::chainstate::nakamoto::test_signers::TestSigners; use crate::chainstate::nakamoto::tests::node::TestStacker; use crate::chainstate::nakamoto::{ NakamotoBlock, NakamotoBlockHeader, NakamotoChainState, SortitionHandle, FIRST_STACKS_BLOCK_ID, }; -use crate::chainstate::stacks::boot::MINERS_NAME; +use crate::chainstate::stacks::boot::{ + MINERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME, +}; use crate::chainstate::stacks::db::{ ChainStateBootData, ChainstateAccountBalance, ChainstateAccountLockup, ChainstateBNSName, ChainstateBNSNamespace, StacksAccount, StacksBlockHeaderTypes, StacksChainState, @@ -67,13 +77,15 @@ use crate::chainstate::stacks::db::{ use crate::chainstate::stacks::{ CoinbasePayload, StacksBlock, StacksBlockHeader, StacksTransaction, StacksTransactionSigner, TenureChangeCause, TenureChangePayload, ThresholdSignature, TokenTransferMemo, - TransactionAnchorMode, TransactionAuth, TransactionPayload, TransactionVersion, + TransactionAnchorMode, TransactionAuth, TransactionContractCall, TransactionPayload, + TransactionPostConditionMode, TransactionSmartContract, TransactionVersion, }; use crate::core; use crate::core::{StacksEpochExtension, STACKS_EPOCH_3_0_MARKER}; use crate::net::codec::test::check_codec_and_corruption; use crate::util_lib::boot::boot_code_id; use crate::util_lib::db::Error as db_error; +use crate::util_lib::strings::StacksString; /// Get an address's account pub fn get_account( @@ -2068,3 +2080,743 @@ fn test_make_miners_stackerdb_config() { assert_eq!(miner_hashbytes[8].1, miner_hash160s[8]); assert_eq!(miner_hashbytes[9].1, miner_hash160s[8]); } + +#[test] +fn parse_vote_for_aggregate_public_key_valid() { + let signer_private_key = StacksPrivateKey::new(); + let mainnet = false; + let chainid = CHAIN_ID_TESTNET; + let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, mainnet); + let contract_addr = vote_contract_id.issuer.into(); + let contract_name = vote_contract_id.name.clone(); + + let signer_index = thread_rng().next_u64(); + let signer_index_arg = Value::UInt(signer_index as u128); + + let point = Point::from(Scalar::random(&mut thread_rng())); + let point_arg = + Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); + let round = thread_rng().next_u64(); + let round_arg = Value::UInt(round as u128); + + let reward_cycle = thread_rng().next_u64(); + let reward_cycle_arg = Value::UInt(reward_cycle as u128); + + let valid_function_args = vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ]; + let valid_tx = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr, + contract_name, + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: valid_function_args, + }), + }; + let params = NakamotoSigners::parse_vote_for_aggregate_public_key(&valid_tx).unwrap(); + assert_eq!(params.signer_index, signer_index); + assert_eq!(params.aggregate_key, point); + assert_eq!(params.voting_round, round); + assert_eq!(params.reward_cycle, reward_cycle); +} + +#[test] +fn parse_vote_for_aggregate_public_key_invalid() { + let signer_private_key = StacksPrivateKey::new(); + let mainnet = false; + let chainid = CHAIN_ID_TESTNET; + let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, mainnet); + let contract_addr: StacksAddress = vote_contract_id.issuer.into(); + let contract_name = vote_contract_id.name.clone(); + + let signer_index = thread_rng().next_u32(); + let signer_index_arg = Value::UInt(signer_index as u128); + + let point = Point::from(Scalar::random(&mut thread_rng())); + let point_arg = + Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); + let round = thread_rng().next_u64(); + let round_arg = Value::UInt(round as u128); + + let reward_cycle = thread_rng().next_u64(); + let reward_cycle_arg = Value::UInt(reward_cycle as u128); + + let valid_function_args = vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ]; + + let mut invalid_contract_address = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: StacksAddress::p2pkh( + false, + &StacksPublicKey::from_private(&signer_private_key), + ), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: valid_function_args.clone(), + }), + }; + invalid_contract_address.set_origin_nonce(1); + + let mut invalid_contract_name = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: "bad-signers-contract-name".into(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: valid_function_args.clone(), + }), + }; + invalid_contract_name.set_origin_nonce(1); + + let mut invalid_signers_vote_function = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: "some-other-function".into(), + function_args: valid_function_args.clone(), + }), + }; + invalid_signers_vote_function.set_origin_nonce(1); + + let mut invalid_function_arg_signer_index = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: vec![ + point_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ], + }), + }; + invalid_function_arg_signer_index.set_origin_nonce(1); + + let mut invalid_function_arg_key = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: vec![ + signer_index_arg.clone(), + signer_index_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ], + }), + }; + invalid_function_arg_key.set_origin_nonce(1); + + let mut invalid_function_arg_round = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: vec![ + signer_index_arg.clone(), + point_arg.clone(), + point_arg.clone(), + reward_cycle_arg.clone(), + ], + }), + }; + invalid_function_arg_round.set_origin_nonce(1); + + let mut invalid_function_arg_reward_cycle = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + point_arg.clone(), + ], + }), + }; + invalid_function_arg_reward_cycle.set_origin_nonce(1); + + let mut account_nonces = std::collections::HashMap::new(); + account_nonces.insert(invalid_contract_name.origin_address(), 1); + for (i, tx) in vec![ + invalid_contract_address, + invalid_contract_name, + invalid_signers_vote_function, + invalid_function_arg_signer_index, + invalid_function_arg_key, + invalid_function_arg_round, + invalid_function_arg_reward_cycle, + ] + .iter() + .enumerate() + { + assert!( + NakamotoSigners::parse_vote_for_aggregate_public_key(&tx).is_none(), + "{}", + format!("parsed the {i}th transaction: {tx:?}") + ); + } +} + +#[test] +fn valid_vote_transaction() { + let signer_private_key = StacksPrivateKey::new(); + let mainnet = false; + let chainid = CHAIN_ID_TESTNET; + let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, mainnet); + let contract_addr = vote_contract_id.issuer.into(); + let contract_name = vote_contract_id.name.clone(); + + let signer_index = thread_rng().next_u32(); + let signer_index_arg = Value::UInt(signer_index as u128); + + let point = Point::from(Scalar::random(&mut thread_rng())); + let point_arg = + Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); + let round = thread_rng().next_u64(); + let round_arg = Value::UInt(round as u128); + + let reward_cycle = thread_rng().next_u64(); + let reward_cycle_arg = Value::UInt(reward_cycle as u128); + + let valid_function_args = vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ]; + let mut valid_tx = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr, + contract_name: contract_name, + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: valid_function_args, + }), + }; + valid_tx.set_origin_nonce(1); + let mut account_nonces = std::collections::HashMap::new(); + account_nonces.insert(valid_tx.origin_address(), 1); + assert!(NakamotoSigners::valid_vote_transaction( + &account_nonces, + &valid_tx, + mainnet + )); +} + +#[test] +fn valid_vote_transaction_malformed_transactions() { + let signer_private_key = StacksPrivateKey::new(); + let mainnet = false; + let chainid = CHAIN_ID_TESTNET; + let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, mainnet); + let contract_addr: StacksAddress = vote_contract_id.issuer.into(); + let contract_name = vote_contract_id.name.clone(); + + let signer_index = thread_rng().next_u32(); + let signer_index_arg = Value::UInt(signer_index as u128); + + let point = Point::from(Scalar::random(&mut thread_rng())); + let point_arg = + Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); + let round = thread_rng().next_u64(); + let round_arg = Value::UInt(round as u128); + + let reward_cycle = thread_rng().next_u64(); + let reward_cycle_arg = Value::UInt(reward_cycle as u128); + + let valid_function_args = vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ]; + // Create a invalid transaction that is not a contract call + let mut invalid_not_contract_call = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::SmartContract( + TransactionSmartContract { + name: "test-contract".into(), + code_body: StacksString::from_str("(/ 1 0)").unwrap(), + }, + None, + ), + }; + invalid_not_contract_call.set_origin_nonce(1); + + let mut invalid_contract_address = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: StacksAddress::p2pkh( + mainnet, + &StacksPublicKey::from_private(&signer_private_key), + ), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: valid_function_args.clone(), + }), + }; + invalid_contract_address.set_origin_nonce(1); + + let mut invalid_contract_name = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: "bad-signers-contract-name".into(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: valid_function_args.clone(), + }), + }; + invalid_contract_name.set_origin_nonce(1); + + let mut invalid_network = StacksTransaction { + version: TransactionVersion::Mainnet, + chain_id: CHAIN_ID_MAINNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: valid_function_args.clone(), + }), + }; + invalid_network.set_origin_nonce(1); + + let mut invalid_signers_vote_function = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: "some-other-function".into(), + function_args: valid_function_args.clone(), + }), + }; + invalid_signers_vote_function.set_origin_nonce(1); + + let mut invalid_function_arg_signer_index = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: vec![ + point_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ], + }), + }; + invalid_function_arg_signer_index.set_origin_nonce(1); + + let mut invalid_function_arg_key = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: vec![ + signer_index_arg.clone(), + signer_index_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ], + }), + }; + invalid_function_arg_key.set_origin_nonce(1); + + let mut invalid_function_arg_round = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: vec![ + signer_index_arg.clone(), + point_arg.clone(), + point_arg.clone(), + reward_cycle_arg.clone(), + ], + }), + }; + invalid_function_arg_round.set_origin_nonce(1); + + let mut invalid_function_arg_reward_cycle = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + point_arg.clone(), + ], + }), + }; + invalid_function_arg_reward_cycle.set_origin_nonce(1); + + let mut invalid_nonce = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: valid_function_args.clone(), + }), + }; + invalid_nonce.set_origin_nonce(0); // old nonce + + let mut account_nonces = std::collections::HashMap::new(); + account_nonces.insert(invalid_not_contract_call.origin_address(), 1); + for tx in vec![ + invalid_not_contract_call, + invalid_contract_address, + invalid_contract_name, + invalid_signers_vote_function, + invalid_function_arg_signer_index, + invalid_function_arg_key, + invalid_function_arg_round, + invalid_function_arg_reward_cycle, + invalid_nonce, + invalid_network, + ] { + assert!(!NakamotoSigners::valid_vote_transaction( + &account_nonces, + &tx, + mainnet + )); + } +} + +#[test] +fn filter_one_transaction_per_signer_multiple_addresses() { + let signer_private_key_1 = StacksPrivateKey::new(); + let signer_private_key_2 = StacksPrivateKey::new(); + let mainnet = false; + let chainid = CHAIN_ID_TESTNET; + let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, mainnet); + let contract_addr: StacksAddress = vote_contract_id.issuer.into(); + let contract_name = vote_contract_id.name.clone(); + + let signer_index = thread_rng().next_u32(); + let signer_index_arg = Value::UInt(signer_index as u128); + + let point = Point::from(Scalar::random(&mut thread_rng())); + let point_arg = + Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); + let round = thread_rng().next_u64(); + let round_arg = Value::UInt(round as u128); + + let reward_cycle = thread_rng().next_u64(); + let reward_cycle_arg = Value::UInt(reward_cycle as u128); + + let function_args = vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ]; + + let mut valid_tx_1_address_1 = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key_1).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: function_args.clone(), + }), + }; + valid_tx_1_address_1.set_origin_nonce(1); + + let mut valid_tx_2_address_1 = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key_1).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: function_args.clone(), + }), + }; + valid_tx_2_address_1.set_origin_nonce(2); + + let mut valid_tx_3_address_1 = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key_1).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: function_args.clone(), + }), + }; + valid_tx_3_address_1.set_origin_nonce(3); + + let mut valid_tx_1_address_2 = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key_2).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: function_args.clone(), + }), + }; + valid_tx_1_address_2.set_origin_nonce(1); + + let mut valid_tx_2_address_2 = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key_2).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr, + contract_name, + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args, + }), + }; + valid_tx_2_address_2.set_origin_nonce(2); + let mut filtered_transactions = HashMap::new(); + let mut account_nonces = std::collections::HashMap::new(); + account_nonces.insert(valid_tx_1_address_1.origin_address(), 1); + account_nonces.insert(valid_tx_1_address_2.origin_address(), 1); + NakamotoSigners::update_filtered_transactions( + &mut filtered_transactions, + &account_nonces, + false, + vec![ + valid_tx_1_address_1.clone(), + valid_tx_3_address_1, + valid_tx_1_address_2.clone(), + valid_tx_2_address_2, + valid_tx_2_address_1, + ], + ); + let txs: Vec<_> = filtered_transactions.into_values().collect(); + assert_eq!(txs.len(), 2); + assert!(txs.contains(&valid_tx_1_address_1)); + assert!(txs.contains(&valid_tx_1_address_2)); +} + +#[test] +fn filter_one_transaction_per_signer_duplicate_nonces() { + let signer_private_key = StacksPrivateKey::new(); + let mainnet = false; + let chainid = CHAIN_ID_TESTNET; + let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, mainnet); + let contract_addr: StacksAddress = vote_contract_id.issuer.into(); + let contract_name = vote_contract_id.name.clone(); + + let signer_index = thread_rng().next_u32(); + let signer_index_arg = Value::UInt(signer_index as u128); + + let point = Point::from(Scalar::random(&mut thread_rng())); + let point_arg = + Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); + let round = thread_rng().next_u64(); + let round_arg = Value::UInt(round as u128); + + let reward_cycle = thread_rng().next_u64(); + let reward_cycle_arg = Value::UInt(reward_cycle as u128); + + let function_args = vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ]; + + let mut valid_tx_1 = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: function_args.clone(), + }), + }; + valid_tx_1.set_origin_nonce(0); + + let mut valid_tx_2 = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr.clone(), + contract_name: contract_name.clone(), + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args: function_args.clone(), + }), + }; + valid_tx_2.set_origin_nonce(0); + + let mut valid_tx_3 = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::ContractCall(TransactionContractCall { + address: contract_addr, + contract_name, + function_name: SIGNERS_VOTING_FUNCTION_NAME.into(), + function_args, + }), + }; + valid_tx_3.set_origin_nonce(0); + + let mut account_nonces = std::collections::HashMap::new(); + account_nonces.insert(valid_tx_1.origin_address(), 0); + let mut txs = vec![valid_tx_2, valid_tx_1, valid_tx_3]; + let mut filtered_transactions = HashMap::new(); + NakamotoSigners::update_filtered_transactions( + &mut filtered_transactions, + &account_nonces, + false, + txs.clone(), + ); + let filtered_txs: Vec<_> = filtered_transactions.into_values().collect(); + txs.sort_by(|a, b| a.txid().cmp(&b.txid())); + assert_eq!(filtered_txs.len(), 1); + assert!(filtered_txs.contains(&txs.first().expect("failed to get first tx"))); +} diff --git a/stackslib/src/chainstate/stacks/address.rs b/stackslib/src/chainstate/stacks/address.rs index e11730bca2..c3706a2565 100644 --- a/stackslib/src/chainstate/stacks/address.rs +++ b/stackslib/src/chainstate/stacks/address.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::cmp::{Ord, Ordering}; +use std::cmp::Ordering; use std::io::prelude::*; use std::io::{Read, Write}; use std::{fmt, io}; diff --git a/stackslib/src/chainstate/stacks/boot/contract_tests.rs b/stackslib/src/chainstate/stacks/boot/contract_tests.rs index ce12011c73..23f2c92008 100644 --- a/stackslib/src/chainstate/stacks/boot/contract_tests.rs +++ b/stackslib/src/chainstate/stacks/boot/contract_tests.rs @@ -1,5 +1,4 @@ use std::collections::{HashMap, VecDeque}; -use std::convert::{TryFrom, TryInto}; use clarity::vm::analysis::arithmetic_checker::ArithmeticOnlyChecker; use clarity::vm::analysis::mem_type_check; diff --git a/stackslib/src/chainstate/stacks/boot/docs.rs b/stackslib/src/chainstate/stacks/boot/docs.rs index 08a203122a..62580f384a 100644 --- a/stackslib/src/chainstate/stacks/boot/docs.rs +++ b/stackslib/src/chainstate/stacks/boot/docs.rs @@ -1,5 +1,3 @@ -use std::iter::FromIterator; - use clarity::vm::docs::contracts::{produce_docs_refs, ContractSupportDocs}; use hashbrown::{HashMap, HashSet}; diff --git a/stackslib/src/chainstate/stacks/boot/mod.rs b/stackslib/src/chainstate/stacks/boot/mod.rs index 7d6d141e10..834d83ed20 100644 --- a/stackslib/src/chainstate/stacks/boot/mod.rs +++ b/stackslib/src/chainstate/stacks/boot/mod.rs @@ -14,10 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::boxed::Box; use std::cmp; -use std::collections::{BTreeMap, HashMap}; -use std::convert::{TryFrom, TryInto}; +use std::collections::BTreeMap; use clarity::vm::analysis::CheckErrors; use clarity::vm::ast::ASTRules; @@ -80,6 +78,7 @@ pub const POX_3_NAME: &'static str = "pox-3"; pub const POX_4_NAME: &'static str = "pox-4"; pub const SIGNERS_NAME: &'static str = "signers"; pub const SIGNERS_VOTING_NAME: &'static str = "signers-voting"; +pub const SIGNERS_VOTING_FUNCTION_NAME: &str = "vote-for-aggregate-public-key"; /// This is the name of a variable in the `.signers` contract which tracks the most recently updated /// reward cycle number. pub const SIGNERS_UPDATE_STATE: &'static str = "last-set-cycle"; @@ -1326,7 +1325,6 @@ pub mod signers_voting_tests; #[cfg(test)] pub mod test { use std::collections::{HashMap, HashSet}; - use std::convert::From; use std::fs; use clarity::boot_util::boot_code_addr; @@ -1936,7 +1934,7 @@ pub mod test { let payload = TransactionPayload::new_contract_call( boot_code_test_addr(), SIGNERS_VOTING_NAME, - "vote-for-aggregate-public-key", + SIGNERS_VOTING_FUNCTION_NAME, vec![ Value::UInt(signer_index), aggregate_public_key, diff --git a/stackslib/src/chainstate/stacks/boot/pox-4.clar b/stackslib/src/chainstate/stacks/boot/pox-4.clar index d54f3b8d5f..77c8ef2550 100644 --- a/stackslib/src/chainstate/stacks/boot/pox-4.clar +++ b/stackslib/src/chainstate/stacks/boot/pox-4.clar @@ -32,6 +32,7 @@ (define-constant ERR_DELEGATION_ALREADY_REVOKED 34) (define-constant ERR_INVALID_SIGNATURE_PUBKEY 35) (define-constant ERR_INVALID_SIGNATURE_RECOVER 36) +(define-constant ERR_INVALID_REWARD_CYCLE 37) ;; Valid values for burnchain address versions. ;; These first four correspond to address hash modes in Stacks 2.1, @@ -1368,6 +1369,10 @@ (asserts! (is-eq (unwrap! (principal-construct? (if is-in-mainnet STACKS_ADDR_VERSION_MAINNET STACKS_ADDR_VERSION_TESTNET) (hash160 signer-key)) (err ERR_INVALID_SIGNER_KEY)) tx-sender) (err ERR_NOT_ALLOWED)) + ;; Must be called with positive period + (asserts! (>= period u1) (err ERR_STACKING_INVALID_LOCK_PERIOD)) + ;; Must be current or future reward cycle + (asserts! (>= reward-cycle (current-pox-reward-cycle)) (err ERR_INVALID_REWARD_CYCLE)) (map-set signer-key-authorizations { pox-addr: pox-addr, period: period, reward-cycle: reward-cycle, topic: topic, signer-key: signer-key } allowed) (ok allowed))) diff --git a/stackslib/src/chainstate/stacks/boot/pox_2_tests.rs b/stackslib/src/chainstate/stacks/boot/pox_2_tests.rs index 119d4e4186..07d34a04cc 100644 --- a/stackslib/src/chainstate/stacks/boot/pox_2_tests.rs +++ b/stackslib/src/chainstate/stacks/boot/pox_2_tests.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use clarity::vm::clarity::ClarityConnection; use clarity::vm::contexts::OwnedEnvironment; diff --git a/stackslib/src/chainstate/stacks/boot/pox_3_tests.rs b/stackslib/src/chainstate/stacks/boot/pox_3_tests.rs index 65aedb1302..f0c7a9ef75 100644 --- a/stackslib/src/chainstate/stacks/boot/pox_3_tests.rs +++ b/stackslib/src/chainstate/stacks/boot/pox_3_tests.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use clarity::vm::clarity::ClarityConnection; use clarity::vm::contexts::OwnedEnvironment; diff --git a/stackslib/src/chainstate/stacks/boot/pox_4_tests.rs b/stackslib/src/chainstate/stacks/boot/pox_4_tests.rs index d9857c55d6..4f51b81e82 100644 --- a/stackslib/src/chainstate/stacks/boot/pox_4_tests.rs +++ b/stackslib/src/chainstate/stacks/boot/pox_4_tests.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use clarity::vm::clarity::ClarityConnection; use clarity::vm::contexts::OwnedEnvironment; @@ -2643,8 +2642,11 @@ fn test_set_signer_key_auth() { let mut signer_nonce = 0; let signer_key = &keys[1]; let signer_public_key = StacksPublicKey::from_private(signer_key); + let signer_addr = key_to_stacks_addr(&signer_key); let pox_addr = pox_addr_from(&signer_key); + let current_reward_cycle = get_current_reward_cycle(&peer, &burnchain); + // Only the address associated with `signer-key` can enable auth for that key let invalid_enable_nonce = alice_nonce; let invalid_enable_tx = make_pox_4_set_signer_key_auth( @@ -2658,21 +2660,55 @@ fn test_set_signer_key_auth() { Some(&alice_key), ); + // Test that period is at least u1 + let signer_invalid_period_nonce = signer_nonce; + signer_nonce += 1; + let invalid_tx_period: StacksTransaction = make_pox_4_set_signer_key_auth( + &pox_addr, + &signer_key, + current_reward_cycle, + &Pox4SignatureTopic::StackStx, + 0, + false, + signer_invalid_period_nonce, + Some(&signer_key), + ); + + let signer_invalid_cycle_nonce = signer_nonce; + signer_nonce += 1; + // Test that confirmed reward cycle is at least current reward cycle + let invalid_tx_cycle: StacksTransaction = make_pox_4_set_signer_key_auth( + &pox_addr, + &signer_key, + 1, + &Pox4SignatureTopic::StackStx, + 1, + false, + signer_invalid_cycle_nonce, + Some(&signer_key), + ); + // Disable auth for `signer-key` - let disable_auth_nonce = signer_nonce; let disable_auth_tx: StacksTransaction = make_pox_4_set_signer_key_auth( &pox_addr, &signer_key, - 1, + current_reward_cycle, &Pox4SignatureTopic::StackStx, lock_period, false, - disable_auth_nonce, + signer_nonce, None, ); - let latest_block = - peer.tenure_with_txs(&[invalid_enable_tx, disable_auth_tx], &mut coinbase_nonce); + let latest_block = peer.tenure_with_txs( + &[ + invalid_enable_tx, + invalid_tx_period, + invalid_tx_cycle, + disable_auth_tx, + ], + &mut coinbase_nonce, + ); let alice_txs = get_last_block_sender_transactions(&observer, alice_addr); let invalid_enable_tx_result = alice_txs @@ -2683,11 +2719,39 @@ fn test_set_signer_key_auth() { let expected_error = Value::error(Value::Int(19)).unwrap(); assert_eq!(invalid_enable_tx_result, expected_error); + let signer_txs = get_last_block_sender_transactions(&observer, signer_addr); + + let invalid_tx_period_result = signer_txs + .clone() + .get(signer_invalid_period_nonce as usize) + .unwrap() + .result + .clone(); + + // Check for invalid lock period err + assert_eq!( + invalid_tx_period_result, + Value::error(Value::Int(2)).unwrap() + ); + + let invalid_tx_cycle_result = signer_txs + .clone() + .get(signer_invalid_cycle_nonce as usize) + .unwrap() + .result + .clone(); + + // Check for invalid cycle err + assert_eq!( + invalid_tx_cycle_result, + Value::error(Value::Int(37)).unwrap() + ); + let signer_key_enabled = get_signer_key_authorization_pox_4( &mut peer, &latest_block, &pox_addr, - 1, + current_reward_cycle.clone() as u64, &Pox4SignatureTopic::StackStx, lock_period.try_into().unwrap(), &signer_public_key, @@ -2701,7 +2765,7 @@ fn test_set_signer_key_auth() { let enable_auth_tx = make_pox_4_set_signer_key_auth( &pox_addr, &signer_key, - 1, + current_reward_cycle, &Pox4SignatureTopic::StackStx, lock_period, true, @@ -2715,7 +2779,7 @@ fn test_set_signer_key_auth() { &mut peer, &latest_block, &pox_addr, - 1, + current_reward_cycle.clone() as u64, &Pox4SignatureTopic::StackStx, lock_period.try_into().unwrap(), &signer_public_key, @@ -2729,7 +2793,7 @@ fn test_set_signer_key_auth() { let disable_auth_tx = make_pox_4_set_signer_key_auth( &pox_addr, &signer_key, - 1, + current_reward_cycle, &Pox4SignatureTopic::StackStx, lock_period, false, @@ -2743,7 +2807,7 @@ fn test_set_signer_key_auth() { &mut peer, &latest_block, &pox_addr, - 1, + current_reward_cycle.clone() as u64, &Pox4SignatureTopic::StackStx, lock_period.try_into().unwrap(), &signer_public_key, diff --git a/stackslib/src/chainstate/stacks/boot/signers_voting_tests.rs b/stackslib/src/chainstate/stacks/boot/signers_voting_tests.rs index cc7226e6ef..5ac7d461c2 100644 --- a/stackslib/src/chainstate/stacks/boot/signers_voting_tests.rs +++ b/stackslib/src/chainstate/stacks/boot/signers_voting_tests.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use clarity::boot_util::boot_code_addr; use clarity::vm::clarity::ClarityConnection; diff --git a/stackslib/src/chainstate/stacks/db/blocks.rs b/stackslib/src/chainstate/stacks/db/blocks.rs index 77fc754a65..708f3a6a0d 100644 --- a/stackslib/src/chainstate/stacks/db/blocks.rs +++ b/stackslib/src/chainstate/stacks/db/blocks.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet}; -use std::convert::From; use std::io::prelude::*; use std::io::{Read, Seek, SeekFrom, Write}; use std::path::{Path, PathBuf}; diff --git a/stackslib/src/chainstate/stacks/db/transactions.rs b/stackslib/src/chainstate/stacks/db/transactions.rs index b0822b817c..9297252d15 100644 --- a/stackslib/src/chainstate/stacks/db/transactions.rs +++ b/stackslib/src/chainstate/stacks/db/transactions.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet}; -use std::convert::{TryFrom, TryInto}; use std::io::prelude::*; use std::io::{Read, Write}; use std::path::{Path, PathBuf}; diff --git a/stackslib/src/chainstate/stacks/index/cache.rs b/stackslib/src/chainstate/stacks/index/cache.rs index 3763f15c6b..a711603447 100644 --- a/stackslib/src/chainstate/stacks/index/cache.rs +++ b/stackslib/src/chainstate/stacks/index/cache.rs @@ -16,10 +16,8 @@ use std::char::from_digit; use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use std::hash::{Hash, Hasher}; use std::io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write}; -use std::iter::FromIterator; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; diff --git a/stackslib/src/chainstate/stacks/index/file.rs b/stackslib/src/chainstate/stacks/index/file.rs index 99df760167..1477f9a7dd 100644 --- a/stackslib/src/chainstate/stacks/index/file.rs +++ b/stackslib/src/chainstate/stacks/index/file.rs @@ -16,11 +16,9 @@ use std::char::from_digit; use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use std::fs::OpenOptions; use std::hash::{Hash, Hasher}; use std::io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write}; -use std::iter::FromIterator; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; diff --git a/stackslib/src/chainstate/stacks/index/storage.rs b/stackslib/src/chainstate/stacks/index/storage.rs index c39976419e..97f7ca999a 100644 --- a/stackslib/src/chainstate/stacks/index/storage.rs +++ b/stackslib/src/chainstate/stacks/index/storage.rs @@ -16,10 +16,8 @@ use std::char::from_digit; use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use std::hash::{Hash, Hasher}; use std::io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write}; -use std::iter::FromIterator; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; diff --git a/stackslib/src/chainstate/stacks/index/trie_sql.rs b/stackslib/src/chainstate/stacks/index/trie_sql.rs index d538ed7e50..be1ae91c21 100644 --- a/stackslib/src/chainstate/stacks/index/trie_sql.rs +++ b/stackslib/src/chainstate/stacks/index/trie_sql.rs @@ -19,9 +19,7 @@ use std::char::from_digit; use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; use std::io::{BufWriter, Cursor, Read, Seek, SeekFrom, Write}; -use std::iter::FromIterator; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; diff --git a/stackslib/src/chainstate/stacks/miner.rs b/stackslib/src/chainstate/stacks/miner.rs index b4dbcc5541..c04b03dcda 100644 --- a/stackslib/src/chainstate/stacks/miner.rs +++ b/stackslib/src/chainstate/stacks/miner.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet}; -use std::convert::From; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::{Arc, Mutex}; use std::thread::ThreadId; diff --git a/stackslib/src/chainstate/stacks/mod.rs b/stackslib/src/chainstate/stacks/mod.rs index bb68fb90bc..7247a28f7e 100644 --- a/stackslib/src/chainstate/stacks/mod.rs +++ b/stackslib/src/chainstate/stacks/mod.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{From, TryFrom}; use std::io::prelude::*; use std::io::{Read, Write}; use std::ops::{Deref, DerefMut}; diff --git a/stackslib/src/chainstate/stacks/transaction.rs b/stackslib/src/chainstate/stacks/transaction.rs index 0c764ec83b..4ede285e41 100644 --- a/stackslib/src/chainstate/stacks/transaction.rs +++ b/stackslib/src/chainstate/stacks/transaction.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; use std::io; use std::io::prelude::*; use std::io::{Read, Write}; diff --git a/stackslib/src/clarity_cli.rs b/stackslib/src/clarity_cli.rs index 426b45bc8a..00a067408e 100644 --- a/stackslib/src/clarity_cli.rs +++ b/stackslib/src/clarity_cli.rs @@ -14,10 +14,8 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::{TryFrom, TryInto}; use std::ffi::OsStr; use std::io::{Read, Write}; -use std::iter::Iterator; use std::path::PathBuf; use std::str::FromStr; use std::{env, fs, io, process}; diff --git a/stackslib/src/clarity_vm/clarity.rs b/stackslib/src/clarity_vm/clarity.rs index 8351412119..81a421cdef 100644 --- a/stackslib/src/clarity_vm/clarity.rs +++ b/stackslib/src/clarity_vm/clarity.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; use std::{error, fmt, thread}; use clarity::vm::analysis::errors::{CheckError, CheckErrors}; diff --git a/stackslib/src/core/mod.rs b/stackslib/src/core/mod.rs index 2280fe6d71..45556adf7a 100644 --- a/stackslib/src/core/mod.rs +++ b/stackslib/src/core/mod.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::HashSet; -use std::convert::TryFrom; use clarity::vm::costs::ExecutionCost; use lazy_static::lazy_static; @@ -34,8 +33,7 @@ pub mod mempool; #[cfg(test)] pub mod tests; -use std::cmp::{Ord, Ordering, PartialOrd}; - +use std::cmp::Ordering; pub type StacksEpoch = GenericStacksEpoch; // fork set identifier -- to be mixed with the consensus hash (encodes the version) diff --git a/stackslib/src/cost_estimates/fee_medians.rs b/stackslib/src/cost_estimates/fee_medians.rs index f3a7282a17..88ab0e9c2a 100644 --- a/stackslib/src/cost_estimates/fee_medians.rs +++ b/stackslib/src/cost_estimates/fee_medians.rs @@ -1,7 +1,5 @@ use std::cmp; use std::cmp::Ordering; -use std::convert::TryFrom; -use std::iter::FromIterator; use std::path::Path; use clarity::vm::costs::ExecutionCost; diff --git a/stackslib/src/cost_estimates/fee_scalar.rs b/stackslib/src/cost_estimates/fee_scalar.rs index 895d47ed8f..1c0349e42b 100644 --- a/stackslib/src/cost_estimates/fee_scalar.rs +++ b/stackslib/src/cost_estimates/fee_scalar.rs @@ -1,6 +1,4 @@ use std::cmp; -use std::convert::TryFrom; -use std::iter::FromIterator; use std::path::Path; use clarity::vm::costs::ExecutionCost; diff --git a/stackslib/src/cost_estimates/mod.rs b/stackslib/src/cost_estimates/mod.rs index 1d799607c1..fc4aa5b1b2 100644 --- a/stackslib/src/cost_estimates/mod.rs +++ b/stackslib/src/cost_estimates/mod.rs @@ -2,7 +2,6 @@ use std::cmp; use std::collections::HashMap; use std::error::Error; use std::fmt::Display; -use std::iter::FromIterator; use std::ops::{Add, Div, Mul, Rem, Sub}; use std::path::Path; diff --git a/stackslib/src/cost_estimates/pessimistic.rs b/stackslib/src/cost_estimates/pessimistic.rs index 7e222f5de6..b986d54dc7 100644 --- a/stackslib/src/cost_estimates/pessimistic.rs +++ b/stackslib/src/cost_estimates/pessimistic.rs @@ -1,6 +1,4 @@ use std::cmp; -use std::convert::TryFrom; -use std::iter::FromIterator; use std::path::Path; use clarity::vm::costs::ExecutionCost; diff --git a/stackslib/src/main.rs b/stackslib/src/main.rs index 0e0f242b00..f3ca5a6751 100644 --- a/stackslib/src/main.rs +++ b/stackslib/src/main.rs @@ -34,7 +34,6 @@ use tikv_jemallocator::Jemalloc; static GLOBAL: Jemalloc = Jemalloc; use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; use std::fs::{File, OpenOptions}; use std::io::prelude::*; use std::io::BufReader; diff --git a/stackslib/src/monitoring/mod.rs b/stackslib/src/monitoring/mod.rs index ff718ba30c..302ff4d0d1 100644 --- a/stackslib/src/monitoring/mod.rs +++ b/stackslib/src/monitoring/mod.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; use std::error::Error; use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; diff --git a/stackslib/src/net/api/mod.rs b/stackslib/src/net/api/mod.rs index 4b45c9f4e0..b6fb21fc2a 100644 --- a/stackslib/src/net/api/mod.rs +++ b/stackslib/src/net/api/mod.rs @@ -14,8 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::From; - use clarity::vm::costs::ExecutionCost; use stacks_common::codec::read_next; use stacks_common::types::chainstate::{BlockHeaderHash, StacksBlockId}; diff --git a/stackslib/src/net/atlas/db.rs b/stackslib/src/net/atlas/db.rs index 10f48a6114..784bff9639 100644 --- a/stackslib/src/net/atlas/db.rs +++ b/stackslib/src/net/atlas/db.rs @@ -34,7 +34,6 @@ //! use std::collections::HashSet; -use std::convert::{From, TryFrom}; use std::fs; use clarity::vm::types::QualifiedContractIdentifier; diff --git a/stackslib/src/net/atlas/mod.rs b/stackslib/src/net/atlas/mod.rs index ffeb569f95..45100d984b 100644 --- a/stackslib/src/net/atlas/mod.rs +++ b/stackslib/src/net/atlas/mod.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; use std::hash::{Hash, Hasher}; use clarity::vm::types::{QualifiedContractIdentifier, SequenceData, TupleData, Value}; diff --git a/stackslib/src/net/atlas/tests.rs b/stackslib/src/net/atlas/tests.rs index 567d49fe61..2ebcb71316 100644 --- a/stackslib/src/net/atlas/tests.rs +++ b/stackslib/src/net/atlas/tests.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{BinaryHeap, HashMap, HashSet}; -use std::convert::TryFrom; use std::{thread, time}; use clarity::vm::types::QualifiedContractIdentifier; diff --git a/stackslib/src/net/chat.rs b/stackslib/src/net/chat.rs index c547eb5af5..1b54241197 100644 --- a/stackslib/src/net/chat.rs +++ b/stackslib/src/net/chat.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::TryFrom; use std::io::{Read, Write}; use std::net::SocketAddr; use std::{cmp, mem}; diff --git a/stackslib/src/net/codec.rs b/stackslib/src/net/codec.rs index e4ba530f2e..c0496aa14c 100644 --- a/stackslib/src/net/codec.rs +++ b/stackslib/src/net/codec.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::HashSet; -use std::convert::TryFrom; use std::io::prelude::*; use std::io::Read; use std::{io, mem}; diff --git a/stackslib/src/net/connection.rs b/stackslib/src/net/connection.rs index 44559fca5c..25d3ee7489 100644 --- a/stackslib/src/net/connection.rs +++ b/stackslib/src/net/connection.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::VecDeque; -use std::convert::TryFrom; use std::io::{Read, Write}; use std::ops::{Deref, DerefMut}; use std::sync::mpsc::{ diff --git a/stackslib/src/net/db.rs b/stackslib/src/net/db.rs index 46707cbdbf..1c116a6174 100644 --- a/stackslib/src/net/db.rs +++ b/stackslib/src/net/db.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::HashSet; -use std::convert::{From, TryFrom}; use std::{fmt, fs}; use clarity::vm::types::{ diff --git a/stackslib/src/net/download.rs b/stackslib/src/net/download.rs index d44efef4a1..f19d6f47d0 100644 --- a/stackslib/src/net/download.rs +++ b/stackslib/src/net/download.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::TryFrom; use std::hash::{Hash, Hasher}; use std::io::{Read, Write}; use std::net::{IpAddr, SocketAddr}; @@ -2507,7 +2506,6 @@ impl PeerNetwork { #[cfg(test)] pub mod test { use std::collections::HashMap; - use std::convert::TryFrom; use clarity::vm::clarity::ClarityConnection; use clarity::vm::costs::ExecutionCost; diff --git a/stackslib/src/net/http/request.rs b/stackslib/src/net/http/request.rs index 36df8235a0..287acb1166 100644 --- a/stackslib/src/net/http/request.rs +++ b/stackslib/src/net/http/request.rs @@ -16,7 +16,6 @@ use std::collections::{BTreeMap, HashMap, HashSet}; use std::io::{Read, Write}; -use std::string::ToString; use percent_encoding::percent_decode_str; use rand::{thread_rng, Rng}; diff --git a/stackslib/src/net/inv/epoch2x.rs b/stackslib/src/net/inv/epoch2x.rs index 4e97996410..480743a369 100644 --- a/stackslib/src/net/inv/epoch2x.rs +++ b/stackslib/src/net/inv/epoch2x.rs @@ -16,7 +16,6 @@ use std::cmp; use std::collections::{BTreeMap, HashMap, HashSet}; -use std::convert::TryFrom; use std::io::{Read, Write}; use std::net::SocketAddr; diff --git a/stackslib/src/net/mod.rs b/stackslib/src/net/mod.rs index fbdc914fb7..6136a4d094 100644 --- a/stackslib/src/net/mod.rs +++ b/stackslib/src/net/mod.rs @@ -15,9 +15,7 @@ // along with this program. If not, see . use std::borrow::Borrow; -use std::cmp::PartialEq; use std::collections::{HashMap, HashSet}; -use std::convert::{From, TryFrom}; use std::hash::{Hash, Hasher}; use std::io::prelude::*; use std::io::{Read, Write}; diff --git a/stackslib/src/net/rpc.rs b/stackslib/src/net/rpc.rs index d6d75cff17..e2f93d7289 100644 --- a/stackslib/src/net/rpc.rs +++ b/stackslib/src/net/rpc.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::collections::{HashMap, HashSet, VecDeque}; -use std::convert::TryFrom; use std::io::prelude::*; use std::io::{Read, Seek, SeekFrom, Write}; use std::net::SocketAddr; diff --git a/stackslib/src/net/tests/mod.rs b/stackslib/src/net/tests/mod.rs index acff183d92..9111f10fc0 100644 --- a/stackslib/src/net/tests/mod.rs +++ b/stackslib/src/net/tests/mod.rs @@ -46,7 +46,9 @@ use crate::chainstate::stacks::address::PoxAddress; use crate::chainstate::stacks::boot::test::{ key_to_stacks_addr, make_pox_4_lockup, make_signer_key_signature, with_sortdb, }; -use crate::chainstate::stacks::boot::MINERS_NAME; +use crate::chainstate::stacks::boot::{ + MINERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME, +}; use crate::chainstate::stacks::db::{MinerPaymentTxFees, StacksAccount, StacksChainState}; use crate::chainstate::stacks::events::TransactionOrigin; use crate::chainstate::stacks::{ @@ -185,9 +187,9 @@ impl NakamotoBootPlan { function_name, .. }) => { - if contract_name.as_str() == "signers-voting" + if contract_name.as_str() == SIGNERS_VOTING_NAME && address.is_burn() - && function_name.as_str() == "vote-for-aggregate-public-key" + && function_name.as_str() == SIGNERS_VOTING_FUNCTION_NAME { false } else { diff --git a/stackslib/src/util_lib/boot.rs b/stackslib/src/util_lib/boot.rs index af3f443278..95cfca9c41 100644 --- a/stackslib/src/util_lib/boot.rs +++ b/stackslib/src/util_lib/boot.rs @@ -1,5 +1,3 @@ -use std::convert::TryFrom; - use clarity::vm::database::STXBalance; use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier}; use clarity::vm::ContractName; diff --git a/stackslib/src/util_lib/db.rs b/stackslib/src/util_lib/db.rs index 584423ffb8..a06c23408b 100644 --- a/stackslib/src/util_lib/db.rs +++ b/stackslib/src/util_lib/db.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryInto; use std::io::Error as IOError; use std::ops::{Deref, DerefMut}; use std::path::{Path, PathBuf}; diff --git a/stackslib/src/util_lib/strings.rs b/stackslib/src/util_lib/strings.rs index 3efb1fc7d9..0486e6bf81 100644 --- a/stackslib/src/util_lib/strings.rs +++ b/stackslib/src/util_lib/strings.rs @@ -15,7 +15,6 @@ // along with this program. If not, see . use std::borrow::Borrow; -use std::convert::TryFrom; use std::io::prelude::*; use std::io::{Read, Write}; use std::ops::{Deref, DerefMut}; diff --git a/testnet/stacks-node/src/config.rs b/testnet/stacks-node/src/config.rs index eb118eb553..7e2751d7a8 100644 --- a/testnet/stacks-node/src/config.rs +++ b/testnet/stacks-node/src/config.rs @@ -1,5 +1,4 @@ use std::collections::HashSet; -use std::convert::TryInto; use std::fs; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::PathBuf; diff --git a/testnet/stacks-node/src/main.rs b/testnet/stacks-node/src/main.rs index c636535353..8351a30303 100644 --- a/testnet/stacks-node/src/main.rs +++ b/testnet/stacks-node/src/main.rs @@ -32,7 +32,6 @@ pub mod syncctl; pub mod tenure; use std::collections::HashMap; -use std::convert::TryInto; use std::{env, panic, process}; use backtrace::Backtrace; diff --git a/testnet/stacks-node/src/mockamoto.rs b/testnet/stacks-node/src/mockamoto.rs index 2629f4f9b2..011300bc88 100644 --- a/testnet/stacks-node/src/mockamoto.rs +++ b/testnet/stacks-node/src/mockamoto.rs @@ -49,7 +49,7 @@ use stacks::chainstate::nakamoto::{ NakamotoBlock, NakamotoBlockHeader, NakamotoChainState, SetupBlockResult, }; use stacks::chainstate::stacks::address::PoxAddress; -use stacks::chainstate::stacks::boot::SIGNERS_VOTING_NAME; +use stacks::chainstate::stacks::boot::{SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME}; use stacks::chainstate::stacks::db::{ChainStateBootData, ClarityTx, StacksChainState}; use stacks::chainstate::stacks::miner::{ BlockBuilder, BlockBuilderSettings, BlockLimitFunction, MinerStatus, TransactionResult, @@ -934,7 +934,7 @@ impl MockamotoNode { let vote_payload = TransactionPayload::new_contract_call( boot_code_addr(false), SIGNERS_VOTING_NAME, - "vote-for-aggregate-public-key", + SIGNERS_VOTING_FUNCTION_NAME, vec![ ClarityValue::UInt(0), aggregate_public_key_val, diff --git a/testnet/stacks-node/src/nakamoto_node/miner.rs b/testnet/stacks-node/src/nakamoto_node/miner.rs index 5e3c39c9e9..9c886b4343 100644 --- a/testnet/stacks-node/src/nakamoto_node/miner.rs +++ b/testnet/stacks-node/src/nakamoto_node/miner.rs @@ -14,7 +14,6 @@ use std::collections::HashMap; // // You should have received a copy of the GNU General Public License // along with this program. If not, see . -use std::convert::TryFrom; use std::thread; use std::thread::JoinHandle; use std::time::{Duration, Instant}; @@ -332,48 +331,43 @@ impl BlockMinerThread { }) .collect(); - // There may be more than signer messages, but odds are there is only one transacton per signer - let mut transactions_to_include = Vec::with_capacity(signer_messages.len()); + if signer_messages.is_empty() { + return Ok(vec![]); + } + + // Get all nonces for the signers from clarity DB to use to validate transactions + let account_nonces = chainstate + .with_read_only_clarity_tx(&sortdb.index_conn(), &self.parent_tenure_id, |clarity_tx| { + clarity_tx.with_clarity_db_readonly(|clarity_db| { + addresses + .iter() + .map(|address| { + ( + address.clone(), + clarity_db + .get_account_nonce(&address.clone().into()) + .unwrap_or(0), + ) + }) + .collect::>() + }) + }) + .unwrap_or_default(); + let mut filtered_transactions: HashMap = HashMap::new(); for (_slot, signer_message) in signer_messages { match signer_message { SignerMessage::Transactions(transactions) => { - for transaction in transactions { - let address = transaction.origin_address(); - let nonce = transaction.get_origin_nonce(); - if !addresses.contains(&address) { - test_debug!("Miner: ignoring transaction ({:?}) with nonce {nonce} from address {address}", transaction.txid()); - continue; - } - - let cur_nonce = chainstate - .with_read_only_clarity_tx( - &sortdb.index_conn(), - &self.parent_tenure_id, - |clarity_tx| { - clarity_tx.with_clarity_db_readonly(|clarity_db| { - clarity_db.get_account_nonce(&address.into()).unwrap_or(0) - }) - }, - ) - .unwrap_or(0); - - if cur_nonce > nonce { - test_debug!("Miner: ignoring transaction ({:?}) with nonce {nonce} from address {address}", transaction.txid()); - continue; - } - debug!("Miner: including signer transaction."; - "nonce" => {nonce}, - "origin_address" => %address, - "txid" => %transaction.txid() - ); - // TODO : filter out transactions that are not valid votes. Do not include transactions with invalid/duplicate nonces for the same address. - transactions_to_include.push(transaction); - } + NakamotoSigners::update_filtered_transactions( + &mut filtered_transactions, + &account_nonces, + self.config.is_mainnet(), + transactions, + ) } _ => {} // Any other message is ignored } } - Ok(transactions_to_include) + Ok(filtered_transactions.into_values().collect()) } fn wait_for_signer_signature( @@ -392,7 +386,7 @@ impl BlockMinerThread { let slot_ids = slot_ids_addresses.keys().cloned().collect::>(); // If more than a threshold percentage of the signers reject the block, we should not wait any further let weights: u64 = signer_weights.values().sum(); - let rejection_threshold = weights / 10 * 7; + let rejection_threshold: u64 = (weights as f64 * 7_f64 / 10_f64).ceil() as u64; let mut rejections = HashSet::new(); let mut rejections_weight: u64 = 0; let now = Instant::now(); diff --git a/testnet/stacks-node/src/nakamoto_node/peer.rs b/testnet/stacks-node/src/nakamoto_node/peer.rs index 2c8beb7744..eeb6789d30 100644 --- a/testnet/stacks-node/src/nakamoto_node/peer.rs +++ b/testnet/stacks-node/src/nakamoto_node/peer.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . use std::collections::VecDeque; -use std::default::Default; use std::net::SocketAddr; use std::sync::mpsc::TrySendError; use std::time::Duration; diff --git a/testnet/stacks-node/src/neon_node.rs b/testnet/stacks-node/src/neon_node.rs index a9c0393674..49064d4971 100644 --- a/testnet/stacks-node/src/neon_node.rs +++ b/testnet/stacks-node/src/neon_node.rs @@ -140,8 +140,6 @@ use std::cmp; use std::cmp::Ordering as CmpOrdering; use std::collections::{BTreeMap, HashMap, HashSet, VecDeque}; -use std::convert::{TryFrom, TryInto}; -use std::default::Default; use std::io::{Read, Write}; use std::net::SocketAddr; use std::sync::mpsc::{Receiver, TrySendError}; diff --git a/testnet/stacks-node/src/node.rs b/testnet/stacks-node/src/node.rs index cdebdbc781..90c2123079 100644 --- a/testnet/stacks-node/src/node.rs +++ b/testnet/stacks-node/src/node.rs @@ -1,5 +1,4 @@ use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; use std::net::SocketAddr; use std::thread::JoinHandle; use std::{env, thread, time}; diff --git a/testnet/stacks-node/src/tests/epoch_205.rs b/testnet/stacks-node/src/tests/epoch_205.rs index 844a314bc6..0f689f00ef 100644 --- a/testnet/stacks-node/src/tests/epoch_205.rs +++ b/testnet/stacks-node/src/tests/epoch_205.rs @@ -1,5 +1,4 @@ use std::collections::HashMap; -use std::convert::TryFrom; use std::sync::atomic::Ordering; use std::{env, thread}; diff --git a/testnet/stacks-node/src/tests/mempool.rs b/testnet/stacks-node/src/tests/mempool.rs index cc1f3d8228..8c906cd43e 100644 --- a/testnet/stacks-node/src/tests/mempool.rs +++ b/testnet/stacks-node/src/tests/mempool.rs @@ -1,4 +1,3 @@ -use std::convert::{From, TryFrom}; use std::sync::Mutex; use clarity::vm::costs::ExecutionCost; diff --git a/testnet/stacks-node/src/tests/mod.rs b/testnet/stacks-node/src/tests/mod.rs index 485dd524d2..7dbabae3ed 100644 --- a/testnet/stacks-node/src/tests/mod.rs +++ b/testnet/stacks-node/src/tests/mod.rs @@ -14,7 +14,6 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . use std::collections::HashMap; -use std::convert::TryInto; use std::sync::atomic::AtomicU64; use std::sync::Arc; diff --git a/testnet/stacks-node/src/tests/nakamoto_integrations.rs b/testnet/stacks-node/src/tests/nakamoto_integrations.rs index e97aefd42a..3b46ce24ac 100644 --- a/testnet/stacks-node/src/tests/nakamoto_integrations.rs +++ b/testnet/stacks-node/src/tests/nakamoto_integrations.rs @@ -30,7 +30,9 @@ use stacks::chainstate::nakamoto::miner::NakamotoBlockBuilder; use stacks::chainstate::nakamoto::test_signers::TestSigners; use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoChainState}; use stacks::chainstate::stacks::address::PoxAddress; -use stacks::chainstate::stacks::boot::{MINERS_NAME, SIGNERS_VOTING_NAME}; +use stacks::chainstate::stacks::boot::{ + MINERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME, +}; use stacks::chainstate::stacks::db::StacksChainState; use stacks::chainstate::stacks::miner::{BlockBuilder, BlockLimitFunction, TransactionResult}; use stacks::chainstate::stacks::{StacksTransaction, ThresholdSignature, TransactionPayload}; @@ -455,7 +457,7 @@ pub fn boot_to_epoch_3( 300, &StacksAddress::burn_address(false), SIGNERS_VOTING_NAME, - "vote-for-aggregate-public-key", + SIGNERS_VOTING_FUNCTION_NAME, &[ clarity::vm::Value::UInt(i as u128), aggregate_public_key.clone(), @@ -564,7 +566,7 @@ fn signer_vote_if_needed( 300, &StacksAddress::burn_address(false), SIGNERS_VOTING_NAME, - "vote-for-aggregate-public-key", + SIGNERS_VOTING_FUNCTION_NAME, &[ clarity::vm::Value::UInt(i as u128), aggregate_public_key.clone(), diff --git a/testnet/stacks-node/src/tests/neon_integrations.rs b/testnet/stacks-node/src/tests/neon_integrations.rs index d751035ac9..cd0c96358e 100644 --- a/testnet/stacks-node/src/tests/neon_integrations.rs +++ b/testnet/stacks-node/src/tests/neon_integrations.rs @@ -1,5 +1,4 @@ use std::collections::{HashMap, HashSet}; -use std::convert::TryFrom; use std::path::Path; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::{mpsc, Arc}; diff --git a/testnet/stacks-node/src/tests/signer.rs b/testnet/stacks-node/src/tests/signer.rs index d903f58c43..c0c2e72e2b 100644 --- a/testnet/stacks-node/src/tests/signer.rs +++ b/testnet/stacks-node/src/tests/signer.rs @@ -7,30 +7,42 @@ use std::time::{Duration, Instant}; use std::{env, thread}; use clarity::boot_util::boot_code_id; +use clarity::vm::Value; use libsigner::{ BlockResponse, RejectCode, RunningSigner, Signer, SignerEventReceiver, SignerMessage, BLOCK_MSG_ID, }; +use rand::thread_rng; +use rand_core::RngCore; use stacks::burnchains::Txid; use stacks::chainstate::coordinator::comm::CoordinatorChannels; use stacks::chainstate::nakamoto::signer_set::NakamotoSigners; use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoBlockHeader, NakamotoBlockVote}; -use stacks::chainstate::stacks::boot::SIGNERS_NAME; +use stacks::chainstate::stacks::boot::{ + SIGNERS_NAME, SIGNERS_VOTING_FUNCTION_NAME, SIGNERS_VOTING_NAME, +}; use stacks::chainstate::stacks::miner::TransactionEvent; -use stacks::chainstate::stacks::{StacksPrivateKey, StacksTransaction, ThresholdSignature}; +use stacks::chainstate::stacks::{ + StacksPrivateKey, StacksTransaction, ThresholdSignature, TransactionAnchorMode, + TransactionAuth, TransactionPayload, TransactionPostConditionMode, TransactionSmartContract, + TransactionVersion, +}; use stacks::core::StacksEpoch; use stacks::net::api::postblock_proposal::BlockValidateResponse; +use stacks::util_lib::strings::StacksString; use stacks_common::bitvec::BitVec; use stacks_common::codec::{read_next, StacksMessageCodec}; -use stacks_common::consts::SIGNER_SLOTS_PER_USER; -use stacks_common::types::chainstate::{ConsensusHash, StacksBlockId, TrieHash}; +use stacks_common::consts::{CHAIN_ID_TESTNET, SIGNER_SLOTS_PER_USER}; +use stacks_common::types::chainstate::{ + ConsensusHash, StacksAddress, StacksBlockId, StacksPublicKey, TrieHash, +}; use stacks_common::types::StacksEpochId; use stacks_common::util::hash::{MerkleTree, Sha512Trunc256Sum}; use stacks_common::util::secp256k1::MessageSignature; use stacks_signer::client::{StackerDB, StacksClient}; use stacks_signer::config::{build_signer_config_tomls, GlobalConfig as SignerConfig, Network}; use stacks_signer::runloop::RunLoopCommand; -use stacks_signer::signer::Command as SignerCommand; +use stacks_signer::signer::{Command as SignerCommand, SignerSlotID}; use tracing_subscriber::prelude::*; use tracing_subscriber::{fmt, EnvFilter}; use wsts::common::Signature; @@ -519,7 +531,7 @@ impl SignerTest { .unwrap() } - fn get_signer_index(&self, reward_cycle: u64) -> u32 { + fn get_signer_index(&self, reward_cycle: u64) -> SignerSlotID { let valid_signer_set = u32::try_from(reward_cycle % 2).expect("FATAL: reward_cycle % 2 exceeds u32::MAX"); let signer_stackerdb_contract_id = boot_code_id(SIGNERS_NAME, false); @@ -529,7 +541,9 @@ impl SignerTest { .expect("FATAL: failed to get signer slots from stackerdb") .iter() .position(|(address, _)| address == self.stacks_client.get_signer_address()) - .map(|pos| u32::try_from(pos).expect("FATAL: number of signers exceeds u32::MAX")) + .map(|pos| { + SignerSlotID(u32::try_from(pos).expect("FATAL: number of signers exceeds u32::MAX")) + }) .expect("FATAL: signer not registered") } @@ -545,38 +559,190 @@ impl SignerTest { .unwrap(); // Get the signer indices let reward_cycle = self.get_current_reward_cycle(); - let valid_signer_index = self.get_signer_index(reward_cycle); - let round = self - .stacks_client - .get_last_round(reward_cycle) - .expect("FATAL: failed to get round") - .unwrap_or(0) - .saturating_add(1); - let point = Point::from(Scalar::random(&mut rand::thread_rng())); - let invalid_nonce_tx = self - .stacks_client - .build_vote_for_aggregate_public_key( - valid_signer_index, - round, - point, - reward_cycle, + + let signer_private_key = self.signer_stacks_private_keys[0]; + + let vote_contract_id = boot_code_id(SIGNERS_VOTING_NAME, false); + let contract_addr = vote_contract_id.issuer.into(); + let contract_name = vote_contract_id.name.clone(); + + let signer_index = thread_rng().next_u64(); + let signer_index_arg = Value::UInt(signer_index as u128); + + let point = Point::from(Scalar::random(&mut thread_rng())); + let point_arg = + Value::buff_from(point.compress().data.to_vec()).expect("Failed to create buff"); + + let round = thread_rng().next_u64(); + let round_arg = Value::UInt(round as u128); + + let reward_cycle_arg = Value::UInt(reward_cycle as u128); + let valid_function_args = vec![ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ]; + + // Create a invalid transaction that is not a contract call + let invalid_not_contract_call = StacksTransaction { + version: TransactionVersion::Testnet, + chain_id: CHAIN_ID_TESTNET, + auth: TransactionAuth::from_p2pkh(&signer_private_key).unwrap(), + anchor_mode: TransactionAnchorMode::Any, + post_condition_mode: TransactionPostConditionMode::Allow, + post_conditions: vec![], + payload: TransactionPayload::SmartContract( + TransactionSmartContract { + name: "test-contract".into(), + code_body: StacksString::from_str("(/ 1 0)").unwrap(), + }, None, - 0, // Old nonce + ), + }; + let invalid_contract_address = StacksClient::build_signed_contract_call_transaction( + &StacksAddress::p2pkh(false, &StacksPublicKey::from_private(&signer_private_key)), + contract_name.clone(), + SIGNERS_VOTING_FUNCTION_NAME.into(), + &valid_function_args, + &signer_private_key, + TransactionVersion::Testnet, + CHAIN_ID_TESTNET, + 1, + 10, + ) + .unwrap(); + + let invalid_contract_name = StacksClient::build_signed_contract_call_transaction( + &contract_addr, + "bad-signers-contract-name".into(), + SIGNERS_VOTING_FUNCTION_NAME.into(), + &valid_function_args, + &signer_private_key, + TransactionVersion::Testnet, + CHAIN_ID_TESTNET, + 1, + 10, + ) + .unwrap(); + + let invalid_signers_vote_function = StacksClient::build_signed_contract_call_transaction( + &contract_addr, + contract_name.clone(), + "some-other-function".into(), + &valid_function_args, + &signer_private_key, + TransactionVersion::Testnet, + CHAIN_ID_TESTNET, + 1, + 10, + ) + .unwrap(); + + let invalid_function_arg_signer_index = + StacksClient::build_signed_contract_call_transaction( + &contract_addr, + contract_name.clone(), + SIGNERS_VOTING_FUNCTION_NAME.into(), + &[ + point_arg.clone(), + point_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ], + &signer_private_key, + TransactionVersion::Testnet, + CHAIN_ID_TESTNET, + 1, + 10, ) - .expect("FATAL: failed to build vote for aggregate public key"); + .unwrap(); + + let invalid_function_arg_key = StacksClient::build_signed_contract_call_transaction( + &contract_addr, + contract_name.clone(), + SIGNERS_VOTING_FUNCTION_NAME.into(), + &[ + signer_index_arg.clone(), + signer_index_arg.clone(), + round_arg.clone(), + reward_cycle_arg.clone(), + ], + &signer_private_key, + TransactionVersion::Testnet, + CHAIN_ID_TESTNET, + 1, + 10, + ) + .unwrap(); + + let invalid_function_arg_round = StacksClient::build_signed_contract_call_transaction( + &contract_addr, + contract_name.clone(), + SIGNERS_VOTING_FUNCTION_NAME.into(), + &[ + signer_index_arg.clone(), + point_arg.clone(), + point_arg.clone(), + reward_cycle_arg.clone(), + ], + &signer_private_key, + TransactionVersion::Testnet, + CHAIN_ID_TESTNET, + 1, + 10, + ) + .unwrap(); + + let invalid_function_arg_reward_cycle = + StacksClient::build_signed_contract_call_transaction( + &contract_addr, + contract_name.clone(), + SIGNERS_VOTING_FUNCTION_NAME.into(), + &[ + signer_index_arg.clone(), + point_arg.clone(), + round_arg.clone(), + point_arg.clone(), + ], + &signer_private_key, + TransactionVersion::Testnet, + CHAIN_ID_TESTNET, + 1, + 10, + ) + .unwrap(); + + let invalid_nonce = StacksClient::build_signed_contract_call_transaction( + &contract_addr, + contract_name.clone(), + SIGNERS_VOTING_FUNCTION_NAME.into(), + &valid_function_args, + &signer_private_key, + TransactionVersion::Testnet, + CHAIN_ID_TESTNET, + 0, // Old nonce + 10, + ) + .unwrap(); + let invalid_stacks_client = StacksClient::new(StacksPrivateKey::new(), host, false); let invalid_signer_tx = invalid_stacks_client - .build_vote_for_aggregate_public_key( - valid_signer_index, - round, - point, - reward_cycle, - None, - 0, - ) + .build_vote_for_aggregate_public_key(0, round, point, reward_cycle, None, 0) .expect("FATAL: failed to build vote for aggregate public key"); - // TODO: add invalid contract calls (one with non 'vote-for-aggregate-public-key' function call and one with invalid function args) - vec![invalid_nonce_tx, invalid_signer_tx] + + vec![ + invalid_nonce, + invalid_not_contract_call, + invalid_contract_name, + invalid_contract_address, + invalid_signers_vote_function, + invalid_function_arg_key, + invalid_function_arg_reward_cycle, + invalid_function_arg_round, + invalid_function_arg_signer_index, + invalid_signer_tx, + ] } fn shutdown(self) {