Repository navigation
v0.7.0
📄 Canonical release notes:
docs/release-notes/0.7.0.md
satd 0.7.0
satd 0.7.0 is a minor release on the 0.x line. It includes every fix shipped
in 0.6.1 to 0.6.3.
Several mempool, relay, RPC and privacy defaults change to match Bitcoin Core;
read the Upgrade notes before upgrading. No reindex is
needed.
Highlights
- Bitcoin Core's ZMQ notifications, byte for byte, so LND's
bitcoind
backend and Umbrel's apps run against satd unchanged
(details). - Package CPFP in
submitpackage, as in Core, so a Lightning commitment
signed at a stale feerate can still be closed (details). - TRUC (version 3) transactions. BIP 431's topology and size limits, with
sibling eviction, as in Core (details). - OP_RETURN data pruning.
-pruneopreturn=<N>(off by default) strips
large OP_RETURN payloads from deep blocks while txids and APIs keep working;
-restoreopreturnsundoes it (details). - Headers and P2P messages read as Core reads them. Headers that fork
below the last checkpoint are refused and their sender banned
(details);version,addrv2and other payloads that
Core accepts are no longer refused (details).
Bitcoin Core ZMQ notifications
satd now publishes Core's ZMQ topics; it used to ignore -zmqpub* with a
warning. -zmqpubhashblock, -zmqpubhashtx, -zmqpubrawblock,
-zmqpubrawtx and -zmqpubsequence take Core's syntax (multi-valued, one
socket per address, unix:<path> or ipc://<path>), -zmqpub<topic>hwm sets
the high-water mark (default 1000, 0 for none), and all ten options take
effect at restart. getzmqnotifications, help's == Zmq == category and
-debug=zmq behave as in Core.
Messages are Core's byte for byte, in Core's order within each event; a reorg
announces each block from the first with more work than the old tip, and
invalidateblock announces none. An unbindable address turns ZMQ off without
stopping the node; an invalid port stops startup with Core's message. Unlike
Core, satd promises no order between block-derived and mempool-derived
messages (per-topic sockets, as LND and Umbrel use, cannot tell), publishes
nothing for blocks connected during IBD or -reindex, drops a subscriber's
messages past 256 MiB queued, and lacks a few address forms; see the
Operator Manual
and CORE_DIFFERENCES.md. The streaming API remains the recommended
interface for new software.
A new PR-gating canary runs LND in bitcoind mode against satd's built-in
ZMTP 3.0 publisher; five of seven interface_zmq.py sub-tests pass.
/metrics gains six satd_zmq_* series. -eventszmqbind is unchanged.
Streaming Consumption API
- No events lost in a deep reorg. The chain-event channel grows from 64
to 1024 entries; a reorg of more than about 30 blocks could drop events. - A package's parent is announced before its child, on the mempool stream
and on ZMQ, for a zero-fee parent admitted with its child. - A watch descriptor with a hardened step is refused
(DescriptorError::HardenedDerivation, renamed fromHardenedWildcard)
instead of panicking the events task:xpub…/0h/*cannot be derived from
a public key.
Block validation
- Tapscript signature hashes cost what they cost in Core. The Rust engine
(authoritative under-consensus=rustorcpp-shadow, shadow under
the defaultrust-shadow) re-hashed the annex per signature check; it
now follows Core'sSignatureHashSchnorr. Results are unchanged. - A custom signet syncs past height 50,000: a
-signetchallengesignet no
longer inherits the default signet's checkpoints or assumevalid block. - Contextual header rules on every path. Timestamp (above MTP, at most two
hours ahead) and BIP 34/66/65 version rules now also apply toheaders
messages and IBD blocks, before indexing, before storing and at connect, as
Core'sAcceptBlockHeader. A header refused only for being ahead of the
clock no longer penalises its sender. - Transaction rules before a block is stored. Context-free checks,
bad-txns-nonfinalandbad-cb-heightrun before a block is written, as
Core'sAcceptBlock, sosubmitblockreturns Core's reason instead of
inconclusiveand the header is marked invalid. -assumevalidskips scripts only under Core's conditions (ancestor of
the assumevalid block on the best-header chain, minimum chain work met, two
weeks of work deep), logging Core's "Enabling/Disabling script verification"
lines. Off that branch satd verifies everything (stricter than Core).-minimumchainworktakes effect (it was ignored); a value Core refuses
stops startup.- BIP 30 as Core checks it. Skipped only above the BIP 34 height on a chain
with the known BIP 34 block, and checked again from height 1,983,702.
Testnet3 now checks every block; mainnet is unaffected until that height. - A failed
invalidateblockleaves the node on a valid tip (#977). It now
disconnects to the invalidated block's parent first, as in Core. - A tall chain of low-work headers no longer puts the node in IBD (#1119).
The header height (getblockchaininfo.headers, the IBD check, readiness)
is the most-work header's, as Core'sm_best_header, not the highest
header stored, so a minimum-difficulty fork taller than the real chain no
longer stops transaction relay. The low-work headers are still stored
until headers presync (#1132). - A coin or undo record whose ten-byte varint overflows 64 bits is refused
as corrupt (#975) instead of decoding to a truncated value. - No forks below the last checkpoint (#1131). A new header below the
highest checkpoint already in the block index is refused as
bad-fork-prior-to-checkpointand its sender banned, as in Core up to v29,
so a peer can no longer fill the block index with cheap early-chain forks.
Blocks that arrive without their header (submitblock, a pushedblock)
are held to the same rule; known headers are skipped, and-checkpoints=0
turns it off. It covers mainnet and the default signet; testnet4, regtest
and custom signets have no checkpoints. Core v30 dropped checkpoints for
headers presync, which satd does not have yet (#1132).
Peers
- A peer that sends an invalid block is disconnected and banned, as Core's
MaybePunishNodeForBlock, IBD blocks included;nobanand manual peers are
kept, local ones only disconnected. Compact-block reconstructions,
future-timestamp refusals and blocks this node could not store are not
charged (#544). - A peer stalling IBD is disconnected (#972), as in Core, with a 15-second
timeout that doubles per disconnect up to 64 seconds and returns to 15 as
blocks connect. A peer counts as
stalling only while another peer has delivered within the timeout, so a local
link drop does not disconnect every peer; a peer silent for ten minutes is
dropped regardless. - A node with a stale address book seeds itself (#974), as in Core: DNS
seeds if fewer than two full-relay outbound peers are up after 11 seconds (5
minutes with 1,000+ addresses), and fixed seeds a minute later if the book
still has nothing reachable. - A dial through
-proxygives the SOCKS5 exchange 20 seconds (or
-timeoutif larger), as in Core;-timeoutbounds only the connect to the
proxy..oniondials too. - An unroutable
-externalipis not advertised, as Core'sAddLocal:
private, loopback, 100.64.0.0/10, documentation and (unlike Core)
fc00::/8addresses are left out ofaddr,versionand
getnetworkinfo'slocaladdresses. - P2P messages are read as Core reads them (#1142), on v1 and v2. A
versionthat ends beforerelay(or any later optional field) is
accepted with Core's defaults, so peers that omit it complete the
handshake. Bytes after a message's last field are ignored, asendcmpct
flag byte other than 0 or 1 means true, and afeefilteroutside
MoneyRangeis ignored and the previous filter kept.
Block relay
- The tip is announced after a parallel-download catch-up; peers relying
on satd's announcements stayed behind.
Privacy
-onlynettakes effect (it was ignored) for automatic outbound
connections and kept gossip addresses, as in Core; inbound and manual
connections are unaffected. Startup refuses
-onlynet=onionwithout-proxyor-onion(unlike Core,-listenonion
alone is not enough) or with-onion=0,-onlynet=cjdnswithout
-cjdnsreachable,-onlynet=i2p(no I2P transport) and unknown networks.
DNS seeding is off when IPv4 and IPv6 are excluded, and-dnsseed=1with
such an-onlynetis refused.-proxyturns listening off by default, as in Core.-bindor
-listen=1keeps it;-listen=0also disables-listenonionunless set.-proxy=0and-onion=0mean "none", as in Core (-onion=0blocks
.onioneven for manual peers); satd used0as an address.- Under
-proxy, DNS seeds are resolved by the proxy (#994) over
short-livedaddr-fetchconnections, as in Core; nothing goes to the local
resolver and the startup warning is gone.-seednodehost names are opened
the same way (not under-connect), andgetpeerinfoshows them by name
withnetworknot_publicly_routable. Host names in-addnodeand
-connectare still refused under-proxy. - The onion address survives a restart (#973): the key is kept in
onion_v3_private_keyin the network's data directory, as in Core. An
upgrading node gets one last new address. - The hidden service comes back after Tor restarts (#973): the address is
withdrawn while the control connection is down, and satd reconnects (backoff
up to 10 minutes; unlike Core, also after authentication orADD_ONION
failures) and re-adds it with the same key.-externaliptakes a v3 onion
address (#973), advertised while the node has no service of its own up. - Files satd creates are readable by its own user only (
umask(077)):
new directories 0700, new files 0600; the RPC cookie follows
-rpccookieperms. Existing files keep their modes until rewritten. - v2 (BIP 324) handshakes send 0 to 4095 random garbage bytes, as Core
does; fixed sizes made satd's connections identifiable.
Health and readiness
- Unknown soft forks are reported (#976), as Core does: a locked-in bit is
logged, and an active one adds "Unknown new rules activated (versionbit N)"
to thewarningsofgetblockchaininfo,getmininginfoand
getnetworkinfoand fires-alertnotify. /readyznotices a node that has lost its peers (#979): it is 503,
naming the detector, whiletip_stall(-alerttipstallseconds, default one
hour) orpeer_floor(-alertpeerfloor, default three, held a minute) is
raised. Both are0on regtest;0excludes a detector.-alertnotifyruns for warnings raised during startup (#884), such as
reindex.haltedandreindex.short.
Address relay
getaddris answered as Core answers it: inbound peers only, once per
connection, from a per-listener sample of at most 23% of the address book
(at most 1,000) cached about a day;addr-permission peers get a fresh one.
-externalipaddresses are announced to outbound peers at connect instead.- Address service bits follow the peer's
version, as in Core. - One network group can no longer fill the tried table. Tried entries use
Core's bucket/position slots (at most 8 of 256 buckets per group) with
test-before-evict, and tried dials pick a bucket first.addpeeraddress … trueinto a taken slot answersfailed-adding-to-tried. The placement key
is appended topeers.dat(0.6.x still loads it); an existing file is
re-placed under a fresh key, trimming each group to its share.
getrawaddrmanreports each entry's slot. - One peer can no longer fill the onion candidate list (512, in memory):
at most 64 per source, and a full list evicts an entry terrible by Core's
IsTerrible. - Outbound peers are asked for addresses, and dials spread over the book,
as in Core:getaddrto every address-relaying outbound peer, seed
addresses added to the book as new, dials drawn at random from tried or new,
and addresses dialled in the last ten minutes skipped. - An
addrv2listing a node with a high service bit is read. Its
services are read without a range check, as in Core, so one entry with
bit 26 set no longer loses the whole message, and over v2 no longer drops
the peer. A v2 packet that does not decode is now skipped, as on v1.
Block serving
- A pruned node no longer claims to be a full node. Pruned nodes and nodes
behind an AssumeUTXO snapshot advertiseNODE_NETWORK_LIMITEDwithout
NODE_NETWORK; every node setsNODE_NETWORK_LIMITED, so an unpruned node
reportslocalservices0000000000000409(plusNODE_P2P_V2and
NODE_COMPACT_FILTERSwhen on). - Blocks are served as Core serves them (
BlockRequestAllowed): off-chain
blocks only if validated and within 30 days of the best header. A pruned node
disconnects a non-nobanpeer asking for a block more than 290 below its
tip, andgetblocksstops 282 below the tip. - Old blocks from limited peers that still hold them. A
NODE_NETWORK_LIMITEDpeer that also advertisesNODE_INTACT_BLOCKS(bit
28,INTACT_BLOCKSingetpeerinfo) serves every intact block and answers
notfoundfor the rest. satd now fetches old blocks from such peers (for
peer-lessgetblockfrompeer, only without aNODE_NETWORKpeer) but never
seeks them out.
OP_RETURN data pruning
-pruneopreturn=<N> (off by default; minimum 288, 1 or all mean 288)
starts a worker that, below tip − N − 2, zeroes the payload of each
non-coinbase OP_RETURN output of at least -pruneopreturnsize bytes (default
4096; k/M and KiB/MiB suffixes accepted) and punches it out of the
block file (fallocate(PUNCH_HOLE) on Linux, F_PUNCHHOLE on macOS). Txids
and a hash of the zeroed block are stored first, so -reindex,
-reindex-chainstate and every surface keep real txids; blocks are still
fully validated. The worker pauses during IBD, reindex and AssumeUTXO
background validation, and startup fails if the filesystem cannot punch
holes. Only whole filesystem blocks are freed, so today's chain gives back
little (a 100 KB output frees about 96 KB); on APFS freeing the space is
best-effort, though the payload is gone either way. It is not a secure erase: copies,
snapshots, backups and the last 288 blocks keep the bytes.
A stripping node gives up:
- Archive-peer status. It advertises
NODE_NETWORK_LIMITED, not
NODE_NETWORK, while any block is stripped, option on or off.
-pruneopreturnpeers=limited(default) serves the last 288 blocks;
=intactalso serves intact blocks at any depth, advertises
NODE_INTACT_BLOCKSand answersnotfoundfor stripped ones. - Raw bytes of stripped transactions.
getblockverbosity 0 returns
Block not available (pruned data);getrawtransactionhex, Esplora/raw
and/hex, and non-verbose Electrumblockchain.transaction.getrefuse
(Transaction data not available (OP_RETURN data stripped)over RPC). JSON
views mark the transactionopreturn_strippedand show each stripped script
as bareOP_RETURNwith its original size. Streaming rescan events
(ScriptMatched,PrefixMatched,SilentPaymentMatched) carry
raw_tx_strippedinstead ofraw_tx;PrefixMatchednow always carries
txid. - Deep reorgs. A reorg or
invalidateblockthat would disconnect a
stripped block fails, naming it, and changes nothing. - Script re-verification on
-reindexfor stripped transactions' inputs.
getopreturnpruneinfo reports configuration, worker state, progress and
totals, also under getblockchaininfo's opreturn_prune; Prometheus gets
satd_opreturn_* metrics and sat-tui op-prune and op-restore columns.
getblockfrompeer restores one block in place (re-stripped on the next pass
if the option is on). With the option off, restoring the last stripped block
makes the node a full node again without a restart and removes the strip
column family.
-restoreopreturns, with every -pruneopreturn* option removed (it is
refused alongside them), re-downloads all stripped blocks and writes them back
in place, resuming after a restart. It asks only connected NODE_NETWORK +
NODE_WITNESS peers (16 blocks per peer, 1024 in flight, 15-second timeout,
no re-ask after notfound), restores newest first on a pruning node, and waits
while the tip is behind, no full peer is connected, or the blocks volume lacks
the room needed plus 1 GiB. When it finishes the node advertises
NODE_NETWORK again and the log says the option can be removed. Progress and
ETA appear under getopreturnpruneinfo's restore and in
satd_opreturn_restore_* metrics. On a synced mainnet node pruned with
-pruneopreturn=all -pruneopreturnsize=84, restoring re-downloaded 11,184
blocks, 18.0 GB, in 29 minutes from 3 to 7 peers.
The Rust and Go event clients mark a stripped prefix hit
outputs_unavailable / OutputsUnavailable (reported by has_unresolved /
HasUnresolved); their new public struct fields break Rust code that builds
them with literals. See
the Operator Manual.
Mempool policy
submitpackagedoes package CPFP (#984), as Core'sAcceptPackage: a
member refused alone only for feerate is judged with its paying child by
combined feerate (sharedeffective-feerate,effective-includes); a short
package is refused on the child withmin relay fee not metor
mempool min fee not met. The pool is trimmed after the whole package is in
(a trimmed member reportsmempool full), eviction ranks by descendant
score, and an ephemeral dust parent's child now pays for both.- TRUC (BIP 431) for version 3 transactions (#1020), as Core: at most one
unconfirmed parent and one child, at most 10,000 vbytes (1,000 with an
unconfirmed parent), only v3 may spend unconfirmed v3, always replaceable. A
second child may evict the first (refusal
insufficient fee (including sibling eviction)); other violations are
TRUC-violation, inpackage_msgwithin a package. Reorg-returned
transactions are exempt;mempool.datreloads are not. Replacement checks
now follow the fee floor, so a replacement below it is refused for its fee. - Cluster limits: Core v31's
-limitclustercount(default and maximum 64)
and-limitclustersize(default 101 kvB, sigop-adjusted; was an unknown
option) bound the whole connected cluster, refusing astoo-large-cluster
(satd counted only ancestors). The size also sets the-maxmempoolfloor
(40 clusters). After a reorg, oversized clusters are trimmed as Core's
TxGraph::Trim, reported asreorgevictions. - Replacements are judged by full cluster feerate diagrams, as Core v31.
Some bumps Core accepts now pass; some satd accepted, such as evicting a
well-paying child, are refused. satd linearizes greedily, so large clusters
can differ from Core at the margin (see
CORE_DIFFERENCES.md). - Package RBF, as Core v31: a parent and child with no in-mempool parents
can replace together if they pay the evicted fees plus their own relay, beat
the parent's feerate, conflict with at most 100 clusters and improve the
diagram. Refusals go inpackage_msg(package RBF failed: …); nothing is
evicted until both are fully checked. - Multi-transaction
testmempoolacceptis a package evaluation, as Core's
AcceptMultipleTransactions: transactions may spend earlier ones, replacing
mempool transactions isbip125-replacement-disallowed, set-wide TRUC or
cluster violations are each one'spackage-error, a child must spend its
parents' ephemeral dust, and the first refusal stops evaluation. Feerates
are still per transaction.txn-already-knownnow carriesreject-details. - Core's script policy. The mempool runs Core's
PolicyScriptChecks
(STANDARD_SCRIPT_VERIFY_FLAGS, Rust engine, any-consensus) and refuses
spends of soft-fork upgrade hooks, non-standard signatures and pushes,
unrecognised output types, P2SH with more than 15 sigops, inputs over BIP
54's 2500 legacy sigops, bloated witnesses, and sigop cost above 16000, with
Core's reasons (mempool-script-verify-flag-failed (...),
bad-txns-nonstandard-inputs,bad-witness-nonstandard,
bad-txns-too-many-sigops), onmempool.datreload too.
-acceptnonstdtxnlifts only the input checks; relaying peers are not
penalised. -bytespersigoptakes effect (default 20; was ignored): mempool
feerates, reportedvsizeand template ranking use Core's sigop-adjusted
size.weightis unchanged.- Ephemeral dust package parents are checked for output standardness and
by the transaction policy (policyfile=): a local one the rules would
quarantine is refused, a peer's is held with its child, and one spending
another package member is refused if it carries aprioritisetransaction
delta, as Core'sPreCheckEphemeralTx. Their entry time follows the node
clock (#981). prioritisetransactiondeltas for absent transactions survive a restart
(#981) inmempool.dat; the file version is unchanged and older satd
ignores them.
Transaction relay
- A parent and its child are relayed together (Core's opportunistic
1-parent-1-child relay): a fee-refused parent from a peer is retried with a
child the same peer sent, judged assubmitpackagejudges it. A failed pair
is not retried before the next block. - Every peer announcing an orphan is recorded and asked for its parents.
- The orphanage is limited as Core v30's is, replacing the 100-orphan,
50-per-peer, 20-minute limits: a latency-score limit of 3,000 and 404,000 WU
per announcing peer, enforced by evicting the highest-DoS-score peer's
announcements. Orphans no longer expire and are keyed by wtxid, so a
malleated copy cannot block the real one;getorphantxslists in wtxid order. - Transactions are announced on Core's schedule, from per-peer queues on
Poisson timers averaging 2 seconds outbound and 5 inbound, at most 70 per
inv(up to 1000 for long queues);nobanpeers are not delayed. A request
for an unannounced transaction getsnotfound, except one from the most
recent block or returned by a reorg, and a BIP 35mempoolrequest is
answered with the peer's next announcement. Newtxannouncedelay=0
(satd extension, reloadable) announces at once, giving up that privacy.
getpeerinfo'sinv_to_sendandlast_inv_sequenceare now filled in. - An announced transaction is downloaded once, as Core's
TxRequestTracker: one request in flight per transaction, outbound peers
preferred, inbound and txid announcements delayed 2 seconds, 60-second
timeout, at most 5000 tracked announcements per peer without therelay
permission. Waits followsetmocktime. - A refused transaction is not fetched again. The last 120000 refusals
are remembered by wtxid until the tip moves, as in Core; fee-only refusals
go to a separate filter so a paying child can still bring them in.
Core functional tests that now pass against satd: mempool_limit.py,
mempool_truc.py, mempool_package_limits.py, mempool_package_rbf.py,
p2p_opportunistic_1p1c.py, mempool_reorg.py, p2p_leak_tx.py,
rpc_orphans.py, p2p_1p1c_network.py and
rpc_signmessagewithprivkey.py.
RPC
gettxoutsetinforefuses the arguments it cannot honour (#896) instead
of ignoring them: ahash_or_heightother than the tip fails with Core's
no-coinstatsindex error (-8), andhash_typemuhashor
hash_serialized_3fails by name (-8).verifybip322(satd's own) verifies BIP 322 signatures (smp,fulor
unprefixed) for any address type, answeringvalid,inconclusiveor
invalid;pofis unsupported. Its legacy/P2SH MINIMALIF flag cannot be
set by block or mempool validation.verifymessageandsignmessagewithprivkey, Core's legacy P2PKH message
signing, byte-compatible with Core.getorphantxs(Core's hidden RPC, verbosity 0–2).getorphaninfoaddsannouncements,latency_score,
max_latency_score,weightandmax_weight;max_sizenow holds the
latency limit.getblockandgetrawtransactionanswer as Core v31's do (#978):
getblockaddsstrippedsizeandcoinbase_tx;getblockverbosity 2
adds each transaction'sfeeand verbosity 3 each input'sprevout, and
getrawtransactionverbosity 2 itsfeeandprevout, all read from undo
data so no-txindexis needed; a coinbase input showstxinwitness.
Fields Core lacks are removed (see Upgrade notes).gettxspendingprevout, as Core v31, served from the address index's
spend rows.-txospenderindex(previously ignored) enables block lookups,
requires-addressindexand is refused with pruning. Unlike Core, a spender
in a block removed byinvalidateblockdisappears at once.getrawmempoollists in Core's mining order (best chunks first),
matching Core wherever satd linearizes the same way; it was hash-map order.
Verbose keys stay sorted by txid.gettxout'sscriptPubKeyaddsdesc,addressandtype, and
typefollows Core'sSolvereverywhere (pubkey,multisig,
anchor,witness_unknowninstead ofnonstandard;nulldataonly for
push-only scripts).
Node attestation
satd can publish and collect Node Attestation Protocol (NAP) attestations:
non-binding statements signed by Bitcoin holders, one key-value pair per coin,
weighted by the coin's value while it stays unspent. The manual's
Node Attestation chapter covers
signing with each kind of wallet, the privacy costs and the statuses.
-nap=1(off by default) publishes. The store is
<datadir>/nap/attestations.datplus an append-only
attestations.journal; an unreadable store stops startup unless
-napstorerecover=1, which renames both with a.bad-<unix time>suffix
and starts empty.-napmaxattestations=<n>caps it (default no limit;
store-fullat the cap).createnapattestation "<txid>:<vout>" "key" "value" ( "descriptor" )
returns the text and a BIP 322to_signPSBT, anchored 6 blocks below the
tip, for a coin of at least 10,000 sats and 4,032 blocks of age (6 on test
networks; coinbase coins wait for maturity).submitnapattestation "message" "signature"takes BIP 322 signatures,
legacysignmessagefor P2PKH, a signedto_signtransaction or a signed
PSBT. Refusals are -26 naming the rule (stale,coin-too-small, …). One
attestation per coin and key, at most 32 keys per coin.
listnapattestationsandremovenapattestationmanage them.- Serving. A publisher advertises
NODE_NAP(bit 30,NAPin
getnetworkinfo) and answersgetnapattin 1 MB pages: one pull per
connection per 10 minutes, 200 MB an hour (eight times that per network
group); over-limit requests are ignored, malformed ones are misbehaviour. It
never sends NAP messages unasked, so Core peers are unaffected. Stale
attestations and ones whose anchor a reorg removed are kept but not served. - Aggregating.
-napaggregator=1(off by default, independent of-nap)
pulls fromNODE_NAPpeers at connect and hourly, and over new short
nap-pullconnections that take no outbound slot, with budgeted
verification (5,000 per pull, 1,000,000 an hour). It keeps at most 256 keys
per coin and-napaggregatormaxattestations (default 1,000,000) in
<datadir>/nap/aggregate/aggregate.dat;listnapaggregatelists them. - Reports.
getnapreport(rpc:read) gives each key's weight per
value, in integers.exportnapsnapshotandreplaynapsnapshot
(rpc:write) let any node reproduce a published report byte for byte.
Electrum and Esplora
- Electrum subscription caps fit a desktop wallet.
-electrummaxsubsperconndefaults to 10,000 (was 1000) and the shared
-addrindexsubscriptionsregistry to 50,000 (about 230 MB when full);
explicit values are kept. Subscribing no longer sweeps the registry each
time. - A backfilled address index leaves out unspendable outputs, as a live one
does; rows from an earlier backfill need no action. - A scripthash's history is capped by the work it costs. Electrum
get_history,get_balance,listunspentandsubscribe, and Esplora
address and scripthash stats,/utxoand/sse, stop reading at the cap
instead of loading the whole history. Electrum answers code2,
history too large (cap = 200000 entries); a refusedsubscribeis not
kept, and a subscription that grows past the cap stops notifying. Esplora
answers 400Too many history entries (more than 200000 transactions).
As-of answers built from history are under the same cap;get_first_use
and paged/txsroutes are not. New-addrindexmaxhistory=<n>(default
200000;0for no cap) sets it. - Esplora responses name their chain tip in
X-Bitcoin-Chain-Tipand
X-Bitcoin-Chain-Tip-Height(mempool/mempool#6584, as rbitcoin serves).
Each request answers for the tip it started at and is rerun after a
disconnect; eight in a row answer 503chain view moved. Mempool and fee
routes,POST /tx,/txs/mempooland SSE carry no headers;-esploracors
exposes them. - Esplora
?asof=<blockhash>, with rbitcoin's rules, answers address,
scripthash,/txs,/utxo, tx status and outspend routes as of a block on
the best chain, without the mempool. A hash not on the best chain, a
malformedasof, orasofon another route is 404. - Electrum answers name their chain tip in
chain_tipand
chain_tip_heightbesideresultonblockchain.scripthash.get_history,
get_balance,listunspentandblockchain.transaction.get,get_merkle,
id_from_pos, as rbitcoin does (server.features:"chain_tip": true),
with the same rerun rule (errorchain view moved).server.version
accepts and echoes rbitcoin's1.4.2-asof; the protocol is still 1.4. - Electrum trailing
asof:<blockhash>(rbitcoin's argument) on the same
six methods answers as of that block without the mempool; a hash off the
best chain isasof not on chain. satd does not require1.4.2-asof
negotiation;server.featuresreports"asof": trueand
"asof_protocol": "1.4.2-asof". Clients must checkasofthere first:
older servers may readasof:…ontransaction.get,get_merkleor
id_from_posas afalseflag and answer live.
Shutdown
- Shutdown waits for running RPC calls (#983), as Core's
Shutdown(), for
a quarter of the remaining-maxshutdownsecs; a call still running costs the
clean-shutdown marker, not the flush.generatetoaddressand
generatetodescriptorreturn the blocks mined so far, thewaitfor*calls
return the current tip, and a long-pollinggetblocktemplatefails with
-9 "Shutting down".
Mining
getblocktemplate'smintimeandcurtimetake the BIP 94 retarget
floor on every network, as Core'sGetMinimumTime; on testnet4 a block at
the advertisedmintimecould fail as a timewarp. Stratum shares and Job
Declaration jobs below the floor are refused.getblocktemplateon mainnet waits for peers and IBD, with Core's
RPC_CLIENT_NOT_CONNECTED(-9) andRPC_CLIENT_IN_INITIAL_DOWNLOAD(-10).
Test chains and proposal mode are not gated.getblocktemplatetransactions carry Core'shash(wtxid) and
depends(#982); gateways that need them, such as OCEAN's DATUM gateway,
refused every non-empty template.
Configuration
- List options take both sources (#995). Core's list options (
bind,
whitebind,connect,addnode,seednode,rpcbind,rpcallowip,
rpcauth,whitelist,externalip,onlynet,signetseednode,
signetchallenge,debug,debugexclude,loglevel) now merge the command
line andbitcoin.conf, command line first, as in Core; a trailing negation
(-noconnect) still drops the file's values. satd's own list options are
unchanged. - The
getaddranswer cache followssetmocktime. -maxmempoolbelow 5 MB is refused with Core's
-maxmempool must be at least 5 MB;0is still allowed.
Terminal dashboard
- A NAP view on key
5when-napor-napaggregatoris on: published
attestations and thegetnapreportbreakdowns. - The services row is yellow wherever work is pending (backfills,
restores); green means done or serving. - The mempool view (
3) has a Quarantine panel while a policy is loaded
(held transactions against thequarantinemempoolbudget, foregone fees, a
per-rule table); the steady view's Txs line adds the held count.
TLS clients
-rpccacertreplaces the public roots instead of adding to them in
sat-cliandsat-tui, so a mis-issued public certificate for the node's
hostname can no longer receive RPC credentials. Without-rpccacertthe
public roots still apply. Also shipped in 0.6.3.
Dependencies
bitcoin0.32.102 (from 0.32.9), which drops 0.32.9'sCompactSizerange
check. satd reads P2P payloads with its own Core-compatible reader, since
0.32.10 and later refuse some messages Core accepts.rustls-webpki0.103.16 (RUSTSEC-2026-0382),webpki-rootsand
webpki-root-certs1.0.10 (RUSTSEC-2026-0381, -0383), as in 0.6.2, for the
distrusted emSign intermediates.tokio1.54.1 (RUSTSEC-2026-0386); satd does not use the affected
AsyncFdconstructors.zstd-sys2.1.2 (RUSTSEC-2026-0410); the advisory is wasm32-only and no
satd build was affected.
Upgrade notes
-
Headers that fork below the last checkpoint are refused (see Block
validation), and the peer that sent them is banned. This covers mainnet
and the default signet;-checkpoints=0turns it off. -
Peers satd used to refuse are now accepted (see Peers and Address
relay): aversionwithoutrelay, trailing bytes after a message, an
addrv2entry with a high service bit. An undecodable v2 packet is
skipped instead of dropping the peer. No action is needed. -
gettxoutsetinfowith a historicalhash_or_height, or with
hash_typemuhashorhash_serialized_3, now fails with -8 (see
RPC). It used to answer with the tip's stats. Call it with no arguments,
or withhash_type"none", to get the tip's. -
-rpccacertis now the only trust anchor forsat-cliand
sat-tui(see TLS clients). An invocation that names a private CA but
connects to a node whose certificate a public CA issued now fails the
handshake. Drop-rpccacertfor such a node, or point it at the public
CA's root or intermediate. -
Transactions reach peers a few seconds later (see Transaction
relay): 2 seconds on average for outbound peers and 5 for inbound, as in
Core. Tooling that expects a transaction on a peer the instant it enters
this node's mempool should wait for it, give the peernoban, or set
txannouncedelay=0. -
The mempool bounds each transaction's whole cluster (see Mempool
policy). A transaction joining a cluster of 64 is refused as
too-large-cluster, however its relatives are arranged; before, only its
ancestors counted. Software that hangs many children off one unconfirmed
parent (a batch payout spending its own change) sees the 64th refused, as
on Core v31. -
Some replacements satd used to accept are now refused (see Mempool
policy, "Replacements in larger clusters"). A replacement that evicts a
conflict's high-feerate descendants must now improve the whole cluster,
as on Core v31. The cluster order satd uses is an approximation of
Core's, so on large clusters the two can differ at the margin. -
getblock,getrawtransactionanddecoderawtransactiondrop the
fields Core does not have (see RPC). Software reading them must change:getrawtransactionno longer hasblockheight; read the height from
getblockheader <blockhash>.- A mempool transaction no longer has
"confirmations": 0: the field
is absent, as in Core. - At verbosity 2, a mempool transaction has no
feeorprevout, as in
Core;getmempoolentryhas its fees. - Transactions inside
getblockno longer carryblockhash,
blockheight,confirmations,timeorblocktime. decoderawtransactionno longer echoes the transaction'shex.
-
A node with
-proxyand no explicit-listenstops accepting inbound
connections, and without an explicit-listenonionit stops publishing
its hidden service. To keep either, setlisten=1(andlistenonion=1). -
Configs with
-onlynetmay now refuse to start, with Core's message,
where they combine options Core refuses (see Privacy). -
A
-maxmempoolbetween 1 and 4 now stops the node from starting (see
Configuration). Raise it to at least 5. -
Sigop-dense transactions cost more to relay. A transaction whose sigop
cost times 20 exceeds its weight now needs a fee for that larger size, and
its reportedvsizegrows to match. -
The mempool refuses non-standard scripts. A transaction Core's
mempool would refuse for its scripts or what it spends is now refused by
satd's, including one loaded from an existingmempool.dat. Send such
spends to a miner directly. -
A pruned node advertises
NODE_NETWORK_LIMITEDinstead of
NODE_NETWORK. Peers that need the whole chain stop syncing from it, and
a peer asking it for a block more than 290 below its tip is disconnected. -
A list option set in both
bitcoin.confand on the command line now
takes both (see Configuration). Check a command line that repeats a
bind,rpcbind,connectoraddnodefrom the config file: the node now
gets the union, and a duplicatebindorwhitebindstops startup. -
New files in the data directory are 0600, and new directories 0700,
as in Core (see Privacy). If another account reads satd's files, grant it
access explicitly. For the RPC cookie, use-rpccookieperms=groupor
all. -
Electrum and Esplora refuse scripthashes with more than 200,000
transactions for requests whose cost follows the whole history (see
Electrum and Esplora): Electrumget_balance,listunspentand
subscribe(asget_historyalready did), Esplora address stats,/utxo
and/sse. Esplora's/txs/chainpages still serve such a history, and
-addrindexmaxhistoryraises or removes the cap. -
-zmqpub*options now take effect.zmqpub*lines carried over from
Bitcoin Core, ignored with a warning before, now bind at startup. If a Core
node on the same machine still holds an address, satd logs the bind failure
and runs with ZMQ off, as Core would; remove the lines if you do not want
satd publishing. -
A datadir that has used
-pruneopreturncannot be opened by satd
0.6.x or earlier (RocksDB reportsColumn families not opened: opreturn_strip). This is deliberate: older versions would misread the
zeroed data. Turning the option off restores nothing;-restoreopreturns
downloads the stripped blocks again, andgetblockfrompeerbrings back
one.
About this release
satd is free software under the MIT License.
- Stability & compatibility — Tier 1 surfaces (Bitcoin Core-compatible
JSON-RPC wire shape, CLI flag names/defaults,bitcoin.confkeys, on-disk
layout, Electrum/Esplora surfaces) are governed by
STABILITY_POLICY.md. satd follows
semantic versioning; while in the
0.xpre-1.0 line, deprecations may be accelerated (see the policy). - Security & disclosure — report vulnerabilities per
SECURITY.md. Consensus-affecting bugs are treated as
P0 with same-day acknowledgement. - Verifying this release — tarballs are signed with minisign, container
images with cosign (keyless OIDC), and git tags with SSH signatures (no GPG).
The key matrix and verification steps are in
SECURITY.md. - Intentional differences from Bitcoin Core — catalogued in
CORE_DIFFERENCES.md. - Operating satd — observability, the full flag matrix, tuning, and the
downstream packaging contract live in the
Operator Manual.
What's Changed
- packaging: pin the Umbrel and StartOS packages to satd 0.6.0 by @bkeroack in #905
- Forward-port 0.6.0 to master and open the 0.7.0 cycle by @bkeroack in #901
- Forward-port #898 and #899 (WatchAddRejected) to master by @bkeroack in #910
- Forward-port #919 (Go e2e flake) and #913 (release-branch CI) to master by @bkeroack in #920
- Forward-port #912, #917 and #911 (0.6.1 IBD and drain fixes) to master by @bkeroack in #921
- Forward-port #923 (getrawaddrman keeps a colliding address in its bucket) to master by @bkeroack in #925
- Forward-port #914 and #915 (0.6.1 StartOS fixes) to master by @bkeroack in #926
- Forward-port #927 (reindex scan reads block headers only, #916) to master by @bkeroack in #928
- Bitcoin Core-compatible ZMQ notifications (-zmqpub*) by @bkeroack in #929
- Forward-port #932 (genesis append, #931) and #930 (StartOS RUST_LOG) to master by @bkeroack in #934
- Announce a package's ephemeral-dust parent before its child by @bkeroack in #935
- deps: rustls 0.23.46 by @bkeroack in #967
- Forward-port the 0.6.1 fixes to master by @bkeroack in #964
- Release notes: 0.6.1 on master by @bkeroack in #968
- packaging: pin the Umbrel package to satd 0.6.1 by @bkeroack in #969
- docs: correct four statements the code contradicts (#980) by @bkeroack in #988
- chain: apply Core's contextual header and block checks before indexing and storing by @bkeroack in #985
- chain: -assumevalid skips scripts only under Core's conditions; honour -minimumchainwork by @bkeroack in #986
- net: charge the sender of an invalid block, including one judged by the IBD connector (#544) by @bkeroack in #987
- net: announce the tip a catch-up reaches by @bkeroack in #997
- storage: refuse a ten-byte varint that does not fit in a u64 (#975) by @bkeroack in #989
- chain: BIP30's buried-BIP34 skip as Core applies it; cpp engine needs one spent output per input by @bkeroack in #990
- rpc: getblocktemplate transactions carry Core's hash and depends (#982) by @bkeroack in #991
- net: apply -onlynet, -proxy's -listen soft-set, and -proxy=0 / -onion=0 as Core does by @bkeroack in #992
- metrics: /readyz is 503 while tip_stall or peer_floor is raised (#979) by @bkeroack in #993
- net: answer getaddr as Core does by @bkeroack in #996
- mempool: apply Core's script policy by @bkeroack in #998
- net: remember refused transactions until the tip moves by @bkeroack in #1001
- mempool: price transactions at Core's sigop-adjusted size by @bkeroack in #1000
- net: serve blocks as Core does by @bkeroack in #1003
- rpc: shutdown waits for running calls before the flush (#983) by @bkeroack in #1004
- chain: invalidateblock disconnects to the parent before it activates (#977) by @bkeroack in #1005
- net: disconnect a peer stalling initial block download (#972) by @bkeroack in #1006
- mempool: persist out-of-pool fee deltas; a dust parent's time follows mocktime (#981) by @bkeroack in #1007
- config: merge list options from the command line and bitcoin.conf (#995) by @bkeroack in #1008
- net: seed the address book after startup, as Core does (#974) by @bkeroack in #1009
- canary: read the Stratum V1 smoke's start height before the miner starts by @bkeroack in #1010
- tor: keep the hidden service's key so its address survives a restart (#973) by @bkeroack in #1011
- chain: warn about unknown version-bits soft forks, as Core does (#976) by @bkeroack in #1012
- init: set Core's umask(077) at startup by @bkeroack in #1013
- tests: give the harness's RPC client a minute and stop() thirty seconds by @bkeroack in #1014
- config: accept a v3 onion -externalip and advertise it (#973) by @bkeroack in #1015
- tor: reconnect to the control port and withdraw the onion while it is down (#973) by @bkeroack in #1016
- rpc: getblock and getrawtransaction answer with Core v31's fields (#978) by @bkeroack in #1018
- mining: getblocktemplate mintime takes Core's BIP 94 retarget floor by @bkeroack in #1017
- mempool: package CPFP in submitpackage (#984) by @bkeroack in #1019
- mempool: TRUC (BIP 431) policy for version 3 transactions by @bkeroack in #1021
- mempool: cluster limits on admission, and -limitclustersize by @bkeroack in #1025
- mempool: RBF diagrams over the clusters a replacement touches by @bkeroack in #1027
- rpc: a multi-transaction testmempoolaccept is evaluated as a package by @bkeroack in #1028
- chain: a custom signet drops the default signet's checkpoints and assumevalid by @bkeroack in #1022
- ibd: judge a peer stalling only while another peer is delivering by @bkeroack in #1024
- net: request an announced tx from one peer at a time, as Core's TxRequestTracker by @bkeroack in #1026
- index: bound scripthash history work and refuse oversized histories by @bkeroack in #1023
- consensus: build the BIP 341 signature message from precomputed hashes by @bkeroack in #1032
- consensus: run rbitcoin's Core-checked script corpus by @bkeroack in #1033
- rpc: gettxspendingprevout, with -txospenderindex served by the address index by @bkeroack in #1030
- mempool: package RBF in submitpackage, as Core's PackageRBFChecks by @bkeroack in #1031
- mempool: trim clusters a reorg grows past the limits; getrawmempool in mining order by @bkeroack in #1034
- net: Core's opportunistic 1-parent-1-child package relay by @bkeroack in #1035
- net, rpc: getorphantxs, and every announcer of an orphan is recorded by @bkeroack in #1036
- net: the orphanage has Core v30's limits and keys orphans by wtxid by @bkeroack in #1037
- net: transaction announcements trickle as Core's do by @bkeroack in #1038
- sat-tui: show the quarantine class while a filtering policy is loaded by @bkeroack in #1039
- deps: rustls-webpki 0.103.16, webpki-roots and webpki-root-certs 1.0.10 by @bkeroack in #1041
- sat-cli, sat-tui: -rpccacert is the only trust anchor by @bkeroack in #1044
- rpc: verifymessage and signmessagewithprivkey, as Core's by @bkeroack in #1040
- rpc: verifybip322, BIP 322 message verification on satd's interpreter by @bkeroack in #1042
- packaging: pin the Umbrel and StartOS packages to satd 0.6.2 by @bkeroack in #1045
- nap: attestation types, encodings and validity at a tip by @bkeroack in #1046
- packaging: pin the Umbrel and StartOS packages to satd 0.6.3 by @bkeroack in #1049
- nap: publisher, attestation store and RPCs (-nap) by @bkeroack in #1048
- net: send random BIP 324 garbage in the v2 handshake by @bkeroack in #1050
- events: refuse hardened derivation steps in watch descriptors by @bkeroack in #1052
- nap: sign attestations through BIP 322's to_sign PSBT (N5) by @bkeroack in #1051
- nap: a 6-block coin age on the test networks by @bkeroack in #1053
- nap: submitnapattestation takes the signed to_sign transaction in hex by @bkeroack in #1054
- ci: run the test suites in parallel and keep the canary caches on master by @bkeroack in #1055
- ci: save the large caches from master only, never from pull requests by @bkeroack in #1056
- ci: the release dry-run's Docker build saves its cache off pull requests only by @bkeroack in #1057
- docker: delete target/ in the final build step, so its layer stays small by @bkeroack in #1058
- ci: install the Linux build deps from a cache of the .deb files by @bkeroack in #1059
- nap: test against signatures real wallets made on signet by @bkeroack in #1060
- Core parity: SOCKS5 dial budget, getblocktemplate gate, gettxout script fields, unroutable -externalip by @bkeroack in #1062
- deps: tokio 1.54.1 for RUSTSEC-2026-0386 by @bkeroack in #1064
- nap: serve attestations over P2P (NODE_NAP bit 30, getnapatt/napatt) by @bkeroack in #1061
- ci: raise the block-differential fuzz timeout to 100 minutes by @bkeroack in #1074
- ci: nightly mutation testing (cargo-mutants) of the consensus-critical code by @bkeroack in #1063
- ci: fuzz every self-contained fuzz target nightly by @bkeroack in #1065
- ci: nightly coverage report (cargo-llvm-cov), counting the satd processes tests spawn by @bkeroack in #1073
- net: address services follow the peer's version and add up over gossip by @bkeroack in #1077
- nap: the aggregate set and cheapest-first intake (N7a) by @bkeroack in #1078
- nap: collect attestations from publishers over P2P (N7b) by @bkeroack in #1079
- nap: a minimum coin value of 10,000 sats by @bkeroack in #1080
- ci: report a nightly run whose runner died before its triage step by @bkeroack in #1081
- nap: the signed message starts "NAP attestation v1" by @bkeroack in #1082
- ci: split the nightly mutation shard into parallel parts, retry lost ones by @bkeroack in #1084
- nap: getnapreport and exportnapsnapshot by @bkeroack in #1083
- nap: stream the snapshot digest and file writes (N9) by @bkeroack in #1086
- sat-tui: a NAP view on key 5, offered only while the node has NAP on (N9) by @bkeroack in #1085
- nap: budget measurements; verify up to 1,000,000 signatures an hour (N9) by @bkeroack in #1087
- nap: the publisher records each change in a journal, not a rewrite of its store by @bkeroack in #1088
- bip322: a valid proof runs its scripts once by @bkeroack in #1089
- nap: split reports, snapshots and replays between threads by @bkeroack in #1090
- index: address backfill skips unspendable outputs, as the live path does by @bkeroack in #1093
- storage: hole-punch module and filesystem probe by @bkeroack in #1092
- deps: zstd-sys 2.1.2 for RUSTSEC-2026-0410 by @bkeroack in #1095
- mutants: stop a runaway mutant taking the runner down by @bkeroack in #1091
- storage: OP_RETURN strip record, span parser and StoredBlock by @bkeroack in #1094
- net: download old blocks from NODE_INTACT_BLOCKS peers when connected to one by @bkeroack in #1096
- storage: OP_RETURN strip records in the store, and a fail-closed get_block by @bkeroack in #1097
- storage: strip one block's OP_RETURN payloads (strip_block), and a test-only stripopreturns RPC by @bkeroack in #1098
- rpc: serve OP_RETURN-stripped blocks from their strip record by @bkeroack in #1099
- chain: replay OP_RETURN-stripped blocks from their strip record by @bkeroack in #1100
- tests: cover 16 mutants the nightly mutation run missed (shard 5/42) by @bkeroack in #1101
- esplora: pin each chain read to its starting tip and name it in X-Bitcoin-Chain-Tip headers by @bkeroack in #1102
- esplora: answer as of a best-chain block with ?asof= by @bkeroack in #1103
- electrum: pin chain reads to the starting tip and name it in chain_tip by @bkeroack in #1108
- electrum: answer as of a best-chain block with a trailing asof: by @bkeroack in #1109
- config: parse the CLI on a 16 MiB thread, and three mempool limits into PolicySizeArgs by @bkeroack in #1112
- esplora, electrum: serve OP_RETURN-stripped blocks from their strip record by @bkeroack in #1104
- index: backfill the address, filter and silent-payment indexes over OP_RETURN-stripped blocks by @bkeroack in #1105
- events: match OP_RETURN-stripped blocks by their recorded txids, and never ship their bytes by @bkeroack in #1106
- chain, tools: fail closed on OP_RETURN-stripped blocks in reorgs, invalidateblock and the offline tools by @bkeroack in #1107
- chain: restore an OP_RETURN-stripped block in place through getblockfrompeer by @bkeroack in #1110
- storage: -pruneopreturn, the background strip worker, getopreturnpruneinfo by @bkeroack in #1111
- storage: a node that restores its last stripped block is NODE_NETWORK again by @bkeroack in #1113
- net: -restoreopreturns downloads the stripped blocks again and makes the node NODE_NETWORK by @bkeroack in #1114
- sat-tui: OP_RETURN pruning and its restore in the services row by @bkeroack in #1115
- OP_RETURN restore: no per-block INFO line, no empty last pass by @bkeroack in #1116
- p2p: ask outbound peers for addresses, and spread automatic dials over the book by @bkeroack in #1118
- sat-tui: op-prune column, and yellow wherever work is pending by @bkeroack in #1117
- test: sort dials before comparing in the outbound-group netgroup test by @bkeroack in #1129
- alertnotify: run for warnings raised during startup (#884) by @bkeroack in #1123
- electrum: default subscription caps a desktop wallet fits in (#1121) by @bkeroack in #1126
- p2p: under -proxy, reach the DNS seeds and -seednode names through it (#994) by @bkeroack in #1128
- addrman: a tried table with Core's slots, a per-group bound and test-before-evict by @bkeroack in #1127
- p2p: bound each source's share of the onion candidate list, and evict terrible onions by @bkeroack in #1130
- gettxoutsetinfo: refuse hash_or_height / hash_type values it cannot honour (#896) by @bkeroack in #1125
- docs: condense the 0.7.0 release notes to fit a GitHub release by @bkeroack in #1134
- Header tip is the most-work header, not the highest one stored (#1119) by @bkeroack in #1124
- Refuse headers that fork below the last checkpoint (#1131) by @bkeroack in #1133
- Read addrv2 services as Core does; skip an undecodable v2 packet by @bkeroack in #1136
- ci: nightly live-peer smoke against testnet4 and signet by @bkeroack in #1137
- Bump bitcoin to 0.32.102; read P2P payloads as Core does by @bkeroack in #1143
- storage: on macOS, zero the punch range's edges before punching it by @bkeroack in #1141
- contrib: remove push-relay files re-added by #1097 by @bkeroack in #1147
- ci: build rbitcoin over a restored cache without cloning by @bkeroack in #1151
- docs: NAP protocol specification (draft) by @bkeroack in #1075
- chore(release): cut 0.7.0 by @bkeroack in #1146
Full Changelog: v0.6.3...v0.7.0