Hardening the Atomic Swap Protocol: BasicSwap v0.16.3 to v0.16.6

Hardening the Atomic Swap Protocol: BasicSwap v0.16.3 to v0.16.6

By BasicSwap Teamโ€ขJune 22, 2026
NewsReleaseSecurity

Over the course of June 2026, BasicSwap shipped a series of releases (v0.16.3 through v0.16.6) with one goal: hardening the atomic-swap protocol. Atomic swaps are already built so that either both sides of a trade complete or both refund, and these releases tighten the checks that hold that guarantee in place.

The headline work is in fund-safety verification. The client confirms that a counterparty's on-chain lock holds the agreed amount before committing your own funds, both sides verify each other's refund paths before any coins are locked, and the client steers swaps toward the adaptor-signature protocol by default. Alongside that, v0.16.3 added automatic chain-feerate validation and subfee bids, and the cycle moved on to build and tooling upgrades. v0.16.6 carried the work forward with address-handling fixes and a round of coin-core updates.

None of these changes alter how you trade day to day, but they make the floor underneath every swap a little more solid.

โœ…Update to v0.16.6

v0.16.6 is the latest release in this series and rolls up every change below. We strongly recommend everyone update to it as soon as possible. Installation steps are at the bottom of this post.

Most Notable Updates ๐Ÿ’ก

Stronger Lock-Amount Verification ๐Ÿ”’

In a secret-hash atomic swap, each side locks coins in an on-chain HTLC, and before you commit your own funds the client checks that the counterparty's lock transaction holds the amount you agreed on. v0.16.3 and v0.16.4 strengthened that check.

The client confirms the value of the counterparty's lock output against the agreed amount, not just its script and address (commit 4ac0321). The check is fail-closed: it requires both the output index and value, and declines to proceed if either can't be read (commit 5e7dbbb). The same check covers the participate transaction as well, so both legs are held to it.

The principle is straightforward: confirm the value, not just the script. In the code, this lives in the client's bid-state checks in basicswap/basicswap.py, which read the locked output's index and value from the coin interface and confirm the value matches the agreed amount. The adaptor-signature swap is a separate protocol with its own equivalent check, verifySCLockTx in interface/btc.py, which ends in ensure(locked_coin == swap_value).

Symmetric Refund-Signature Verification ๐Ÿค

In an adaptor-signature (XMR-style) swap, before the leader locks its coins it receives a set of signatures from the follower, including the signature on the lock-refund transaction, which is the leader's own escape hatch if a swap doesn't go through.

v0.16.5 makes that verification fully symmetric. The leader verifies the follower's lock-refund signature with verifyTxSig before it commits any coins, matching the equivalent check on the follower's side (commit 597cdcb, PR #497). Both parties confirm each other's refund paths are valid before anything is locked, and the check is fail-closed: a signature that doesn't verify stops the bid before the leader publishes its lock.

This is protocol symmetry as defense in depth: in a two-party protocol, a check that protects one side generally wants a mirror on the other.

Smarter Swap-Type Defaults ๐Ÿ”„

BasicSwap supports both secret-hash (HTLC) and adaptor-signature swaps. Adaptor-signature swaps leave a smaller, less distinctive on-chain footprint: one leg is paid to an ordinary-looking combined key, and the swap secret is never revealed on-chain. That smaller footprint is why this series nudges trades toward them.

v0.16.3 made the adaptor-signature type the default for newly created offers, for every coin pair that supports it (commit 46e3d02). If you're curious about the difference between the two swap types, our earlier write-up on HTLC vs. adaptor-signature swaps covers it in depth.

Automatic Fee Validation & Subfee Bids ๐Ÿ’ธ

v0.16.3 added automatic chain-feerate validation. The client rejects feerates that fall outside a sensible range when you send a bid, send an offer, or receive an offer. By default the low bound is the node's estimated feerate for confirmation within 24 blocks, and the high bound is 4ร— the node's fast-confirmation estimate. Both bounds are tunable through low_fee_conf_target, low_feerate, high_estimated_feerate_multiplier, and high_feerate. This trims both accidental fee overpayment and a class of fee-based nuisance behaviour.

The same release introduced subfee bids, which let you create a bid specifying the amount before the lock-transaction fee is taken out (currently when the "to" coin isn't XMR-like), plus a new startup_delay setting for how long the client waits for coin daemons to come up between startup tries (per-coin overridable). On the UI side, the offer page now fixes the other-chain feerate display on reversed swaps, warns when an offer's fee differs from the local estimate by more than 1.2ร— in either direction, and exposes the subfee bid option.

v0.16.6 made this validation more forgiving in everyday use. It eases the limits when only a relay-fee floor is available, and adds an optional skip-fee-checks control when you place a bid, so a brief fee spike cannot block a trade you want to make (commits be3c63e, 7a57cee).

Continued Hardening in v0.16.6 ๐Ÿ›ก๏ธ

The hardening work carried into v0.16.6.

The Electrum-mode wallet now avoids handing out a receive address that was already used, and corrects how the address gap limit is measured: it counts the run of unused addresses at the end of the derivation chain rather than the raw index. A database migration adds an ever_used flag so existing wallets get the same protection. This is a privacy and correctness improvement (commit acb889f, PR #492).

Particl's anonymous and blind scriptless lock transactions are now matched against the specific lock transaction id, output index, and amount before they are spent, so the correct output is always the one selected (commit c52f882).

Lock-transaction verification also became more direct. When the transaction id and output index are known, the client reads the output with gettxout instead of rescanning the chain (commit 5391c57). That keeps the lock-amount checks from the earlier releases both quick and reliable.

Coin Core Updates ๐Ÿช™

v0.16.6 refreshes two coin cores, and BasicSwap's prepare script applies them automatically when you run the update with --upgradecores.

โš ๏ธMandatory Dash Core update (23.1.4)

If you have Dash enabled on BasicSwap, v0.16.6 upgrades Dash Core to 23.1.4, which is a mandatory update for Dash. Run your update with the --upgradecores step so it is applied.

Particl Core was updated to 27.2.4. It includes an important hardening update to Particl's secure-messaging layer, which BasicSwap uses to carry offers and bids. The same hardening is available for the other maintained Particl lines, 23.2.10.0 and 0.19.2.25, for nodes running those.

โ„น๏ธParticl node operators: please update soon

Particl Core 27.2.4 is not a mandatory consensus update, but it is an important secure-messaging hardening release. We strongly encourage everyone running a Particl node to update as soon as they can, to 27.2.4 or to 23.2.10.0 / 0.19.2.25 if you run one of those lines.

Under the Hood ๐Ÿงน

The series also refreshed the build and test surface. Minimum Python moved to 3.11 (commit df672d4), the CI test matrix was raised to Python 3.14 (commit f536f89), and the Docker base images were updated to Debian Trixie (commit d23665d).

Most usefully for the changes above, a new regression test simulates an underfunded initiate transaction through a MAKE_INVALID_ITX debug hook (commit 379eaaf), so the strengthened lock-amount verification can't quietly regress in a future release.

The Principles Behind These Changes ๐Ÿ“

A few principles guide how BasicSwap handles funds, and this series of updates leans into all three:

  • Check the amount, not just the address. Matching a counterparty's transaction by address is necessary but not sufficient. The value it locks is checked against what was agreed.
  • Fail closed. When a safety check can't get the information it needs, it stops rather than proceeding.
  • Symmetry. Both sides of a swap verify each other's refund paths, not just one.

Install the Latest BasicSwap Update ๐Ÿ–ฅ๏ธ

To install, update BasicSwap through the usual process and relaunch.

โ„น๏ธRun --upgradecores when you update

v0.16.6 updates coin daemons, including a mandatory Dash Core update (23.1.4) and Particl Core 27.2.4. If you are coming from v0.16.2 or earlier, you also pick up the v0.16.3 daemon updates, including a mandatory Firo update (0.14.15.3 to 0.14.16.1) along with Monero and Decred. Run your update with the --upgradecores step so these cores are upgraded.

Docker

If you've installed BasicSwap following the Docker method:

  1. Shutdown BasicSwap properly and stop the Docker image (docker-compose stop),
  2. From the basicswap folder, type the git pull command,
  3. In the /docker folder, run the docker-compose build --no-cache command,
  4. Once the process completes, launch BasicSwap again using the docker-compose up command.

Note: Depending on your Docker version, adding a dash between docker and compose may not be necessary and may instead throw an error.

Non-Docker

If you've installed BasicSwap following the non-Docker method:

  1. Shutdown BasicSwap properly,
  2. From the ~/coinswaps/basicswap folder, type the git pull command,
  3. Execute the following command: pip install --require-hashes -r requirements.txt,
  4. Execute the following command: pip3 install .,
  5. Launch BasicSwap as usual.

Install Script

If you've installed BasicSwap using Nahuhh's GitHub installation script:

  • Execute the update command: bsx-update.

Changelog ๐Ÿ“

A single, merged changelog spanning v0.16.3 through v0.16.6.

Security & Fund Safety

  • Verify the initiate (lock) transaction amount for secret-hash swaps [4ac0321] (v0.16.3)
  • Always require the initiate-tx output index and value, and reject the swap (fail closed) when either is missing [5e7dbbb] (v0.16.4)
  • Double-check the participate-tx output amount for secret-hash swaps [5e7dbbb] (v0.16.4)
  • Verify the follower's script-chain lock-refund transaction signature before the leader commits funds [597cdcb] / PR #497 (v0.16.5)
  • Prevent reused receive addresses and fix the Electrum wallet gap-limit calculation [acb889f] / PR #492 (v0.16.6)
  • Improve Particl anon and blind scriptless lock-tx detection and spending [c52f882] (v0.16.6)

Swap Protocol & Defaults

  • Make the adaptor-signature type the default for newly created offers, where the coin pair supports it [46e3d02] (v0.16.3)
  • In the offer UI, select the secret-hash swap type only when both coins lack segwit [4062959] (v0.16.6)
  • AMM: dialog fixes (swap type auto-updates; stop silently changing the other coin) [2555c8c] [e757d43] (v0.16.6)
  • Wait for refund / refund-spend transaction locks to expire before attempting to submit them (v0.16.3)

Coin(s)

  • Particl: Updated to v27.2.4 (secure-messaging hardening) [339a7a0] (v0.16.6)
  • Dash: Upgraded to v23.1.4 [mandatory] [9db8067] (v0.16.6)
  • Firo: Spark withdraw fix [5751966] (v0.16.6)

Fees & Bids

  • Automatic chain-feerate validation when sending a bid, sending an offer, or receiving an offer, tunable via low_fee_conf_target, low_feerate, high_estimated_feerate_multiplier, high_feerate (v0.16.3)
  • Subfee bids: specify the bid amount before the lock-tx fee (when the "to" coin isn't XMR-like) (v0.16.3)
  • New startup_delay setting, per-coin overridable (v0.16.3)
  • UI: fixed other-chain feerate display on reversed swaps, a warning when an offer's fee differs from the local estimate by more than 1.2ร— in either direction, and a subfee bid option (v0.16.3)
  • Increase the DCR fee estimate by 1 byte (v0.16.3)
  • Add a skip-fee-checks option when placing bids and ease the feerate validation limits [7a57cee] [be3c63e] (v0.16.6)

Tests

  • Add a regression test for an underfunded initiate transaction via a new MAKE_INVALID_ITX debug hook [379eaaf] (v0.16.5)
  • Add an Electrum subfee bid test [70a67bf] (v0.16.6)

Build / Tooling

  • Raise the minimum Python version to 3.11 [df672d4] (v0.16.4)
  • Raise the CI test matrix to Python 3.14 [f536f89] (v0.16.4)
  • Update Docker base images to Debian Trixie [d23665d] (v0.16.5)
  • black formatter version bumps [136b311] [553b5a6]
  • Use gettxout in getLockTxHeight() for faster, direct lock-tx verification [5391c57] (v0.16.6)
  • Add an alternative Dash release signer [bf723b4] (v0.16.6)

You can inspect all changes by verifying the v0.16.2 to v0.16.6 comparison page here.

Stay Connected

Keep up with BasicSwap on social media: