diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8e7d298f1..cf133ec8b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -901,6 +901,8 @@ jobs: # any other failure (empty/malformed file) aborts the release. - name: Build release notes body id: notes + env: + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail body="${RUNNER_TEMP}/RELEASE_BODY.md" @@ -922,6 +924,30 @@ jobs: fi echo "body_path=${body}" >> "$GITHUB_OUTPUT" + # GitHub's generated PR list compares against the latest release, + # pre-releases included, so a final tag cut after an -rc would list + # only the PRs since the rc. Compare a final tag with the previous + # final release instead; an -rc tag keeps GitHub's own choice. An + # empty value (no earlier final release, or the lookup failed) also + # leaves the choice to GitHub. + # + # "Previous" is by version, not by creation date: a patch on an + # older line (a v0.5.3 shipped after v0.6.0) must not become the + # base for the next v0.6.x. The current tag joins the sorted list + # and the entry just below it is taken, which also holds on a re-run + # after this tag's own release exists. + prev="" + if [[ "${GITHUB_REF_NAME}" != *-* ]]; then + prev=$( { gh release list --repo "${GITHUB_REPOSITORY}" \ + --exclude-pre-releases --exclude-drafts --limit 100 \ + --json tagName --jq '.[].tagName' || true; } \ + | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' \ + | { cat; echo "${GITHUB_REF_NAME}"; } | sort -uV \ + | awk -v t="${GITHUB_REF_NAME}" '$0 == t { print p; exit } { p = $0 }' || true) + fi + echo "PR list compares against: ${prev:-GitHub's default}" + echo "previous_tag=${prev}" >> "$GITHUB_OUTPUT" + - name: Create or update GitHub Release uses: softprops/action-gh-release@v3 with: @@ -929,6 +955,7 @@ jobs: # GitHub's auto-generated PR list is appended after it. body_path: ${{ steps.notes.outputs.body_path }} generate_release_notes: true + previous_tag: ${{ steps.notes.outputs.previous_tag }} # A tag carrying a pre-release suffix (e.g. v0.1.0-rc1, v0.3.0-pre) # is marked as a GitHub pre-release so it never shows as "Latest". prerelease: ${{ contains(github.ref_name, '-') }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 847350a7f..0e9c38ed2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,141 +11,15 @@ layout) per [`STABILITY_POLICY.md`](STABILITY_POLICY.md). ## [Unreleased] -Bound for **0.6.0**, a minor release on the 0.x line. This is an index: every -item below is (or will be) written up in full in the in-development -[`docs/release-notes/0.6.0-pre.md`](docs/release-notes/0.6.0-pre.md). - -### Changed - -- `satd-init` (the reference stack, the appliance, Umbrel and StartOS) writes `rpcallowip=0.0.0.0/0`. The allowlist now gates the published TLS JSON-RPC listener too, and the stack-subnet list it wrote refused every client on the LAN before the handshake. The plain listener is kept off the LAN by the network, as the other plain listeners already were. -- `satd-init` (the reference stack, Umbrel and StartOS) writes `upgradechainstate=1` for a satd that has the flag, so an app-store node rebuilds its chainstate from the block files by itself on the first start after a release that changes the format, and restarts a rebuild a power cut interrupted. -- sat-tui draws the mempool size and UTXO age histograms as log-scaled bar charts, four rows tall with each bucket's count on its bar, instead of one-row linear sparklines that showed the largest bucket and flattened the rest. Both tails of each distribution are now visible. The age chart marks its young buckets as estimates while the node is still building its exact counts. -- **Storage: every index is keyed on a dense transaction ordinal instead of a 32-byte txid.** Four schema steps, 3 → 7, and **one** `-reindex-chainstate` for all of them; the block files are untouched and nothing is re-downloaded. On a synced, fully indexed mainnet node the chainstate went from 805 GB to 388 GB. Pruned datadirs must resync. The four bullets below are the individual steps. -- **Chainstate schema 4.** The transaction index is keyed on dense chain-order transaction ordinals (`tx_loc`, `txseq_txid`, `txseq_block`) instead of `txid -> block_hash`, replacing the `tx_index` column family. An ordinal carries the transaction's position within its block as well as the block, so `getrawtransaction` indexes into the block instead of scanning it for a matching txid. Existing datadirs need one `-reindex-chainstate`; pruned datadirs must resync. A node that runs neither `-txindex` nor `-addressindex` writes no more than before. -- **Chainstate schema 5.** Coins record the ordinal of the transaction that created them, and the confirmed-side spend index (`spent`) is keyed on it: 16 bytes per row instead of the 76 `outpoint_spend` took, with both 32-byte identifiers recovered through the ordinal map. Esplora's `/tx/:txid/outspends` now answers from one prefix scan rather than one point read per output. The AssumeUTXO caveat is unchanged and now enforced: a snapshot load marks the address and spend indexes incomplete, so those surfaces refuse rather than answering "unspent" for history the node does not have yet — that was previously reported as complete. -- **Chainstate schema 6.** Address funding rows key on the creating transaction's ordinal: 32 bytes a row instead of 64, on the largest-but-one family in a fully indexed chainstate. The documented iteration order is unchanged — the store resolves and sorts before a row leaves it — so Electrum's `listunspent`, `get_balance` and `get_history`, and Esplora's address endpoints, answer byte-for-byte as before. -- **Chainstate schema 7.** Address spending rows key on the spending transaction's ordinal and name the output they consumed by the funding transaction's: 32 bytes a row instead of 92, on the largest family in a fully indexed chainstate — it carried two 32-byte transaction identifiers. Spend-by-address stays a single prefix scan, and `getaddresshistory` still emits `prev_txid` and `prev_vout` on the wire. -- A missing cumulative transaction count for a block's parent is now a hard error at connect time instead of silently restarting the count at zero. It also fixes a long-standing defect: `-reindex-chainstate` dropped genesis' own row and never re-seeded it, so every `getchaintxstats` total after a chainstate rebuild was one short for the life of the datadir. -- `gettxoutsetinfo`'s `utxo_age_distribution` counts the `<1h`, `<1d`, `<1w` and `<1mo` buckets exactly, from a per-height window of the most recent 4320 heights kept beside the coins, and gains an `exact` field. Before, each 1000-block chunk went into one bucket by its midpoint, so `<1d` was always empty or held a whole chunk. The window is an additive metadata key: no schema step and no reindex; an existing datadir builds it with one background scan at startup, reporting `exact: false` until it lands. -- A block announced by `headers` is fetched as a `cmpctblock`, as Bitcoin Core fetches it, when it is the only block the announcement leaves missing, its parent is connected, the peer speaks BIP 152 version 2, the tip is recent and nothing else is downloading. satd used to fetch every block whole from a peer it had not selected for high-bandwidth relay. A compact fetch that cannot be completed falls back to the full block. `sendheaders` now goes to inbound peers too, on Core's condition (the peer has shown a block past the minimum chain work), once per connection; outbound peers still get it in the handshake. Core's `p2p_compactblocks_blocksonly.py` joins the functional run-set (#867). -- Esplora `POST /tx` needs `rpc:submit` (or `rpc:write`) on a bearer token, on top of `esplora:read`. `esplora:read` alone could broadcast before, which made "read" a misnomer once `rpc:submit` became the broadcast capability elsewhere. Operator credentials and the loopback default are unaffected. - -### Added - -- `contrib/warnet/`: a lab image that runs satd tanks under Warnet's stock Bitcoin Core Helm chart, example networks with satd alone and mixed with Bitcoin Core, and keyless scenarios (mining, a transaction flood, and a cross-implementation consensus check) that need no node wallet. A compose test in CI runs the image on the chart's rendered `bitcoin.conf`. -- The address-manager RPCs: `getnodeaddresses` and `getaddrmaninfo`, and Core's hidden test RPCs `addpeeraddress` and `getrawaddrman`. The address book now records each address's service bits and the peer that announced it, and `peers.dat` moves to format version 2 (a version 1 file still loads). `getrawaddrman`'s `bucket/position` keys are derived with Core's formulas, not read from a bucketed table. -- `-rpcwhitelist` and `-rpcwhitelistdefault` limit an RPC user to a list of JSON-RPC methods, with Bitcoin Core's semantics: a refused call is HTTP 403 with an empty body, a batch is refused whole, a user listed twice keeps the intersection, and any list turns the default for unlisted users to deny. Both keys used to refuse startup. A restricted user is refused a WebSocket upgrade; bearer tokens are unaffected; `-rpcdisableauth` cannot be combined with an allowlist. -- `-upgradechainstate=1` rebuilds the UTXO set from the block files by itself when the datadir's chainstate schema is older than the binary's, or a previous rebuild did not finish, and does nothing otherwise. Off by default; it never downgrades and refuses a pruned node. The app-store packages will turn it on, which is how an app-store node crosses the schema 3 → 7 change without a shell (#839). -- Every block template is checked before it is used, as Core's `CreateNewBlock` checks its own. A structural check (every consensus rule but script execution) runs on each template: `getblocktemplate` and the `generate` RPCs answer a failing one with `TestBlockValidity failed: `, and the Stratum server replaces it with a coinbase-only job. A full check through the node's own script verifier follows in the background on each new tip; a failure there switches every Stratum job to coinbase-only until a template passes again. A failure is logged with the reject reason and the offending transaction, counted in `satd_template_checks_total`, and raised as the new critical `template_invalid` alert (a new `status` kind on the streaming API, the webhook dispatcher and `-alertnotify`). -- `getstoragefootprint` and `sat-cli debug storage-footprint` report per-column-family SST bytes, live key estimates, bytes per key and pending compaction, so operators can see where a chainstate's disk goes without an offline `ldb` dump against a stopped node. The `chain_tx` column family was missing from the existing per-CF diagnostics and is now reported. -- `getmempoolsummary [top_n]`, a bounded aggregate view of the mempool: a vsize histogram over every transaction plus the `top_n` by ancestor feerate. Replaces `getrawmempool verbose` for callers that only render an aggregate — on a mainnet-sized mempool that reply is ~85 MiB and ~4 s of node CPU, against ~9 KB here. -- `-prune=1` is Bitcoin Core's spelling for *manual* pruning, and satd now implements it: prune mode with no budget, where the new `pruneblockchain` RPC is the only thing that ever deletes. `getblockchaininfo` reports such a node as pruned with `automatic_pruning: false` and no `prune_target_size`. satd refused the flag at startup before. -- `-fastprune`, Core's 64 KiB block-file size. Pruning deletes whole files, so a short test chain never crossed the 128 MiB boundary and a prune could delete nothing. -- A bearer-token capability for mempool submission, `rpc:submit`: `sendrawtransaction`, `submitpackage`, and the other methods the read-only listener classes as mempool-submit. A broadcaster's token can hold `rpc:read` + `rpc:submit` and never `stop` or `addnode`. `rpc:write` implies it, so existing tokens are unaffected. -- The metrics and health endpoints can be served over TLS on a second port (`-metricstlsbind`), with optional client certificates (`-metricsmtls`), so Prometheus can scrape a node across a network without a reverse proxy (#752). -- The appliance images are published as release assets, alongside the - tarballs and signed with the same minisign key. They fit GitHub's 2 GiB - per-asset limit — `qemu-img` compresses them during the build — so there is - no separate download host and nothing to reassemble (#742). -- Appliance images for **arm64** as well as amd64, for Apple Silicon and - arm64 servers. The arm64 desktop carries Sparrow only, since Electrum and - Liana publish no arm64 build; its welcome page names what is installed - (#742). -- Operator Manual: a new **Installing satd** chapter covering the Umbrel community store (`epochbtc/umbrel-apps`), the StartOS package (`epochbtc/satd-startos`), the appliance images, the container image and the tarballs; the README's quick start points at it. -- A status page on the metrics listener, `statuspage=1`: a single-page, self-refreshing `sat-tui` for a browser, with connection strings from `statusadvertise`. Off by default (#749). -- Streaming API: `Subscribe`/`Watch` responses carry `satd-version` and `satd-events-schema` gRPC headers; `STABILITY_POLICY.md` now states the SDK ↔ node compatibility rule. -- Rust SDK: warns when the node is one minor version behind, refuses two or more (`StreamError::NodeTooOld`, override `allow_old_node()`) and any schema mismatch (`StreamError::SchemaMismatch`); `StreamClient::node_version()`. -- Go SDK: the same check (`ErrNodeTooOld`, `WithAllowOldNode`, `ErrSchemaMismatch`, `WithLogger`, `Client.NodeVersion`); `Watch` now waits for response headers; the module is versioned with the node (`satdevents.Version`), next tag `clients/go/v0.6.0`. -- The Umbrel and StartOS packages open onto the status page, and the appliance turns it on; Umbrel publishes Esplora over TLS on 8431; the reference stack and the appliance serve metrics and the status page over native TLS on 9336 (#750). -- **Stratum V1 solo-mining server** (`--stratum=1`): miners connect to the node directly; the username is the payout address. Loopback by default, TLS/mTLS listener, refused on signet (#746). -- **Stratum V2** on the same server (`--stratumv2bind`): Noise NX transport, standard and extended channels, and an authority key persisted at `/stratum_v2.key` so miners that pin it survive a restart (#748). -- Compact block reconstruction is observable: one `compact block reconstructed` log line per block (transactions prefilled, from the mempool, from the extra pool, requested; whether a round trip was needed) and `satd_net_compact_block_*` metrics. `-blockreconstructionextratxn` now works: replaced and policy-refused transactions are kept (default 100) so blocks containing them reconstruct without a round trip (#765). -- BIP 152 high-bandwidth compact block relay: satd selects up to three high-bandwidth peers, announces new blocks — including blocks found through the Stratum server — as `cmpctblock` before connecting them, and serves `MSG_CMPCT_BLOCK` and `getblocktxn` from a tip cache. Core's `p2p_compactblocks_hb.py` joins the functional run-set (#770). -- PSBT version 2 (BIP 370) on every PSBT method, carrying the BIP 375 silent payment fields losslessly. Core rejects any PSBT above version 0. `decodepsbt` gains a version 2 shape; `combinepsbt` refuses a conflicting value rather than picking one; `joinpsbts` refuses a silent payment PSBT that has committed to its input set. -- `createpsbt` accepts a silent payment address (`sp1…`) as an output key, with a new trailing `psbt_version=2`. `decodepsbt` and `analyzepsbt` spell the recipient back as its address. `createrawtransaction` refuses an `sp1…` key and says why a raw transaction cannot pay one. -- `sat-cli signpsbtwithkey` is a BIP 375 Signer: it computes the ECDH shares for the inputs it holds, proves them (BIP 374), derives the silent payment output scripts once every eligible input is covered, freezes the transaction, and only then signs. A partial run reports which inputs still owe a share and exits 2. `sat-cli signpsbtwithsigner` passes a version 2 PSBT to an external signer and verifies the reply. The node holds no keys. -- Send-side silent payments (BIP 375): `analyzepsbt` verifies the BIP 374 DLEQ proofs, binds each input's public key to the output it spends, cross-checks previous outputs against the UTXO set, recomputes every silent payment output script, and reports a per-output status; `finalizepsbt` refuses to extract a transaction whose silent payment outputs do not verify, with no override. The node stays keyless. -- Opt-in `cmpctblock` prefill (`-cmpctblockprefill`, `-cmpctblockprefillbytes`, default off / 8192 bytes): new blocks are announced with the transactions this node lacked prefilled, following Core's proposal #35558 (#772). -- `getmininginfo` reports `bits`, `target`, `blockmintxfee` and the next block's `height`/`bits`/`difficulty`/`target`; `getblock`, `getblockheader`, `getblockchaininfo` and `getchainstates` report `target`; on signet `getblockchaininfo` and `getmininginfo` report `signet_challenge`; `getprioritisedtransactions` reports `modified_fee`; `getmempoolentry` reports `wtxid`. -- **Stratum V2 Job Declaration** (`--stratumv2jd=1`), solo semantics: a miner declares its own transaction set from this node's mempool. New `getstratuminfo` RPC (#751). -- `getstratuminfo` lists each connected miner (device, difficulty, share counts, best share, last share, estimated hashrate) and a node-wide hashrate; the metrics endpoint exports `satd_stratum_*` connections, miners, shares by result, blocks found and hashrate. -- `-debug=stratum`: the Stratum server's per-miner debug lines (device, version-rolling mask, every share with the difficulty it achieved, a five-minute status line with estimated hashrate). Always logged: a disconnect summary per miner, and every refused share with its reason. -- A block found through the Stratum server is saved to `/stratum/found/-.hex` before it is submitted, so a block the node refuses, or a submission that panics, can still be sent to another node with `submitblock`. -- BIP 339 wtxid relay. `wtxidrelay` is negotiated between `version` and `verack`; a peer that negotiates it (every Bitcoin Core since 0.21) is announced transactions by wtxid and can fetch them by wtxid, and its own `MSG_WTX` announcements are fetched. Other peers are unchanged. `wtxidrelay` and `sendaddrv2` go only to peers at protocol version 70016 or above, a `wtxidrelay` after `verack` disconnects, and the same txid with another witness is refused as `txn-same-nonwitness-data-in-mempool`, all as in Core (#714). - -### Fixed - -- A config reload (`SIGHUP`) or TLS certificate reload (`SIGUSR1`) sent while the node was still starting stopped it: satd installed its handlers only at the end of startup, after RPC was already answering. Both are now installed as startup begins, and a reload sent during startup is applied once startup finishes. -- `gettxoutsetinfo`'s `txouts` and `total_amount`, the UTXO height histogram and the exact recent-age buckets drifted from the coins on disk on every reorg whose new branch re-mined the old block's transactions. Each output the new branch created again was counted as a second coin, and each output both branches created and spent inside the block was counted out although it was never written. The UTXO set itself was right. On signet, eleven one-block reorgs left the counters 19,547 coins and 348,804.25 tBTC away from the coins, which also makes `dumptxoutset` refuse. The counters now stay exact; `-reindex-chainstate` (which the 0.6.0 upgrade runs anyway) corrects a node that has drifted. -- A node stopped during initial sync could die of SIGSEGV after logging `satd stopped` (#868, #37), stay up for minutes first, and leave a clean-shutdown marker naming a tip below the one the next start loaded. Nothing stopped the block connector: it went on connecting after the shutdown flush and the marker, and it or a prefetch worker was still reading RocksDB when the process exited and destroyed RocksDB's C++ statics. Shutdown now stops the connector and waits for it before flushing, for at most half of what is left of `-maxshutdownsecs` (a connector still in a block then costs the marker, not the flush), stops the prefetch, maintenance and shadow-verification threads, and ends the process without running the static destructors, as a fatal error or a panic now does too. -- `-stopatheight` stops a node in initial sync, at the target height (#873). Initial sync connects blocks without the event the option listened for, so the node went past the target and kept running. `-stopatheight=0` is off, as in Core; it stopped the node at its first block. -- Reorgs (#739, #856, #861). A block with more work than the tip is activated only once every block of its branch has data. Before, satd disconnected its chain first, found a block missing and rolled back with an `ERROR`. A branch is now activated when its last missing block arrives; it used to wait for yet another block. A second copy of a block, stored by the initial-sync downloader, no longer marks the connected tip unconnected, a race that stopped sync for good. A block that fails validation during a reorg is marked invalid. Connecting stored blocks above the tip is no longer reported as a reorg. -- Addresses from gossip (#866). An address that names no host (`0.0.0.0`, `255.255.255.255`, `::`) is never learned, taken by `addpeeraddress`, loaded from `peers.dat` or dialled; satd dialled a gossiped `0.0.0.0` on every reconnect tick, which on Linux reaches the local host, and on satd's own port meant connecting to itself. Off regtest the address book takes routable addresses only, as Core's does. A peer found through gossip, `peers.dat` or a DNS seed is now `outbound-full-relay` rather than `manual`: it no longer takes `out` whitelist permissions or skips the handshake's service check, and `addnode onetry` no longer leaves an address marked manual. -- A pruned node in initial sync could stop for good. Pruning deleted a block file once the active-chain blocks in it were below the cut, but blocks are stored as they download, far ahead of the tip, so the file often also held blocks not yet connected; they went with it, and the node retried the next one's missing record forever (`failed to read stored block`). A file now goes only when every block stored in it is at or below the cut, as in Core. -- A stored block whose record was gone was retried forever, by the initial-sync connector and by the one that connects stored blocks at the tip: the index said the data was there, so nothing downloaded it again, and `getblockfrompeer`, the remedy the node logged, refuses a block above the tip in prune mode. The node now asks a peer for the block itself (at most every 10 s, a random peer each time), repairs the record through the `getblockfrompeer` route, and connects it. The error names the missing record instead of reporting a failed read as `block file write failed`. -- `mocktime=` in `bitcoin.conf` sets the regtest clock at startup, as `-mocktime` does and as Bitcoin Core reads it. The file spelling was skipped as an unsupported option before. -- `sat-cli` reads `rpcuser`, `rpcpassword`, `rpccookiefile`, `rpcport`, `rpcconnect` and the chain from `bitcoin.conf` like `bitcoin-cli`, including a top-level `rpcport` and a `[test]` section, and accepts `-signet`, `-testnet4`, `-chain=` and `-conf=`. A node configured with `rpcuser`/`rpcpassword` writes no cookie, so `sat-cli` could not reach it without repeating the credentials on its command line. `-generate` is refused with a pointer to the keyless RPCs. -- An `-addnode` or `-connect` host name that does not resolve at startup is kept and looked up again on every reconnect tick, and dialled once it resolves, instead of being dropped. `addnode add` accepts a name that does not resolve yet, `addnode remove` finds an added node by its string, and a disconnected named peer is looked up again before it is redialled, so a peer that moves to a new address is found. Every `-addnode` entry is registered before RPC starts, so the first `getaddednodeinfo` lists them all (#876). -- A config reload that adds an `-addnode` entry makes it an added node, as startup does: `getaddednodeinfo` lists it and `addnode remove` removes it (#879). The reload dialled the peer without adding it, so `addnode remove` failed with `-24` while the node went on redialling it. An `-addnode` or `-connect` host name that a reload adds and that does not resolve yet is kept and looked up again; it was dropped. -- An interrupted `-reindex-chainstate` or `-reindex` no longer starts on the next run with a truncated UTXO set and indexes stamped complete, stuck at the height it reached. A marker written before the wipe makes a plain start refuse and name the flag that finishes the job (#839). Opening for a rebuild no longer drops the previous schema's column families before the rebuild starts, so a rebuild refused at startup leaves a datadir the previous release can still read. -- A `-reindex` over block files that end early (a missing `blk*.dat`, or one cut off partway) no longer reports `Reindex from flat files complete` at the height of the hole. The replay is held to the tip the node had reached before the wipe. Block files that end below it still replay and the node syncs the rest from peers, but it logs an error and raises the `reindex.short` warning, which names the missing file. Pruned nodes are exempt (#504). -- The metrics listener answers while the node starts, including through a `-reindex-chainstate` or `-reindex` rebuild that runs for hours on mainnet. It used to come up only after the chain state was loaded, so a scrape was refused, `/readyz` was connection-refused rather than 503, and the status page an app store opens onto was a closed port. Now `/healthz` is 200, `/readyz` is 503 `not ready: starting: `, `/metrics` carries `satd_build_info` and `satd_startup_*` gauges, and with `statuspage=1` the page shows **starting** or **rebuilding chainstate** with the rebuild's progress and ETA. The running node's listener takes the bound port over before the full RPC answers, so the port never closes in between. A metrics TLS certificate or bind problem now stops satd before the rebuild instead of after it (#841). -- `-blockmaxweight` is applied (#836). It was parsed and read nowhere, so every template filled to 4,000,000 WU. It now follows Bitcoin Core: a value above the consensus maximum stops the node with Core's message, one below the 8,000 WU coinbase reserve is raised to it, a package that would *reach* the cap is left out (`>=`, where satd refused only one that went past it), and a malformed value in `bitcoin.conf` stops the node instead of becoming the default. Selection gives up on a near-full block at Core v30's 4,000 WU margin rather than v29's reserve-sized one. -- **Consensus (signet):** default-signet block solutions are verified (#767). They never were, so a node on the default signet followed any block whose proof of work met signet's trivial target. Custom signets signed with Bitcoin Core's tooling verify too now: satd's BIP 325 `to_spend` transaction lacked the leading `OP_0`, so only solutions satd had signed itself ever passed (#837). -- **Consensus:** BIP 34 accepted a coinbase that pushed the right block height in a non-minimal encoding (padded, an unneeded sign byte, `OP_PUSHDATA1`, a data push for heights 1–16). Bitcoin Core requires the scriptSig to start with exactly `CScript() << height` and rejects those blocks as `bad-cb-height`, so satd would have followed a block the network rejects. The check now compares bytes, as Core does; Stratum Job Declaration applies the same rule to a declared coinbase. -- Block templates ranked each transaction by its own fee rate, so a child paying for a cheap parent (CPFP, a Lightning anchor, a zero-fee ephemeral-dust parent) waited behind the parent and was left out once middling transactions filled the block — on `getblocktemplate`, `generatetoaddress` and the Stratum server alike. Templates now use Bitcoin Core's ancestor-score package selection: a transaction is ranked by the fee rate of itself plus its unconfirmed ancestors, and the whole package goes in together. -- Block templates kept to the block weight limit but not the sigop limit, so a mempool dense in sigops (bare multisig outputs count 20 each) produced a template `connect_block` refuses as `bad-blk-sigops` — on `getblocktemplate`, `generatetoaddress` and the Stratum server alike, so a miner who found that block found nothing. Templates now reserve 400 sigop cost for the coinbase and leave out a transaction that would bring the total to 80,000, as Core does; `getblocktemplate` reports each transaction's `sigops`; and a Stratum V2 Job Declaration, or a custom job whose own coinbase outputs add sigops, is refused when the block it yields could exceed the limit. -- Esplora (plain and TLS) and `streamws` bounded nothing at the socket level and never closed an idle keep-alive connection: `-esploramaxconns` and `-streamwsmaxconns` are taken per request or inside the handlers, and `axum::serve` builds hyper without a timer, so a client that opened connections and never closed them was limited only by the process's file-descriptor table — the same failure the TLS RPC listener had (#813). Both surfaces now run the capped accept loop the RPC listeners use: a permit per socket taken at accept (`-esploramaxsockets`, `--streamws-max-sockets`, default 1024), an idle budget on keep-alive connections (`-esplorarequesttimeout` for Esplora, 30 s for streamws), and the TLS handshake on the connection's own task instead of inline in Esplora's accept loop, where one slow client stalled every other connection for the handshake budget; `-esploramaxsockets` is one cap shared by the plain and TLS listeners. The permit now travels with the socket, so an open WebSocket keeps its slot until it closes — on streamws and on the JSON-RPC listeners, which serve WebSocket too and released the slot at the upgrade. Every socket and connection cap is clamped to 100,000, so a mistyped value no longer panics satd at boot. -- A client that connected to a JSON-RPC listener and never sent a byte, or sent one byte of the HTTP/2 preface, held its connection slot forever, `-rpcservertimeout` notwithstanding: hyper's header timer is armed only once the protocol version has been sniffed from the first bytes, and such a socket never got that far; a client that started HTTP/2 unasked got a connection with no idle timeout at all. The JSON-RPC, Esplora and streamws listeners now serve HTTP/1.1 only, so the header timer covers a request head from its first byte. **Upgrade note:** a client using HTTP/2 prior knowledge against them must use HTTP/1.1; none of them offered HTTP/2 over TLS. -- A JSON-RPC batch, and a WebSocket on the JSON-RPC listener, are charged against a bearer token's `rate_limit` per call. The HTTP layer charged one unit per request before it could see the body, so a `1/s` token could carry any number of calls in one batch, or over one socket after the upgrade. A call over budget is answered in-band with `-32005` and `retry_after_secs`. -- The TLS RPC listener's connection cap (100, the same as the plain listener) counted in-flight handshakes only: the permit taken before the handshake was released as soon as the connection began serving, so established idle connections were uncounted and a keepalive flood could exhaust file descriptors and wedge the listener. The permit is now held until the connection closes, as the plain listener already did (#813). Follow-up: the cap and the `-rpcservertimeout=0` lockout trade-off are documented; a peer outside `-rpcallowip` no longer takes a connection slot — the allowlist is checked before the cap, and the peer is answered `403` and closed from a separate pool of 16 — so outsiders can no longer lock allowlisted clients out; an IPv4 client of a dual-stack `[::]` bind is matched as IPv4, where it missed every IPv4 entry and the loopback exemption; the TLS listeners (`-rpctlsbind`, `-rpcreadonlytlsbind`) enforce their allowlist (`-rpcallowip`, `-rpcreadonlyallowip`) at all, which they never did — a configured allowlist now drops a non-listed peer at accept, before the handshake and before it takes a connection slot, while an empty allowlist leaves a TLS bind reachable as before; and every listener that sheds at capacity logs the first five refusals a minute plus a suppressed count, instead of one warning per refused socket. -- A node could wedge behind its only peer instead of syncing. A peer in BIP 152 high-bandwidth mode announces new blocks with `cmpctblock` rather than `headers`, and that path did not record that the announcing peer held the block it announced. The node learned the headers, raised its download target, and then asked nobody for the data: the parallel download scheduler will not assign a peer heights above what it believes the peer has (Core's `UpdateBlockAvailability`; #770). -- The TUI's mempool panels showed stale data indefinitely against a mainnet-sized mempool. They were fed by `getrawmempool verbose`, whose reply exceeds the node's 20 MiB response cap at roughly 17,000 transactions, and the steady-state poll loop discarded RPC errors without surfacing them. The panels now read `getmempoolsummary`, and any method whose poll fails is named in the title bar (`rpc failing: `) instead of silently leaving its panel frozen. -- JSON-RPC replies are no longer bound by the request-body cap. One 20 MiB constant governed both directions, so a reply whose size is set by the chain or the mempool — `getrawmempool verbose` crosses it at roughly 17,000 mempool transactions — was answered `-32008 Response is too big` where Bitcoin Core, which caps requests only, answers the call. The request cap is unchanged; responses are bounded separately at 256 MiB, derived from the default `-maxmempool` (#723). -- Compact block receive path hardened: a `cmpctblock`'s header is validated before the block is reconstructed, pending reconstructions and the record of which blocks were asked of a peer are bounded per peer and expire, a block being reconstructed is not also downloaded in full, and a merkle mismatch after reconstruction falls back to fetching the full block instead of penalising the peer (Core parity; #763). -- The Stratum server no longer drops a miner that is still hashing. Every connection used to be closed after 120 s without a message, and a miner sends nothing but shares, so even a miner at vardiff's 30 s target was dropped about every half hour, a miner with rarer shares far more often, and each one restarted at its lowest difficulty. An authorized miner may now go quiet for twenty expected share intervals (within 2 min to 1 day; a day before its rate is known). Connections that have not authorized still get 120 s. A Stratum V2 miner pointed at the V1 port is now named as such in the disconnect reason. -- Stratum vardiff no longer chases chance. It rescaled the difficulty every 90 s on about three expected shares, by up to ×4, so a miner on target wandered across a factor of 17 within an hour; it now moves only when the shares since the last change are significantly off target (with a looser test once 40 shares were expected, and a looser one still before a miner's first share), leaves a rate within 15% alone, and steps by at most ×8. In simulation 95% of settled miners hold within ×2 with at most two changes in five hours, where the old rule changed about 170 times across a median ×17. Slow "lottery" miners (ESP32-class, ~1 MH/s) at the default difficulty were closed by the idle limit before vardiff could lower them and restarted at 10,000, so they never submitted; they now reach difficulty 1 in about twelve minutes and are not dropped. The idle limit is no longer shortened by vardiff lowering a quiet miner's difficulty. -- Stratum work follows a tip the node reached through block download. Those blocks connect without a chain event, so work stayed on the old tip for up to 30 seconds after a node caught up — long enough for a Job Declaration client to be refused (#763). -- The `tip_stall` alert no longer fires during a long initial sync. Its clock was advanced only by chain events, and the block-download path connects without emitting any, so it measured time since process start: every mainnet IBD raised a critical alert an hour in and held it for the rest of the sync, on a node connecting blocks as fast as it could. The clock now follows the polled tip as well. A node wedged mid-sync still pages (#814). -- `preciousblock` was a stub that acknowledged the call and did nothing. It now works as Core's does, and equal-work tips are ordered by the order their data became connectable (Core's `nSequenceId`) rather than by block hash. - -- A rejected script now says *which* script rule it broke, as Bitcoin Core does: `mempool-script-verify-flag-failed (Non-canonical DER signature)` rather than a generic `ERR_SCRIPT`, and `testmempoolaccept`'s `reject-details` names the input it failed on. A block that fails to connect logs Core's `Block validation error: ` line. - -- A `pong` now answers for everything the peer sent ahead of the `ping`, as Bitcoin Core's does. satd answered `ping` on the peer's socket task while `block` and `tx` travelled a queue to the manager and on to the block processor, so a peer that used `send_and_ping(block)` — the standard way to wait for a block, and what Core's own test framework does — got the pong before the block was connected. - -- `-maxtipage` is honoured: a tip older than it keeps the node in initial block download. It was parsed and ignored. -- With no `-rpcbind`, the RPC server listens on `::1` as well as `127.0.0.1`, as Core does; a malformed `-rpcbind` port, an empty or malformed `-rpcauth`, and a cookie file that cannot be written stop startup with Core's messages; `-norpcauth`, `-norpccookiefile`, bracketed IPv6 in `-rpcallowip`, `-cjdnsreachable`'s allowlist rule and Core's `-noX=0` double negative are accepted. -- An expired mempool transaction now takes its descendants with it, and `-mempoolexpiry` is applied whenever a transaction is accepted, as in Core, rather than on a 30-second timer; a child of an expired parent used to stay in the pool. -- `generatetoaddress`, `generatetodescriptor` and `generateblock` work on every chain, as in Core, instead of only regtest. -- Blocks mined by the node's RPCs always carry the witness commitment, as Core's do; an empty block on a custom signet was rejected `bad-signet-blksig` for lack of it. -- `-rpcservertimeout` in `bitcoin.conf` was ignored (the command-line flag worked), so every RPC connection used the 30-second default and a client idle longer than that was disconnected. -- `-peertimeout` is implemented: a handshake that does not finish within it is dropped with Core's reason, on the node clock. satd bounded each handshake step by `-timeout`, Core's socket-connect budget. -- `getblocktemplate` honours `longpollid`: the call waits for a new tip or a mempool change instead of returning at once. -- Core's handshake rules: an outbound peer lacking the desired service flags is dropped, as is a connection to ourselves; pre-handshake messages are logged. New log lines for connections, bans, RPC dispatch, mutated blocks and transaction rejections. -- A peer that announces headers the node cannot connect yet is no longer ban-scored, as in Core. A node syncing from a peer that was still mining could reach the ban threshold in half a second and disconnect its only block source, stopping the sync dead. -- The P2P loop no longer waits out its 500 ms tick when the peer-message queue is still full, which had capped every inbound path — initial block download most of all — at 64 messages per tick. -- `invalidateblock` now moves the node's header tip off the invalidated branch, as Core's `RecalculateBestHeader` does, and `reconsiderblock` lets it back. `reconsiderblock` also clears the marks *below* the block it is given, as Core does: reconsidering the tip of an invalidated branch used to leave the path back to the fork still marked, so the branch could never reconnect. `getblockchaininfo` reported a `headers` height on the refused branch — one the node would never reach, growing with every block mined on it (#778). -- A full mempool raises a rolling minimum fee (Core's `mempool min fee not met`), reported as `getmempoolinfo.mempoolminfee` and advertised to peers in `feefilter`; `sendrawtransaction` and `testmempoolaccept` rejections use Core's `reason, detail` message; `getrawmempool` accepts `mempool_sequence=true`. -- P2P follows Core's rules for v2 transport detection and `addnode v2transport`, `feefilter` sending, protocol-violation disconnects (`-blocksonly`, block-relay-only, BIP 37 filters, unpermitted `mempool`), ping timing on the node clock, per-message byte counts for the version handshake, and Core's message log lines; a 4 MB message over v2 is no longer refused, and IBD asks a peer only for blocks it has announced. -- A signet block with an invalid solution is rejected as `bad-signet-blksig` (Core's reason) rather than `bad-signet-solution`; `-signetchallenge` given twice is refused, and a non-hex value is reported in Core's words. -- A node that mined its own chain, and so had never been sent a header by a peer, counted itself as still in initial block download and ignored every transaction its peers announced. It now takes them (#759). -- `verificationprogress` in `getblockchaininfo` and `getchainstates` is Bitcoin Core's transaction-count estimate, equal to Core's; it was the tip's timestamp over the current time and read 0.69 at genesis (#744). -- An appliance image can be built around a published release. The - `--satd-source release` path asked for - `satd--x86_64-linux-gnu.tar.gz`, a target triple and compression - format the release has never used, so it always 404'd; released images now - carry the signed release binary rather than a rebuild (#742). -- The appliance boot test waits for satd to finish starting instead of asking - `systemctl is-active` once. The single call raced the unit and failed a - release build with a diagnostic that showed the service running (#742). -- The Umbrel package takes its own host ports (8430, 8433, 8436, 8439, 50012), so it installs beside Bitcoin Node, Fulcrum and Ride The Lightning, and its backups leave the chain out (#743). -- The StartOS package's backups skip the AssumeUTXO background chainstate, and its sync check no longer reports `verificationprogress`, which read about 69% at genesis (#743). -- `getblocktemplate` now carries the retargeted difficulty at a retarget boundary (and after a testnet 20-minute gap) instead of the tip's bits, and reports `mintime` as median time past + 1 instead of `curtime` (#746). +Nothing yet — 0.6.0 was cut on 2026-10-05. Add a one-line bullet here for every +user-facing change, and the full write-up to the next +`docs/release-notes/-pre.md`. ## Releases | Version | Date | Notes | |---|---|---| +| [0.6.0](docs/release-notes/0.6.0.md) | 2026-10-05 | Solo mining, silent-payment sending, and half-size indexes. A **Stratum V1 and V2 solo-mining server** in the node (TLS for V1, Noise for V2, Job Declaration, per-miner statistics) lets a miner mine to its own address with no pool and no proxy, from block templates that now match Core's: the right difficulty at a retarget, the sigop limit, CPFP package selection, `-blockmaxweight`, and a validity check before a miner sees one. **Silent-payment sending (BIP 375)** over PSBT version 2, with `analyzepsbt` and `finalizepsbt` verifying every silent-payment output before it can be broadcast; the node stays keyless. Every index is keyed on transaction ordinals instead of txids, which **halves a fully indexed chainstate** (805 GB to 388 GB on mainnet). Faster block relay with BIP 152 high-bandwidth compact blocks and BIP 339 wtxid relay, sync, reorg and reindex robustness fixes, and another round of Core parity from Core's functional tests. Installable from Umbrel's community store or a signed appliance image for amd64 and arm64. **One `-reindex-chainstate` is required** (chainstate schema 3 to 7; app-store installs run it by themselves, pruned nodes must resync, and there is no downgrade). A configured `-rpcallowip` now also gates the TLS JSON-RPC listener, the HTTP listeners speak HTTP/1.1 only, and Esplora tokens that broadcast need `rpc:submit`. | | [0.5.2](docs/release-notes/0.5.2.md) | 2026-09-11 | Deployment and Bitcoin Core parity. Three new ways to run satd — a docker-compose **reference stack** with TLS everywhere and LND/CLN/RTL/Cashu/BTCPay overlays, a bootable **appliance image**, and **Umbrel and StartOS packages** — each installed and driven on a real server; `sat-cli`/`sat-tui` reach a TLS RPC listener; the container image ships `sat-tui` and a `HEALTHCHECK` and is **136 MB instead of 670 MB**. MCP is reachable by hostname (`-mcpallowedhost`, #734). The defects filed from the 0.5.1 functional-test review (#660–#673) and the ones Core's suite went on to find, closed in Core's direction: a mistyped RPC argument no longer discards the ones after it (#672), surplus arguments are refused (#688), `createrawtransaction`/`createpsbt` check the address network and parse amounts exactly (#689), mempool policy at Core parity — dust thresholds, feerate-diagram RBF, ephemeral dust, `submitpackage`/`testmempoolaccept` shapes (#660, #661, #665, #673), `getblocktemplate` proposal mode validates like `submitblock` (#663), more than a dozen RPC fields that were constants now come from the node (#667, #702), `-connect=0` no longer dials the address zero and a pinned node stops listening (#690), and two **security** fixes — `-whitelist` no longer reaches Tor or outbound peers (#701) and a proxied node no longer resolves peer hostnames locally (#668). An **IBD wedge** on fork-heavy networks, where a competing header could permanently replace the next block to fetch, is fixed and a damaged datadir heals on the first restart (#738). Drop-in binary upgrade, no reindex; `banlist.json` moves to Core's format, so delete it before any downgrade. | | [0.5.1](docs/release-notes/0.5.1.md) | 2026-09-04 | A Bitcoin Core compatibility release. satd now runs **Core's own functional test suite**, unmodified and pinned to a Core release, with all 264 test files inventoried as run-or-skip-with-a-reason; the set gates every pull request and found twelve defects in shipped behaviour — an RPC `Content-Type` Core-derived clients reject outright, a startup listener answering "no such method" where Core answers "warming up", a `bitcoin.conf` fee-rate value silently discarded, a panic on any unparseable `-bind` (and no IPv6 bind at all), mempool chain limits enforcing a limit Core v31 no longer has, and an oversized locator banning the peer where Core only disconnects. The mempool event broadcast now holds a whole block's confirmations — its 1024-slot ring was smaller than a single mainnet block, so every subscriber silently lost roughly 37% of each block's burst (#682). Silent payments reach wallets that exist today via `blockchain.tweaks.subscribe` on the Electrum server, plus `tweak_unspent_only` cut-through and spendable silent-payment outputs (#609). **Breaking:** `getindexinfo` is now Core's method — satd's richer view moved to `getsatdindexinfo`; `uacomment` is now validated and an unparseable `minrelaytxfee`/`dustrelayfee` now stops the node. Drop-in binary upgrade, no reindex. | | [0.5.0](docs/release-notes/0.5.0.md) | 2026-08-25 | The wallet-backend release — **BIP 352 silent payments, receive-side, end to end**: an opt-in tweak index (`silentpaymentindex=1`), a streaming tweak firehose with taproot-era cold-sync and mempool-time tweaks, and a server-side scan-key watch (confirmed + unconfirmed) with index-accelerated rescan, proven against the BIP 352 reference vectors. Adds a first-party Go SDK (`satdevents`) in full parity with the Rust SDK, node-health alerting (six detectors reported via status events, Prometheus, and webhooks), three BIP 141 witness rules at exact Core parity, chain-integrity fixes (tip standing on never-connected blocks, reorg/connector races, MTP from displaced branches, a block/index durability hole plus `getblockfrompeer` repair), Core v28+ obfuscated block-file reading, and measured index footprints in the manual. New surfaces are opt-in — defaults stay Bitcoin Core-compatible. | @@ -157,4 +31,4 @@ item below is (or will be) written up in full in the in-development | [0.2.0](docs/release-notes/0.2.0.md) | 2026-05-27 | BIP 324 v2 transport, native TLS, client-side PSBT signing, Core CLI/config-compat gap closed, AssumeUTXO fast-start. **Breaking storage cleanup** — see notes. | | [0.1.0](docs/release-notes/0.1.0.md) | 2026-05-08 | First public release: mainnet-validated node, native Esplora/Electrum/cfilters, Core-compatible RPC/CLI, signed reproducible builds. | -[Unreleased]: https://github.com/epochbtc/satd/compare/v0.5.2...HEAD +[Unreleased]: https://github.com/epochbtc/satd/compare/v0.6.0...HEAD diff --git a/Cargo.lock b/Cargo.lock index 56243e2f5..d6aeacc69 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1038,7 +1038,7 @@ dependencies = [ [[package]] name = "consensus" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "bitcoin", "bitcoinconsensus", @@ -1481,7 +1481,7 @@ dependencies = [ [[package]] name = "electrum-proto" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "async-trait", "bitcoin", @@ -1552,7 +1552,7 @@ dependencies = [ [[package]] name = "esplora-handlers" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "axum 0.8.9", "axum-test", @@ -2959,7 +2959,7 @@ dependencies = [ [[package]] name = "node" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "arc-swap", "async-trait", @@ -3014,7 +3014,7 @@ dependencies = [ [[package]] name = "node-filter-index" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "bitcoin", "serde", @@ -3023,7 +3023,7 @@ dependencies = [ [[package]] name = "node-index" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "bitcoin", "parking_lot", @@ -3034,7 +3034,7 @@ dependencies = [ [[package]] name = "node-sp-index" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "bitcoin", "hex", @@ -4464,7 +4464,7 @@ dependencies = [ [[package]] name = "sat-cli" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "base64 0.22.1", "bitcoin", @@ -4484,7 +4484,7 @@ dependencies = [ [[package]] name = "sat-tui" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "base64 0.22.1", "clap", @@ -4500,7 +4500,7 @@ dependencies = [ [[package]] name = "satd" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "axum 0.8.9", "base64 0.22.1", @@ -4550,7 +4550,7 @@ dependencies = [ [[package]] name = "satd-alert" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "hex", "hmac", @@ -4566,7 +4566,7 @@ dependencies = [ [[package]] name = "satd-auth" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "base64 0.22.1", "dashmap", @@ -4583,7 +4583,7 @@ dependencies = [ [[package]] name = "satd-events" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "async-trait", "axum 0.8.9", @@ -4612,7 +4612,7 @@ dependencies = [ [[package]] name = "satd-events-client" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "bitcoin", "prost", @@ -4629,7 +4629,7 @@ dependencies = [ [[package]] name = "satd-events-proto" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "prost", "protoc-bin-vendored", @@ -4639,7 +4639,7 @@ dependencies = [ [[package]] name = "satd-mcp" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "bitcoin", "bytes", @@ -4664,7 +4664,7 @@ dependencies = [ [[package]] name = "satd-policy" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "bitcoin", "proptest", @@ -4673,7 +4673,7 @@ dependencies = [ [[package]] name = "satd-psbt" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "base64 0.22.1", "bitcoin", @@ -5425,7 +5425,7 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tls-config" -version = "0.6.0-rc1" +version = "0.6.0" dependencies = [ "rcgen", "reqwest", diff --git a/Cargo.toml b/Cargo.toml index d6e8c6141..06d2a24ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ debug = "line-tables-only" [workspace.package] authors = ["Benjamen Keroack ", "20 Gauge Software, Inc.", "The satd developers"] -version = "0.6.0-rc1" +version = "0.6.0" edition = "2024" license = "MIT" repository = "https://github.com/epochbtc/satd" diff --git a/clients/go/version.go b/clients/go/version.go index 88cf3d877..be4803c44 100644 --- a/clients/go/version.go +++ b/clients/go/version.go @@ -4,7 +4,7 @@ package satdevents // released with: the module is tagged clients/go/vX.Y.Z at the node's vX.Y.Z // commit, even when no Go code changed. TestVersionMatchesWorkspace pins it to // the workspace Cargo.toml, so a version bump that misses this file fails CI. -const Version = "0.6.0-rc1" +const Version = "0.6.0" // schemaVersion is the NodeEvent.schema_version this SDK decodes. It mirrors // SCHEMA_VERSION in the satd node; the two are bumped together. diff --git a/contrib/appliance/README.md b/contrib/appliance/README.md index 31e5156ff..4f6747cf0 100644 --- a/contrib/appliance/README.md +++ b/contrib/appliance/README.md @@ -61,7 +61,7 @@ contrib/appliance/build-in-docker.sh --flavor core --out contrib/appliance/out # From a published, signed release instead. contrib/appliance/build-in-docker.sh --flavor desktop \ - --satd-source release --satd-version 0.5.2 --out out/ + --satd-source release --satd-version 0.6.0 --out out/ # With the tools already on the host. sudo contrib/appliance/build.sh --flavor core --out out/ diff --git a/contrib/warnet/README.md b/contrib/warnet/README.md index 842512f52..4f954220e 100644 --- a/contrib/warnet/README.md +++ b/contrib/warnet/README.md @@ -37,8 +37,8 @@ Warnet renders only image tags shaped like `X.Y[.Z][-suffix]`. Tag the image `-warnet`: ```sh -docker build --build-arg SATD_IMAGE=ghcr.io/epochbtc/satd:0.5.2 \ - -t satd-warnet:0.5.2-warnet0 contrib/warnet +docker build --build-arg SATD_IMAGE=ghcr.io/epochbtc/satd:0.6.0 \ + -t satd-warnet:0.6.0-warnet0 contrib/warnet ``` `SATD_IMAGE` defaults to `ghcr.io/epochbtc/satd:latest`. Any satd image @@ -47,7 +47,7 @@ works, including one built from this repository with `docker build -t satd:dev . Load the image into the cluster. With [kind](https://kind.sigs.k8s.io/): ```sh -kind load docker-image satd-warnet:0.5.2-warnet0 --name +kind load docker-image satd-warnet:0.6.0-warnet0 --name ``` The examples set `pullPolicy: Never`, so a tank whose image is missing fails diff --git a/contrib/warnet/examples/all-satd/node-defaults.yaml b/contrib/warnet/examples/all-satd/node-defaults.yaml index 8abae0f04..d625baf42 100644 --- a/contrib/warnet/examples/all-satd/node-defaults.yaml +++ b/contrib/warnet/examples/all-satd/node-defaults.yaml @@ -3,5 +3,5 @@ # missing image fail loudly instead of pulling something else. image: repository: satd-warnet - tag: "0.5.2-warnet0" + tag: "0.6.0-warnet0" pullPolicy: Never diff --git a/contrib/warnet/examples/core-satd/network.yaml b/contrib/warnet/examples/core-satd/network.yaml index 7391b6742..cad59d86b 100644 --- a/contrib/warnet/examples/core-satd/network.yaml +++ b/contrib/warnet/examples/core-satd/network.yaml @@ -7,7 +7,7 @@ nodes: - name: tank-0001 image: repository: satd-warnet - tag: "0.5.2-warnet0" + tag: "0.6.0-warnet0" pullPolicy: Never addnode: - tank-0002 @@ -17,7 +17,7 @@ nodes: - name: tank-0003 image: repository: satd-warnet - tag: "0.5.2-warnet0" + tag: "0.6.0-warnet0" pullPolicy: Never addnode: - tank-0000 diff --git a/docs/manual/src/electrum.md b/docs/manual/src/electrum.md index 455985d5f..fd0350aed 100644 --- a/docs/manual/src/electrum.md +++ b/docs/manual/src/electrum.md @@ -210,7 +210,7 @@ Override it if you would rather not advertise the token, or need a different one for another client: ```ini -electrumservername=satd/0.5.2 +electrumservername=satd/0.6.0 ``` Keep an override from *beginning* with `electrs`. Several clients test that diff --git a/docs/release-notes/0.6.0-pre.md b/docs/release-notes/0.6.0.md similarity index 99% rename from docs/release-notes/0.6.0-pre.md rename to docs/release-notes/0.6.0.md index 427d8313d..ee6d46594 100644 --- a/docs/release-notes/0.6.0-pre.md +++ b/docs/release-notes/0.6.0.md @@ -1,6 +1,6 @@ # satd 0.6.0 -> Pre-release — in progress · [Changelog](../../CHANGELOG.md) +> Released 2026-10-05 · git tag [`v0.6.0`](https://github.com/epochbtc/satd/releases/tag/v0.6.0) · [Changelog](../../CHANGELOG.md) satd 0.6.0 builds a **solo-mining server** into the node, Stratum V1 and V2 with Job Declaration, so a miner can point its hardware at its own node with diff --git a/docs/release-notes/README.md b/docs/release-notes/README.md index db24f2f43..1364df505 100644 --- a/docs/release-notes/README.md +++ b/docs/release-notes/README.md @@ -16,6 +16,7 @@ The split is deliberate: | File | Release | Status | |---|---|---| +| [`0.6.0.md`](0.6.0.md) | 0.6.0 | Released 2026-10-05 | | [`0.5.2.md`](0.5.2.md) | 0.5.2 | Released 2026-09-11 | | [`0.5.1.md`](0.5.1.md) | 0.5.1 | Released 2026-09-04 | | [`0.5.0.md`](0.5.0.md) | 0.5.0 | Released 2026-08-25 | diff --git a/satd-events-client/Cargo.toml b/satd-events-client/Cargo.toml index 51846d0ff..788ff2e00 100644 --- a/satd-events-client/Cargo.toml +++ b/satd-events-client/Cargo.toml @@ -38,7 +38,7 @@ tls = ["dep:tokio-rustls", "tonic/tls", "tonic/tls-webpki-roots"] [dependencies] # Cargo uses `path` for the local build and `version` to resolve the published # dependency; keep this in lockstep with the workspace `version`. -satd-events-proto = { version = "=0.6.0-rc1", path = "../satd-events-proto" } +satd-events-proto = { version = "=0.6.0", path = "../satd-events-proto" } tonic = { workspace = true } prost = { workspace = true } # Only pulled by the `tls` feature, and only to install the `ring` rustls