Shrike signing with the unified opt-in signature hash as Bitcoin Knots v29.4.1.knots20260508rc4 defines it, on upstream Sparrow 2.5.5.
Every signer can sign
A device that cannot opt in is no longer turned away. It is handed the hash type it can produce, and its signature combines with the opted-in ones. The previous release said this was out of reach because a PSBT carries one hash type per input and so cannot ask one cosigner for the opt-in and another for the legacy type. That is a limit of the container rather than of consensus: each device gets its own PSBT declaring what it can sign, and the partial signatures combine.
One marked signer is now enough, where the previous release wanted enough of them to meet the wallet's threshold. Replay protection belongs to the transaction rather than to each signature in it, and a transaction carrying a single opted-in signature is invalid under the pre-fork rules whatever the rest carry.
So a 2-of-3 with one signer on firmware that implements the opt-in is protected, and all three can still sign. Mark two and no quorum exists without a marked signer, so every transaction the wallet can make opts in. Mark one and the other two could meet the threshold between them, so the protection depends on the marked signer taking part. The wallet says which of those two it is rather than claiming the guarantee in advance.
What the send screen now separates
Replay protection and the commitment to spent amounts are two properties with two thresholds, and the previous release reported only the first. The transaction is unreplayable on one opted-in signature; committing to every amount it spends, which is what closes CVE-2020-14199, belongs to each signature and takes that signature opting in. Over a mixed set the send screen now says how many opted in, and that the others were shown the amounts by this computer rather than committing to them.
It also says when one of those signatures can be lifted out. A legacy SIGHASH_ALL signature commits to every input, so it is useless in any other transaction. A legacy ANYONECANPAY one commits only to its own input and to the outputs, so that input alone can be lifted into another transaction and spent on the chain that kept SHA256d, paying the same outputs. The transaction is still protected; that one input is not, and reporting only the first would have hidden it. Choosing Anyone Can Pay is what reaches this, and the wallet never selects it on its own account.
Verified
The replay protection of a mixed transaction is demonstrated rather than argued. Both chains are built from one shared history so the same coins exist on each, then:
1. post-fork node accepts and mines the mixed transaction (one input 0x21, one 0x81)
2. node that never scheduled the fork refuses it:
mempool-script-verify-flag-failed (Signature opts in to the hardfork, which is not active here)
3. the same spend signed entirely the old way is accepted there
4. the ANYONECANPAY signature, copied out byte for byte, is mined on the pre-fork chain
Step 3 is what makes step 2 mean something: without it a refusal could be about a fee or a missing input rather than the opt-in. Step 4 is the caveat above, made real. Two mutations confirm it is not vacuous: signing that leg SIGHASH_ALL instead, or altering one output, both make the lift fail, so the lift is real but bounded to the same output set.
A 2-of-2 with one patched signer is proven too, which is the case most people are asking about. Both cosigners sign the same input, one producing 0x21 and the other 0x01 in a single witness. Consensus allows this because each signature carries its own hash type byte, so CHECKMULTISIG verifies each against the message that signature names. A post-fork node mines it, a node that never scheduled the fork refuses it, and the same 2-of-2 signed entirely the old way is accepted there.
Neither device has to be lied to about its firmware. Leave the unpatched one unmarked and it is handed the type it can produce. Marking a device whose firmware does not implement the opt-in is the one way to reach a device that will not sign, which is why the dialog says to leave it unmarked if you are unsure.
Each behavior above was checked by reverting the code that implements it and confirming the test covering it fails, then restoring.
343 wallet and 611 drongo unit tests, and seven node-driven end to end tests against a Knots regtest node.
Reproducible: this commit built locally with Eclipse Temurin 25.0.2 produces an application tree whose 171 paths, every file and symlink, hash identically to the tree CI published in shrike-2.5.5-x86_64.tar.gz. That archive is the one listed in SHA256SUMS as ec5a9cea885a14df14f44dca201884c34844f7dae21c98eea14cf1f664900b38, so the check is anchored to the file published here rather than to a build step. Only the Linux x86_64 build was reproduced; the archive contents are compared rather than the installers.
Also in this release
The Replay protection dialog is legible. Its height follows the number of keystores rather than being fixed, the content lines up with the header, and it reports which of the three states the wallet is in as devices are marked, instead of only whether the threshold was met.
A device that cannot produce the declared hash type is told why before it refuses, rather than surfacing raw firmware text.
The send screen names the signers that can opt in, rather than saying "the marked signers" and leaving you to work out which.
Downloads
| Platform | File |
|---|---|
| Windows | Shrike-2.5.5.msi, or .zip for a portable copy |
| macOS | Shrike-2.5.5-arm64-unsigned.dmg, or -x86_64-unsigned.dmg for Intel |
| Linux x86_64 | shrike_2.5.5-1_amd64.deb, or -x86_64.tar.gz |
| Linux aarch64 | shrike_2.5.5-1_arm64.deb, or -aarch64.tar.gz |
| Headless | the shrikeserver-* files |
Verify before running:
gpg --import privkeyio-signing-key.asc
gpg --verify SHA256SUMS.asc SHA256SUMS
sha256sum --ignore-missing -c SHA256SUMSSigned by Kyle Santiago kyle@privkey.io, key A47D99B6DB0D715D40C59A2023AE8A8EA7E24E38.
The macOS builds are not code signed or notarized, so macOS reports the app as damaged or from an unidentified developer on first launch. That means it is unsigned, not that the download is bad:
xattr -dr com.apple.quarantine /Applications/Shrike.app
Verify SHA256SUMS first, because clearing quarantine removes the check that would otherwise stop a tampered download. The Windows installer is not Authenticode signed either, and SmartScreen will warn accordingly.