This release activates NU7 on Mainnet.
Added
- Added
inherit_activation_heights = truefor configured Testnets, allowing
forks to override selected activation heights while retaining the public
Testnet upgrade schedule
(#1097). - Added the admin
preciousblockRPC, which prefers a chain tip over other tips
with equal work, like Bitcoin Core. A later call overrides an earlier one, and
the preference is not kept across restarts
(#1117). - Added the
getstandardfeeRPC, which returns the recommended fee per
logical action in zatoshis (Zakura's ZIP 317 marginal fee) and an estimator
version, using the same result shape as Zebra
(#1352). - Added a
submitsolutionRPC that lets miners submit a prepared block by
workidand solved header
(#754). - Added the
[network.zakura.quic]config section for the native P2P
transport's socket buffers, congestion control, flow-control windows, timers
and admission limits. The defaults keep the previous behavior except for two
admission limits, and an out-of-range value fails at startup and names the
key
(#1354). - Added a
quic_connJSONL trace table that records each native connection's
RTT, congestion window, packet loss and MTU every 10 seconds and at close.
Itsconncolumn matches theconncolumn of the other Zakura trace
tables, and a close row names the cause when this node closed the
connection (#1354). - With
network.zakura.trace_dirset,discovery.jsonlrecords a
discovery_violationrow for each peer that discovery disconnects. The row
names the peer, the message type, the reader stage that rejected the frame
and the error (#1453). zakura-node-services:task::spawn_erasedspawns a future as a
type-erasedtask::ErasedFuture, so tasks with the same output type share
tokio's per-task code.zakuradnow uses it for tasks spawned at startup,
per session, or rarely, reducing generated code
(#1505).zakura-node-services:task::erasedboxes a future as atask::ErasedFuture
for spawners other thantask::spawn_erased, such asJoinSet::spawn.
zakura-network,zakura-rpc,zakura-consensusandzakura-statenow
spawn their startup, per-connection and per-session tasks type-erased,
reducing generated code
(#1519).zakura-chain:ZcashSerialize::zcash_serialize_into_vecappends a value
to an existingVec<u8>.Block,block::HeaderandTransactioncompile
theirVec, size and slice encoders and decoders once inzakura-chain, so
other crates no longer compile their own copies
(#1526).
Changed
-
Verify transparent scripts with the Rust
zcash_scriptinterpreter and stop
building zcashd's C++ script interpreter
(#1247). -
Stop maintaining transparent address and spender indexes in existing and fresh
pruned checkpoint-sync databases, retaining validation and mining support.
Upgrade existing state in place without resyncing, keep existing index rows,
and preserve the choice across restart and rollback. The database format
upgrade prevents older binaries from opening incomplete indexes
(#1366). -
Return an explicit unavailable error for address balance, UTXO, transaction
history, and transparent spender queries when their indexes are omitted
(#1366). -
Allow standalone rollback of databases without transparent indexes with
zakura-rollback-state --storage-mode pruned
(#1366). -
Mining now prefers the valid block that first reached the full-block verifier
when forks have equal work and no operatorpreciousblockpreference applies. Native sync still uses header hash to choose between
equal-work download paths and serves retained bodies from that branch even
when the mining tip differs.
Trusted mirrors preserve the primary's preference when both nodes are upgraded.
Receipt order is local to a running process, so restored forks use the hash
fallback after restart
(#1084). -
The zcashd-compat sync checks,
zakura-watchdogand
deploy/zcashd-compat/sync-check.sh, now allow a height drift of 30 blocks
by default instead of 10, the same time at NU7's 25-second spacing
(#1229). -
A full mempool now evicts the transactions that pay the lowest fee per unit
of ZIP-401 cost, instead of random transactions. A new transaction must pay
at least one marginal fee per 10,000 cost more than every transaction it
evicts, or the mempool rejects it and does not change. Evicted transactions
can return after the next block
(#1233). -
Full-mempool admission now selects victims from an ordered index, updating
only affected groups of at most three transactions instead of rebuilding
scores for the entire mempool on each attempt. Local benchmarks with 8,000
transactions measured approximately 8,200–11,000× faster eviction selection
than the previous heap selector. These measurements cover victim planning
for a small incoming transaction and exclude downloads, cryptographic
verification, and committing the eviction
(#1233). -
The mempool now limits each connected group of unconfirmed transactions
to three members, including parents, children, and siblings. This permits
parent-child-grandchild chains while bounding dependency depth to two
edges and package width to three transactions. A transaction that would
exceed the limit is rejected without changing the mempool
(#1233). -
Limited the RPC TLS listener to HTTP/1.1. Its TLS configuration advertises
no ALPN protocols, so TLS clients already used HTTP/1.1
(#1250). -
Pace legacy block gossip with at most 32 outstanding advertisements. Hold each slot until the peer's block write completes plus 750 ms, with timeouts for peers that do not request or finish an upload. Release the slot after 3 seconds when a connection other than a configured sidecar cannot write the inventory. Prioritize measured heartbeat RTT for locally mined blocks, sending every fourth advertisement to a random remaining peer, and randomize peer order for relayed blocks. Include configured sidecars within the cap
(#1311). -
Moved Zcash chain history trees into the owned
zakura_mmr_treecrate,
preserving history serialization and node behavior while consolidating integer
dependencies. The chain library's exposed history version, node, and error types
now originate from the owned crate
(#1312). -
Replaced compatibility monitoring and deployment checks with shared Python
checks. The fleet watchdog reports failures and recoveries to Slack
(#1318). -
Changed hostname processing to the unicode-rs backend to reduce build
dependencies. Hostnames using characters added in Unicode 17 are now rejected,
including their ASCII Punycode spellings
(#1319). -
zcashd_source = "embedded"now downloads the standalonezcashd
executable that the embeddedvalargroup/zcashdrelease publishes, instead
of extracting it from the release archive. Zakura verifies its SHA256 before
making it executable and installing it, and requires HTTPS on every redirect.
Zakura now hashes a cached executable before reusing it and downloads it
again if its contents differ. A matching cached executable is reused without
network access. The release-specific cache path causes existing
installations to download the embedded release on first use. Legacy
zcashd.sha256files are ignored
(#1326). -
The embedded zcashd compatibility manifest now uses schema version 2. This
is a source-incompatible change to thezakuralibrary's public Rust API:
ZcashdReleaseArtifactreplacesruntime_archive_url,
runtime_archive_sha256andruntime_archive_member_binary_pathwith
runtime_binary_urlandruntime_binary_sha256, and
EMBEDDED_MANIFEST_SCHEMA_VERSIONis now2. Schema 1 manifests are
rejected
(#1326). -
scripts/resolve-zcashd-compat-manifest.shand
make compat-zcashd-preparenow prepare the zcashd-compat Docker build
context from the standalone executable. They verify it before replacing an
existing context.ZCASHD_COMPAT_CONTEXT_DIRreplaces
ZCASHD_COMPAT_ARCHIVE_PATHandZCASHD_COMPAT_EXTRACT_DIR
(#1326). -
Update the embedded zcashd-compat binary and default split-container image to
valargroup/zcashd v1.3.0, which supports NU7 and schedules Mainnet NU7 at
height 3,543,405 to match Zakura. Older sidecars stop following the chain
when NU7 activates
(#1327,
#1596,
#1600). -
Reduced mined-block propagation latency by letting legacy and dual-P2P peers
pull an admitted block while contextual commit continues. Native P2P v2 still
uses inventory and pull; Zakura does not push full blocks
(#754). -
Repeated
getblocktemplatepolls of unchanged template content now reuse the
candidate the node already prepared, instead of verifying the same
transactions again under each newworkid
(#754). -
invalidateblocknow fails fast when every block write slot is in use, instead of queueing
behind in-flight contextual writes (#930). -
A mining template parent whose speculative preparation misses its deadline is not speculated on
again until the chain tip changes. Foreground template recovery and block submission still
validate normally (#930). -
The new
mining.template_preparation.stale_before_dispatchmetric counts queued templates whose
parent the chain left before any verification started.mining.template_preparation.cancelled
now counts only preparations the node gave up on after dispatching them, and
mining.template_preparation.abandonedis gone: the node no longer observes a preparation's
answer separately from its computation (#930). -
The mempool only bans a peer for a transaction failure that does not depend
on the node's chain tip. These failures include invalid transaction
structure, outputs worth more than inputs, invalid Sprout proofs,
non-canonical proof sizes, spends repeated inside one transaction, expiry
heights above the maximum, and transparent coinbase spends to transparent
outputs. For v5 and later transactions, they also include invalid
signatures, scripts, Sapling and Halo2 proofs, Orchard pool deposits after
NU6.3, and ZIP 218 shielded limits after NU7, because those transactions
encode their consensus branch ID. A
v4 signature hash uses the branch ID of the node's own tip, so v4 signature
and Sapling failures only start a cooldown. -
Selected failures that previously banned peers now start a cooldown: the mempool
ignores transactions from the peer's IP for 10 minutes. Each later cooldown
doubles up to 2 hours and disconnects the peer without banning it. Failures
already in flight during a cooldown do not count as another strike. These
failures include v4 signature and Sapling failures, v4 transactions that
exceed NU7 shielded limits, and Sprout deposits after Canopy. Branch ID
mismatches, unsupported transaction versions, and expired transactions keep
their existing no-penalty policy. Transactions that declare a later network
upgrade than the tip's, unknown branch IDs, lock times, missing inputs, spends and
anchors in the chain, policy rejections, and transactions that the node
re-verifies after a chain tip reset start no cooldown. Block verification
keeps its existing ban policy
(#960). -
The mempool never bans, cools down, or disconnects a zcashd-compat sidecar
(zcashd_compat.block_gossip_peer_ips). A sidecar syncs from the node, so
its tip trails the node's tip
(#960). -
Added
CapacityBusyoutcomes to the public header-serving APIs. Callers must
handle the new enum variants. Bumpedzakura-node-servicesto 5.0.0,
zakura-networkto 10.0.0, andzakura-stateto 11.0.0, with dependent
crate updates to keep the published dependency graph consistent. -
Replaced the Iroh library under the native Zakura P2P stack with the
Zakura-ownedzakura-quictransport, built on upstreamquinn-proto. Node
IDs, key files and bootstrap entries stay the same. The native protocol
moves fromp2p-v2/2top2p-v2/3, so this release reaches earlier
releases only over the legacy Zcash protocol
(#1354). -
Changed native dialing to offer every supported
p2p-v2/Nprotocol version
in the TLS handshake. Releases that support overlapping versions then
connect natively across a future protocol break
(#1354). -
Changed native connection admission to refuse attempts over the per-IP and
pending-handshake limits before the TLS handshake instead of after it. An
attempt from an unverified address over the per-IP or pending-handshake
limit gets a QUIC Retry instead of a refusal, so spoofed sources can't lock
out real hosts. An
IP at its connection limit still gets one handshake at a time, so a
registered peer can redial
(#1354). -
Changed the native dial timeout to cover the last staggered address attempt
and the configured QUIC handshake timeout, instead of a fixed 10 seconds
(#1354). -
Changed native connection admission so a QUIC handshake must finish within
10 seconds, and a connection attempt from an unverified address gets a QUIC
Retry once 8 handshakes are in progress. Before, a flood of spoofed packets
could hold every handshake slot and turn away every honest peer
(#1354). -
Changed native dialing to wait at least 10 minutes before redialing a peer
that answers with a different P2P protocol version, even when the peer
advertises new addresses
(#1354). -
Changed native discovery so a relayed node record carries a signed extension
area, and a node skips a relayed record it can't decode instead of
disconnecting the peer that relayed it. Changed the native control handshake
to ignore fields a newer release appends. Later releases can then extend
records and control messages without another protocol break
(#1354). -
Changed native discovery to verify node-record signatures with the same
strict Ed25519 key policy as the TLS handshake. A node now rejects a record
whose key could never authenticate a dial
(#1354). -
Changed native discovery dialing to start at most one handshake per IP of a
record, using the first address the record lists for that IP. A successful
dial now clears dial backoff only for the IP that authenticated
(#1354). -
Changed the transport's log targets from
iroh,iroh::magicsockand
iroh_quinn_udptozakura_quic. Update tracing filters that name the old
targets (#1354). -
Upgraded
jsonrpseeto 0.26 andtowerto 0.5, which removesthiserror
1.x from the dependency graph. Library crates that expose buffered services,
the RPC middlewares orjsonrpseetypes now use the new versions' types.
JSON-RPC calls keepzcashderror codes, including inside batch requests
(#1392). -
The plain HTTP JSON-RPC listener now closes connections that send no request
within 30 seconds, and JSON-RPC notifications sent over HTTP now get anull
response body instead of an empty one
(#1392). -
Refuse to start block sync when its configured session slots cannot fund at least one served
request per slot, instead of warning and advertising one request
(#1395). -
Reduced the state writer's work when it validates each new block by reusing
the header hashes it already computed while reading recent headers
(#1398). -
Reduced state-service overhead when processing new blocks by sharing unchanged
note commitment tree and anchor indexes between pending chain snapshots
(#1399). -
Limited JSON-RPC batch requests to 1,000 entries, counting notifications and
invalid entries. Larger batches now get a single-32010"The batch request
was too large" error with HTTP status 200, and none of their entries run,
which bounds the decoded batch memory a client can hold while a call waits.
Clients that send larger batches need to split them
(#1407). -
Reduced state-service overhead when processing new blocks by sharing unchanged
spent-output, nullifier, transaction-location and block-hash indexes between
pending chain snapshots
(#1443). -
Native discovery now uses stream version 2 behind capability bit 9. Each
discovery message has its own frame type and size limit, and nodes no longer
exchange discovery with peers that only support version 1. Node records,
service lists, and service summaries have tighter size limits
(#1448). -
A node record carries at most 8 services. A node that advertises more fails
to start, andupdate_advertised_servicesreturns an error, instead of
silently dropping services from its record
(#1448). -
Panic reports are no longer colored and no longer include a span trace. They
still end with the diagnostic metadata section. This removescolor-eyreand
its backtrace dependencies
(#1470). -
Switched Zakura native header sync to stream version 9 on capability bit
10. A node now subscribes to a peer's headers with renewable credit instead
of sending oneGetHeadersrequest per page, and acknowledges only headers
that state applied. Version 8 and capability bit 5 are retired with no
compatibility path, so native header sync works only between nodes that run
version 9. Older peers stay connected for every other stream
(#1474). -
zakura-chain:ZcashReader::with_limitnow returns aLimitedReaderguard
that dereferences to the sameZcashReader, instead of a
ZcashReader<io::Take<..>>, so nested decoders are no longer compiled once per
limit depth (#1489). -
zakura-chain:ZcashDeserialize::zcash_deserialize_fromis now a required
method, andZcashReader::read_valuedecodes nested values on the same reader
instead of wrapping stream input in a newZcashReader. Implementations that
only provided the streamingzcash_deserializemust now implement
zcash_deserialize_from. Nested decoders are no longer compiled once per
nesting depth, shrinking thezakuradbinary by about 1.5 MB
(#1496). -
Selected node-only features of the Common cryptography dependencies so block
validation and coinbase construction no longer pull in wallet key-derivation
dependencies (#1500). -
A config section written as a TOML array, such as
rpc = [], is now rejected
with the field path instead of loading as that section's defaults
(#1501). -
Config values under
network,network.cache_dir, the configured-network
checkpoints, andzcashd_compat.zcashd_extra_argsnow accept the same scalar
conversions as other config fields, and their errors name the offending field.
Unknown keys next tonetwork.paramsare now rejected instead of ignored, and
paramsorregtestanywhere other than directly insidenetworkis rejected
as before (#1502). -
Reduced the size of release
zakuradbinaries by about 5% by compiling
them with fewer codegen units
(#1520). -
zakura-chain:ZcashSerialize::zcash_serializeand the
zcash_serialize_*helpers now takewriter: &mut Wwith
W: io::Write + ?Sized, and implementations pass that writer to nested
values. Implementations must update their signature, and callers that passed
an owned writer must pass&mut writer. Nested encoders are no longer
compiled once per nesting depth
(#1525). -
zakuradcommand-line help and errors are no longer colored, and mistyped
subcommands no longer suggest a correction. This removes nine dependencies
from the build (#1539). -
zakura-node-services:rpc_client::RpcRequestClientnow uses hyper instead of
reqwest. Its calls returnhttp::Response<Bytes>with the body already read, and
fail with the newRpcRequestError. Newwith_basic_authandgetmethods send
authenticated requests andGETrequests
(#1556). -
The workspace's RocksDB dependency is now
zakura-rocksdb0.24.0, Zakura's
fork ofrocksdb0.24.0, which compiles the bundled RocksDB C++ as a unity
build to shorten clean builds. RocksDB itself is unchanged, and the on-disk
database format is unaffected. Crates that userocksdb0.24 types alongside
zakura-statenow needzakura-rocksdbinstead
(#1558). -
Schedule NU7 on Mainnet at the ZIP 259 block height 3,543,405 and update the
Zakura Common libraries to the final4.0.0release. ZIP 218 moves the
Mainnet third halving to height 6,132,390, and the existing NSM calculation
schedules reissuance at height 8,939,664
(#1588). -
Extended this release's Mainnet end of support to height 3,640,098, with the
first unsupported block estimated at December 4, 2026 UTC, superseding the
November 5 end of support set in the release candidates
(#1460). Unlike earlier
releases, which deliberately halted before the expected NU7 activation,
support now extends past the Mainnet NU7 activation at height 3,543,405,
which this release follows. Warnings still begin three days before the halt
(#1599).
Deprecated
- Deprecated
zakura.block_sync.max_blocks_per_response. Native block sync now
always requests one block perGetBlocksand serves at most one block per
GetBlocks. Configs that set the key still load, the value is ignored, and a
warning is logged at startup when it asks for more than one block
(#1422).
Removed
-
Removed the
comparison-interpreterfeature fromzakuradand
zakura-script
(#1247). -
Removed the unused ZIP-401 low-fee eviction penalty and
VerifiedUnminedTx::eviction_weight
(#1233). -
Removed runtime tracing-filter reload and the
filter-reloadbuild feature.
Removetracing.endpoint_addrfrom configuration and settracing.filter
before starting the node instead
(#1266). -
Removed the manual lightwalletd integration-test harness and the
lightwalletd-grpc-testsbuild feature
(#1266). -
Removed the optional
flamegraphCargo feature and built-in tracing-span
flamegraph output. Removeflamegraphfrom build feature lists. Existing
tracing.flamegraphconfiguration entries are accepted and ignored, and omitted
from generated configs. Use the external CPU profiling workflow for sampled CPU
flamegraphs
(#1306). -
Removed the terminal progress display. Legacy
progress-bar,howudoin,
andindicatifbuild features remain accepted as no-op compatibility flags.
Existingtracing.progress_barsettings are accepted and ignored. They no
longer redirect logs to a file; usetracing.log_fileto select a log file
explicitly (#1310). -
Removed Sentry reporting, the optional
sentryfeature, and the Rust
zakura-watchdog. Local panic diagnostics remain available
(#1318). -
Removed the
tarandflate2dependencies ofzakurad, which also removes
tar,filetimeandxattrfrom the build
(#1326). -
Coinbase transactions no longer pay unified miner addresses through the
Orchard pool at pre-NU6.3 heights; such addresses fall back to their Sapling
or transparent receiver instead. At NU6.3 and later, Orchard receivers are
still paid through the Ironwood pool. Only custom regtest configurations are
affected in practice
(#1337). -
The
mining.prepared_cache.mismatchesandmining.prepared_cache.work_id_conflictsmetrics,
which counted work-ID collisions in a cache that is now keyed by candidate content
(#930). -
Removed native block-sync stream version 2 (capability bit 3). Nodes now
negotiate only regulated version 3, so peers that offer only version 2 no
longer exchange blocks over native block sync
(#1174). -
Removed bundled Sapling proving parameters and Sapling miner reward
construction. Sapling-only mining addresses now return an explicit
getblocktemplateerror. Unified mining addresses use their Orchard receiver
for Ironwood rewards after NU6.3, or their transparent receiver when available.
Sapling transaction and shielded coinbase validation remains supported
(#1341). -
Removed QUIC NAT traversal.
[network.zakura] nat_traversal = truenow fails
at startup; set it tofalseor delete it. With relays off, NAT traversal
never gave a node new inbound reachability
(#1354). -
Removed the
zakura-quicqlogcargo feature and the
[network.zakura.quic] qlog_dirsetting, which release builds already
rejected. A config that setsqlog_dirnow fails with an unknown-key error.
This also dropsqlogand its dependencies from the build
(#1405). -
zakuradno longer prints a prefilled GitHub issue link in panic and error
reports. Panic reports still include the diagnostic metadata section
(#1406). -
Stopped publishing the
zakura-tower-batch-controland
zakura-tower-fallbackcrates. Their batch and fallback middleware is now a
private part ofzakura-consensus, so the types of thezakura-consensus
batch verifier statics can no longer be named outside that crate; the
verifiers still work throughtower::Service
(#1423). -
Removed the
schemarsdependency and theJsonSchemaimplementations on
zakura-chain,zakura-network, andzakura-rpctypes. Therpc.discover
OpenRPC document is unchanged
(#1427). -
Removed gRPC server reflection from the indexer endpoint. Tools such as
grpcurlmust provide the indexer protobuf schema explicitly
(#1469). -
Removed the optional
tokio-consolebuild feature and its async task debugger
integration fromzakurad
(#1472). -
Config durations no longer accept week, month, or year units (
w,wk,wks,
week,weeks,M,month,months,y,yr,yrs,year,years).
A config that uses them now fails to load; write the duration in days or
smaller units instead. Durations of a month or more are now written in days,
such as400days 4h
(#1486). -
Removed OpenTelemetry trace export and the optional
opentelemetrybuild
feature. Existing
tracing.opentelemetry_endpoint,tracing.opentelemetry_service_name, and
tracing.opentelemetry_sample_percentsettings are accepted and ignored, and
theOTEL_*environment variables are no longer read. The Docker
observability stack no longer includes Jaeger
(#1524,
#1531). -
Removed systemd-journald logging. The
journaldbuild feature is still
accepted but does nothing, and existingtracing.use_journaldsettings are
accepted and ignored. Generated configs no longer includeuse_journald
(#1536).
Fixed
-
Keep block verification responsive during concurrent submissions of different
block bodies sharing a header by indexing receipt identities outside the
registry lock
(#1084). -
Repair VCT roots for headers received near the network tip in one request
instead of one request per block. This speeds up sync when a release raises the
checkpoint above headers the node already holds
(#1208). -
zakurad now sends its zcashd-compat sidecar every block it requests. It used
to stop each response at 1 MB, which stalled a sidecar catching up through
large blocks until its download timeout
(#1242). -
Bounded pending RPC TLS handshakes and their duration, bounded open RPC TLS
connections, closed TLS connections that send no request headers for 30
seconds, and cancelled TLS connections when the listener stops, preventing
silent clients from retaining unlimited sockets and tasks
(#1250). -
Admission under mempool capacity pressure now requires both the incoming
transaction's own fee rate and its combined rate with all unconfirmed
ancestors to outbid every eviction victim by the existing increment. This
prevents protected ancestors from diluting the retained package below the
victim's rate and stops the fixed-price chain eviction cycle. The additional
calculation sums at most two ancestors once per attempt; admission with
spare capacity keeps its existing behavior
(#1253). -
Drop a legacy peer's
FindBlocks/FindHeadersstall count when the peer leaves the
peer set, so a reconnect at the same address starts with a fresh count
(#1255). -
Fixed startup crashes when OpenTelemetry tracing is enabled
(#1272). -
Normalize OpenTelemetry endpoint paths while preserving queries, ignoring
fragments, and accepting trailing slashes on existing trace paths
(#1273). -
Omit OpenTelemetry endpoint URLs from startup status logs and replace the full
startup configuration dump with selected diagnostics to avoid logging credentials
(#1273). -
Fixed open-file limit handling to preserve an already usable limit when
kernel caps are lower and to verify actual capacity after limit errors
(#1289). -
Avoid startup panics from non-Unicode environment variables: ignore unrelated
variables and non-Unicode names, and report an error for non-Unicode config
values (#1313). -
Fixed a zcashd-compat sidecar occasionally staying one block behind zakurad
until the next block arrived. zakurad no longer asks the sidecar for new block
hashes or headers unless it advertised more blocks than zakurad has: zcashd
leaves those requests unanswered, and each one held up the block announcements
the sidecar follows (#1331). -
Avoid repeated native header discovery requests for peers whose advertised tips
are at or below the local finalized height
(#1344). -
Mempool transaction requests by ID no longer return a same-txid variant
with different signatures or proofs, which made requesting peers
disconnect an honest node
(#1356). -
Native legacy requests no longer disconnect a peer that answers a block or
transaction request with NIL while it is still starting up, or whose
response stream is reset partway through an item after a write timeout.
The request falls back to another peer instead
(#1361). -
The mined-block RPC observes a completed contextual commit before sending an early-relay
event. A withdrawal waiter retains the rejection revision so a parent change cannot erase an
unread rejection (#930). -
invalidateblockno longer disables optimistic mined-block relay until a matching
reconsiderblock. A contextually rejected candidate no longer blocks early relay for its
siblings either: the server now authorizes early relay per candidate rather than once per
parent, so several candidates on one parent can each be relayed early, each still gated on
valid proof of work, an idle block writer, and the relay preflight
(#930). -
Speculative mining template preparation can no longer accumulate verification work. A
preparation the node stops waiting for, because its deadline passed or the chain tip moved,
keeps the speculative worker until its computation actually finishes, so at most one speculative
verification runs at a time. A block or transaction that fails a check now finishes the checks
it dispatched before reporting the failure, because dropping a check does not stop the batch
work it already queued. A template whose parent the chain has already left starts no
verification at all (#930). -
A server template that is condemned after its preparation deadline is still withdrawn. The node
cannot stop a verification it dispatched, so a rejection that arrives late is the only rejection
it will get; discarding it left miners working on a template the node knew was invalid
(#930). -
A rejection published while
getblocktemplatewas reading the withdrawal state is no longer
acknowledged unread, so the withdrawal reaches long-polling miners immediately instead of at the
next poll. A request whose tip went stale can no longer erase the current parent's withdrawals,
a returned template is re-checked against both the rejection state and the chain tip before it
is published, and a rejection recorded while the chain has briefly left and returned to a parent
is no longer dropped (#930). -
mining.preparation.duration_secondsnow measures the whole preparation, including semantic
transaction verification, rather than only the contextual check at the end. The
state.semantic_commit.prequeue_checks.duration_secondsand
state.semantic_commit.queue_and_commit.duration_secondshistograms again measure mined
submissions only, instead of mixing in every synced block
(#930). -
Evict auxiliary input instead of refusing transitions when the auxiliary store is
full. Eviction keeps authenticated input and the next commit's input, removes input
off the selected path first, then levels the fullest per-header bucket, and never
evicts headers. VCT repairs reclaim available input slots in a saturated database.
Bound repair ranges to reclaimable capacity and retain their new roots before reporting
success. Preserve usable disputed roots when peers supply only body-size hints.
Replace non-authoritative input in full repair buckets, including
after restart, and drop new input that a full bucket cannot take without refusing
its header (#950). -
Stop the daemon when the block writer fails, including when read-state clones
still hold its task handle or shutdown races with the failure
(#950). -
Preserve transaction source attribution across mempool tip resets, so a
re-verified transaction can still ban its peer. -
When the peer cooldown history reaches capacity, evict the entry whose
cooldown ends first. Use an expiry index to find it without scanning the
history. -
Fixed the mempool downloader so that a peer that answers a transaction
request with a different transaction no longer leaks that request's download
slot. The mempool now treats the response as a failed download
(#960). -
Notify waiting header-sync peers when serving capacity becomes available
(#992). -
Close header-sync sessions when the outbound queue drops a capacity refusal. Keep capacity waits registered across peer events and release leases from abandoned state acquisitions.
-
For the first 40 blocks after each network upgrade activates, the mempool
starts no peer cooldown for transactions built for the previous upgrade.
Peers whose tips lag the activation relay these transactions, so they no
longer get cooldowns or disconnects
(#1238). -
Stop dialing or caching the ephemeral source endpoints of disconnected inbound legacy peers,
remove banned IPs from otherwise preserved peer caches, and cap inventory advertisements before collecting them
(#1256). -
Reset a Zakura block-sync peer's status, range, and RTprop when it replaces its session, so
a reconnecting peer that withholdsStatuscannot stall the download floor
(#1259). -
Repair both headers when a VCT boundary check cannot tell which of two auxiliary
deliveries is wrong, and try every untried delivery pair at that boundary before requesting
another repair. A supplier that served one corrupt auth-data root can no longer wedge VCT
checkpoint sync at the preceding height
(#1302). -
Refund queued datagram capacity when a native connection exits and discard
late stop-sending codes after its send handle closes
(#1354). -
Require source validation before a native handshake consumes the last per-IP
slot, including the slot reserved for redialing
(#1354). -
Advertise newly available native block bodies within one second
(#1354). -
Rank block providers by availability at the missing height, so a peer that
cannot serve that height does not delay recovery
(#1354). -
Bound the header bootstrap wait when the node has no committed blocks
(#1354). -
Return an unavailable-range response when native block-serving queues are
full, so requesters can retry another provider without waiting for a timeout
(#1354). -
Preserve the selected native header tip when an equal-work fork arrives, so
body downloads continue until a stronger eligible fork requires a switch
(#1381). -
Fixed macOS and Windows builds after the native QUIC transport change (#1387).
-
Fixed RPC metrics growing without bound when clients call method names the
node doesn't serve. Those calls are now counted under theunknownmethod
label instead of adding new metric series
(#1407). -
zakuradutility commands that fail, such asprune-state,rollback-state
andverify-historical-treestates, no longer exit before their queued log
lines are written, so their final error message can't be lost.zakuradno
longer depends on the Abscissa application framework. Its command line,
configuration, output and exit statuses are otherwise unchanged, except that
--verboseno longer logs Abscissa's own "registered component" debug lines
(#1419). -
Zakura now reserves header-serving capacity for bounded repair requests whether
the supplier retains or has finalized the target. Both use the same locator
and paging rules (#931). -
Zakura rejects duplicate and full-capacity header requests before reading the
target path. Reserve admission bounds ancestry work to 4,000 headers and keeps
ordinary slots available when a reserve lease is active
(#931). -
Zakura serves a header request from the nearest locator ancestor of the target,
whatever the locator order (#931). -
Keep a reconnected Zakura block-sync peer's registry entry and serving state when its previous
session's disconnect is handled late, so the new session's status and requests stay visible
(#1421). -
Hand a Zakura header-sync target to another peer's cached status as soon as
its owner releases it, so an owner that answers Busy cannot hold the target
and a peer held back at capacity takes the next free slot. Each status starts
at most one attempt until its peer sends a new one or the header authority
changes, so failed attempts never repeat in a loop. Ordinary offers deferred
before sending by local headroom, resource stalls, or metadata repair remain
available when the blocker clears
(#1447). -
A node that closes a discovery-only connection after its exchange first
waits up to 2 seconds for its own answers to the peer's queries to be
written, so the peer's exchange no longer ends without them
(#1453). -
Start a Zakura header sync peer's newly advertised tip as soon as the node's
work with that peer ends without new headers, as after the peer reorganizes,
times out, or hits a local failure. A tip the peer advertised during that work
was dropped, so the node waited up to 30 seconds for the peer's next status
(#1476). -
The
zakura.p2p.reservation.refusedmetric with reasonunsolicitedno
longer counts successful response claims
(#1477). -
zakuradwarns at startup when it listens for native peers but advertises no
discovery address they can dial, for example a wildcardlisten_addrwithout
network.external_addr. Peers drop such a node's discovery record
(#1477). -
Fixed dual-stack nodes stalling for 30 minutes and restarting when legacy fallback waited for a
native full-verification commit whose parent only fallback could supply. Fallback now takes over
those commits immediately
(#1509). -
Bound header locator reads through accepted state-work completion, including
target eviction, disconnect and caller timeouts
(#1510). -
Avoid repeated cached header-offer scans when scheduling opportunities are
unchanged, including changes to irrelevant peer Status metadata, while promptly
retrying after capacity and ownership changes
(#1511). -
Capped the decoded memory held by native block sync's submission window at
512 MiB above the last checkpoint, so blocks that decode much larger than
their wire size can no longer fill the 401-block window with decoded bodies.
Checkpoint ranges keep the full window
(#1514).
Security
- Bound nested collection allocations by the bytes available in native block
messages, including when buffered blocks are decoded again. - Check nested allocation counts against available bytes when decoding legacy
blocks, transactions, and header collections. - Updated
rustlsto 0.23.45 so TLS 1.3 handshake messages that should be
encrypted are no longer accepted in plaintext (RUSTSEC-2026-0285)
(#1240). - Ban peers that serve a checkpoint block with forged authorizing data, and request that
block again from another peer. Retry the forged block's queued descendants without
restarting the sync round
(#1251). - Stop leaking Zakura upgrade dials. A timed-out legacy upgrade now cancels its dial, and an
upgrade dial stops after six consecutive failures instead of redialing a peer-chosen address
forever (#1257). - Bound block, transaction and inventory list decoding in native legacy gossip
by the bytes available in each message
(#1345). - Native legacy gossip now rejects transaction responses whose available or
missing transaction IDs were not requested, as it already did for blocks
(#1348). - Native legacy gossip now rejects block and transaction responses padded
with bytes after the encoded item
(#1348). - Native legacy requests now disconnect a peer whose response proves a
protocol violation, such as an unrequested block or a malformed payload.
Previously the peer stayed connected and could be chosen again for
source-attributed downloads
(#1349). - Native legacy requests now try up to three peers, starting from a rotating
position, so a few peers that answer with nothing can no longer keep
block and transaction requests from reaching honest peers. A peer that is
being disconnected is no longer chosen for new requests
(#1362). - Bounded the work, memory and in-flight requests a peer can demand from
GetBlocks serving on native block sync, advertising at most 2,816 in-flight
requests per peer by default. Block-sync configs with more than 3,633
inbound plus outbound peer slots are now rejected at startup
(#1174). - Updated rustls to 0.23.45, which fixes RUSTSEC-2026-0285
(#1354). - Moved the native transport to
quinn-proto0.11.19, which fixes
GHSA-qfwj, GHSA-hmxj, GHSA-2hv7, GHSA-465w, GHSA-3g6h, GHSA-53rm and
GHSA-wppq (#1354). - Native legacy requests now reject
FindBlocksanswers with more than the
500 hashes a Zakura node sends, before buffering them, and disconnect a
peer whose answer repeats a hash
(#1368). - Native legacy transaction requests now try the next peer after 8 seconds
from the start of an exchange, or 13 seconds including time queued behind
other requests to that peer, whichever comes first. This gives a fallback
peer a chance to answer before a mempool download's deadline, without
guaranteeing it finishes. A request cancelled while waiting for its turn no
longer delays later requests
(#1369). - Disconnect a block-sync peer that queues more responses than its live requests owe, or that
sends a body whose transactions do not match its header's Merkle root. Charge block-sync
admission for the measured decoded size of bodies when it exceeds the calibrated estimate
(#1395). - Cap the block-sync stream's inbound queue at 8 frames, so a peer's queued raw block bodies
stay under about 20 MB per connection however many requests are in flight
(#1410). - Discovery verifies a peer's announced node record outside the address book
lock, with at most a quarter of the CPU cores verifying at once, and skips
records it already holds. A record with a bad signature now disconnects the
peer even when it has expired, and the node announces its own record at most
once every 15 seconds
(#1449). - Discovery answers peer-sample requests through the regulated serving path,
with node-wide limits on concurrent work and queued output. A sample costs
the same small amount of work whatever the address book size. A peer with
more than two unanswered sample requests is disconnected. Discovery
exchanges run every 15 seconds, whatever the configured refresh interval
(#1450). - Discovery accepts a peer-sample response only when it answers an
outstanding request, and only up to the size that request asked for.
Relayed node records are verified outside the address book lock, with the
same CPU budget as announced records. A relay that forwards a record with a
forged signature, a malformed key, or the wrong network or chain is
disconnected. Each relay can add at most 256 third-party records to the
address book
(#1451). - Discovery answers service-summary requests through the regulated serving
path, with node-wide limits on concurrent work and queued output. It builds
only the summaries a request asks for, after output space is reserved, and
sends exactly one answer per request. A peer with more than two unanswered
service-summary requests is disconnected
(#1452). - Discovery accepts a service-summary response only when it answers an
outstanding request, and only up to the size and the services that request
asked for. The discovery reader no longer hands peer header-sync summaries
to header sync, which ignored them, so a full header-sync queue cannot stall
it (#1453). - Disconnect a Zakura header sync peer whose headers fail contextual
difficulty or time validation, never acknowledge the failed headers, and
refuse its header sync readmission forunproductive_peer_cooldown
(60 seconds by default). State's rejection of such a branch is now
attributed to the peer instead of being logged as a localstate_error.
Local failures, such as a pruned parent or a stale preparation, keep the
connection (#1475). - Capped how much body work native block sync queues ahead. Peers that only
sent a status could each add a full 32,000-request window to the refill
target, so 256 of them could raise it to millions of heights, leaving only
the header lead to bound the queue. The target now stops at the 32,768
requests the node can have in flight at once, plus one 4,000-height refill
batch
(#1506). - Capped native P2P connections by a new
[network.zakura] peer_memory_budget_bytessetting, 8 GiB by default. Each connection's QUIC
windows can hold 64 MiB that the node can't reclaim, so 256 connections
could hold 16 GiB. The budget reserves 256 MiB for each bound socket's
datagram queue and divides the rest by that cost. The default
max_connectionsdrops from 256 to 124. A larger configured value is lowered at startup with a warning,
and startup fails when the budget admits fewer than 19 connections on
Mainnet (16 elsewhere), or fewer than a smallermax_connections
(#1507). - Capped the QUIC connections a native node holds. Live connections,
handshakes included, now stay withinpeer_memory_budget_bytes, and a
closed connection releases its stream buffers and waits out its drain in a
separate small allowance, so a peer that gets rejected and reconnects can't
pile up buffers, and its closed connections take no slot from other inbound
peers. The budget funds the admitted
connections plusmax_pending_handshakesof headroom, so the default
max_connectionsdrops from 124 to 92, and a larger configured value is
lowered at startup. Discovery dials leave room for an inbound connection,
even when candidates advertise several addresses that never answer.
The memory budget is checked only when P2P v2 is enabled, so an
unused undersized budget cannot prevent legacy-only startup
(#1508). - Bound header sync reactor time per
Statusunder floods. One peer that keeps
changing its own target can no longer make every message rescan and sort all
idle offers. Full rescans now run at most 32 times back to back, then once per
millisecond after the previous rescan ends. The remaining per-Statuswork still grows slightly with the
number of connected peers, and a tight flood averaged about 2 µs per message
at 512 sessions
(#1512). - Disconnect a header sync peer that sends a page whose headers do not form
one chain from the page parent, before those headers are prepared
(#1530). - Keep header-sync state watches progressing under Status floods and connection churn, bound lifecycle notifications, and avoid rebuilding repair supplier membership on every Status (#1566).
- Reject TLS certificate chains built from the eight compromised eMudhra
intermediate CA keys when validating the managed zcashd download, by updating
rustls-webpki to 0.103.16 (which distrusts their public keys) and webpki-roots
to 1.0.10 (which removes the emSign ECC Root CA - G3 trust anchor)
(#1582).
This release also ships Mainnet release state up to height 3,524,060.