This release contains bugfixes and improvements to the stacks-node and stacks-signer binaries, ensuring more consistent block production.
This release is compatible with chainstate directories from 4.0.x.
This release is recommended for all Stacks node and signer operators.
This release includes the following changes:
https://github.com/stacks-network/stacks-core/blob/4.0.5/CHANGELOG.md
⚠️ Breaking Changes
burnchain.mode = "helium"is now rejected at startup, and thestacks-node heliumsubcommand andstacks-inspect --network-config heliumoption are removed. Run a private regtest network withstacks-node start --configandmode = "nakamoto-neon"instead; the node no longer mines its own Bitcoin blocks, so mine them with bitcoind (e.g.generatetoaddress).- Removed the
burnchain.commit_anchor_block_within,node.name,node.max_microblocksandnode.chain_liveness_poll_time_secsconfiguration options, which the node no longer reads (commit_anchor_block_withinwas only used by the removed helium run loop). Nodes whose config file still sets any of them will fail to start with anunknown fielderror; delete those lines before upgrading. burnchain.modeis now required in the node config file (it previously defaulted tomocknet), and themocknetandargonmodes are rejected at startup. Set[burnchain] modeexplicitly, e.g.nakamoto-neonfor a private regtest network.- The signer no longer accepts
network = "mocknet"; usenetwork = "testnet", which has the same chain ID and address version. - Removed the deprecated
[connection_options]config keysmax_neighbors_per_host,max_clients_per_host,soft_max_neighbors_per_hostandfull_inv_sync_interval, which had no effect on node behavior. A node whose config file still sets any of them will fail to start; delete these keys from the config file before upgrading. - Removed the
burnchain.rpc_sslconfiguration option, which had no effects. Nodes whose config file still setsrpc_sslwill fail to start with anunknown field "rpc_ssl"error; delete therpc_sslline from the[burnchain]section before upgrading.
Added
- Stacks signers will now allow a cross-cycle extend if the next cycle does not yet have a successful sortition. That is, if the next cycle only has empty sortitions (i.e., no miner elected yet), the prior cycle's miner will be allowed to extend. As soon as a miner is selected in the next cycle, the next cycle takes over.
- Signers now prune their database in small batches every 10 seconds, keeping at least the last
MAX_FORK_DEPTH(100) burn blocks of tenures plus the tip's tenure, sosignerdb.sqliteno longer grows without bound. An existing database is drained over the following days and stops growing, but its file keeps its size until it is compacted offline. - Signers database pruning is on by default and can be disabled with
db_pruning = falsein the signer config.
Changed
- From Epoch 4.1, contract analysis rejects mixing tuples with different fields where one type is inferred (
if,match,default-to,is-eq,fold,concat,append, list construction, function returns). Contracts deployed earlier execute unchanged, and calls into them can still return values wider than their declared types. - Clarity execution no longer copies function bodies on every call, or trait definitions when dynamic dispatch checks a target that lacks
impl-trait; execution behavior and costs are unchanged. - Starting in Epoch 4.1, a block-commit whose parent is
(height > 0, vtxindex 0)is only accepted if that parent block-commit exists, and is no longer assumed to descend from the PoX anchor block. Epochs 3.0 through 4.0 keep accepting such commits without a parent (the former shadow-block rule) so that sortition history replays unchanged; building off of genesis ((0, 0)) is unaffected. - Beginning in Epoch 4.1, contract principals inside Clarity values are limited to 40-byte names, matching deployed contracts (previously up to 128 bytes).
from-consensus-buff?returnsnonefor a longer name; a transaction whose contract-call arguments, token-transfer recipient, coinbase recipient, or NFT post-condition value carries one is invalid; andprincipal-destruct?aborts with a runtime error for one. Long-name values already in chainstate remain readable. - Beginning in Epoch 4.1,
concatfails its transaction with aValueTooLargeruntime error when it would build a buffer or string larger thanMAX_VALUE_SIZE. - From Epoch 4.1, contract analysis rejects calls with the wrong number of arguments to user-defined functions and to
map-set,map-insert,map-delete,var-setandget-block-info?. - From Epoch 4.1, contract analysis types a
foldresult to admit its initial value, which an empty sequence returns unchanged, instead of the callback's return type alone. Contracts deployed earlier execute unchanged. clarity_vmnow imports the MARF fromstacks_marfdirectly instead of through thestackslib::chainstate::stacks::indexre-export. No behaviour change.- The MARF's
get_by_path,get_by_hashandget_by_keynow share a single implementation instead of repeating the same lookup three times. No behaviour change. - The MARF no longer depends on
stackslib::util_lib::db, removing the dependency cycle between the two in preparation for moving the MARF into its own crate. No behaviour change. - The chainstate headers MARF is now held in a
MarfHeadersDBnewtype that carries theHeadersDBimplementation, so it stays legal once the MARF moves to its own crate. No behaviour change. - Removed
MARF::get_by_hash, which was identical toMARF::get_by_path; callers now useget_by_path. - The MARF now lives in the
stacks-marfcrate.stackslib::chainstate::stacks::indexis re-exported from it, so existing import paths keep working. - The MARF's test-only helpers are now available behind a
testingfeature, and its modules importstacks_commondirectly instead of throughstackslib's re-exports, in preparation for moving the MARF into its own crate. No behaviour change. - The chainstate snapshot module now imports the MARF from
stacks_marfdirectly instead of through thestackslib::chainstate::stacks::indexre-export. No behaviour change. - The MARF's tests now use
assert_matches!instead ofassert!(matches!(..)), which reports the actual value on failure. Test-only change. - Moves SQLite plumbing that knows nothing about the MARF, and isn't specific to stackslib, down into stacks-common where both crates can reach it.
- Restricted test-only net/p2p helper functions to test builds.
- Clarified the
with-poxallowance andPoxpost-condition documentation. - Added a runtime guard against
restrict-assets?running its body with awith-all-assets-unsafeallowance. Starting in epoch 4.1,restrict-assets?fails with an includable error before running its body. - Updated RPC OpenAPI descriptions, schemas and examples for Epoch 4.0 and PoX-5, including a separate
/v3/stacker_setschema and example for PoX-5 reward sets. - Replace complex chainstate results and callbacks with documented named types and aliases, updating node and inspection-tool callers without changing consensus rules or database schemas.
- Replace complex networking results with documented named types for HTTP stream reads, downloads, mempool responses, and relayed blocks without changing wire formats or RPC payloads.
- Removed the
fixes_tuple_merge_size_checkepoch gate: an oversized tuplemerge(combined size aboveMAX_VALUE_SIZE) is now rejected withValueTooLargeat the merge site in every epoch, byTupleTypeSignature::shallow_merge.
Fixed
- Removed redundant borrows, clones, conversions, temporary bindings, and control flow reported by Clippy.
- Fixed the deterministic WebAssembly CI check to compile for
wasm32-unknown-unknown, and resolved the unused import and argument warnings it surfaced. - From Epoch 4.1, calls to Clarity 2+ functions no longer fail with ValueTooLarge when a list argument, such as a filter result, carries a larger bound than its length. Affected calls in previously deployed contracts can now succeed.
- Fixed a miner stall which occurs when an empty sortition arrives after the miner had proposed its tenure-start (
BlockFound) block but before the signers had signed it. Previously a miner issued a second, "late"BlockFoundfor the same sortition, which signers interpreted as a sibling block and the signers would never sign. The relayer now waits a short, bounded time for an in-flight tenure-start proposal to land before re-issuing aBlockFound, and a lateBlockFoundthread exits once its tenure has started so that the tenure can be extended. - Fixed the miner's signer coordinator ignoring the miner's abort flag while waiting for signatures on a block proposal, which could keep a miner thread alive after the relayer tried to stop it (#7703).
- Fixed a miner stall where a different proposal of the miner's own tenure-start block reached consensus while the miner was waiting on its replacement. The miner now detects the accepted sibling, adopts it, and continues its tenure on top of it instead of re-sending the replacement until the next Bitcoin block.
- The signer's capitulation check now ranks equally-weighted candidate miner views deterministically (newest parent-tenure knowledge first) instead of by hash-set iteration order, so signers could flip between views while the split lasts.
- Signers no longer sign a block in a tenure after permitting the active miner's tenure to reorg it. Previously a block that was still being validated or pre-committed when the reorging tenure's burn block arrived could be signed afterwards, leaving the reorged tenure with more globally accepted blocks than the reorg rules allow.
- Signers now always allow the transition of a block to
GloballyAccepted, since this comes from the node and should override any other state which is just based on signer messages. - Fixed a chain stall where signers, after rejecting an invalid sortition winner, fell back to the prior tenure but re-judged the already-permitted reorg. The reorg'ed tenure was judged against blocks which landed later, and the signers concluded with no valid miner.
- The signer's local state machine no longer stays
Uninitializeduntil the next burn block when a transient stacks-node error interrupts its burn-block arrival processing or its startup initialization; the arrival is parked as pending and retried, with exponential backoff (up to 16 seconds) while the node keeps erroring. - Miners now re-propose a block after
miner.transient_rejection_retry_timeout_ms(default 5 seconds) when some signers rejected it for a transient reason (NoSignerConsensus,ConsensusHashMismatch,NoSortitionView,ConnectivityIssues, or anUnknownParent/NotFoundErrorvalidation failure), instead of waiting for the full rejection-based timeout (e.g. 90 seconds). Such a block is also re-proposed, rather than abandoned, when its rejections only exceed the blocking minority because of transient rejections. This removes a long stall at the start of a tenure when a few signers are slow to converge after a new burn block.
Removed
- Removed the
heliumnetwork mode and its legacy Stacks 2.x run loop, which was incompatible with Epoch 3 (Nakamoto). Use Clarinet or a private testnet (nakamoto-neonmode) for local development. - Removed
mocknetnetwork mode. This used a simulated in-process burnchain incompatible with Epoch 3 (Nakamoto). Use Clarinet or a private testnet (nakamoto-neonmode) for local development. - Removed dead net/p2p helper functions.
- Removed the unused
disable_block_pushanddisable_microblock_pushp2p fault-injection options. - Removed the never-used shadow block mechanism (
chainstate::nakamoto::shadowand thestacks-inspectshadow commands). The block-commit rule that accepts a missing vtxindex-0 parent in Nakamoto epochs is kept, since sortition history depends on it. - Block headers with the former shadow-block high bit (0x80) set are now rejected instead of having the bit masked off. This is stricter than SIP-044's
version & 0x7fwording but changes nothing in practice: such blocks were never accepted from the network.
Docker images have been published to GitHub Container Registry:
stacks-core
- Debian (glibc): https://github.com/stacks-network/stacks-core/pkgs/container/stacks-core/1380342805?tag=4.0.5
docker pull ghcr.io/stacks-network/stacks-core:4.0.5@sha256:51aa7bf6f990b99fa010ebb23abe78653cf1f1d129ab8e7d88b1fe11a403c97d- Alpine (musl): https://github.com/stacks-network/stacks-core/pkgs/container/stacks-core/1380340057?tag=4.0.5-alpine
docker pull ghcr.io/stacks-network/stacks-core:4.0.5-alpine@sha256:eaa53873c8ed50605e1ee8920c0ee07dcdaaabc6f90144593dead0a23165d2castacks-signer
- Debian (glibc): https://github.com/stacks-network/stacks-signer/pkgs/container/stacks-signer/1380336290?tag=4.0.5
docker pull ghcr.io/stacks-network/stacks-signer:4.0.5@sha256:350ace132032df994716e8fe7521474e73ac21b9751e520f63efe5daaf605a79- Alpine (musl): https://github.com/stacks-network/stacks-signer/pkgs/container/stacks-signer/1380335823?tag=4.0.5-alpine
docker pull ghcr.io/stacks-network/stacks-signer:4.0.5-alpine@sha256:06e42df211f6743b1187867a879274bde99e11f688766fd5613eaaed186b1eee