What an atomic swap is
You pick a random secret and give the other party only its hash. You lock your coins into a script on your chain that says: spendable by them if they present the secret, or refundable by me after 48 hours. They lock their coins into the mirror script on their chain: spendable by you with the secret, or refundable by them after 24 hours.
You take their coins by publishing the secret. Publishing it is unavoidable: it is in the transaction. They then use the same secret to take yours. If they never lock, you refund after 48 hours and have lost a transaction fee. If you never reveal, they refund after 24 hours. At no moment has one side paid while the other has not.
The two rules that make it safe
- Whoever holds the secret locks first, with the longer timeout. You reveal nothing until you are spending their coins. If you are the one with the secret, you cannot lose the trade; you can only fail to complete it.
- The second locker gets the shorter timeout. If the timeouts were the other way round, the first locker could wait for their own refund window to open and race the claim. Check this number in their script yourself.
Where a dishonest counterparty attacks
Every real attack on a swap between these two chains lands on one of five places. Each has a check.
- Their lock is on the wrong chain. Addresses look identical on both
chains. A screenshot of a
bc1qaddress holding coins proves nothing. Confirm their contract output on the legacy chain with your own legacy node or two independent explorers, and confirm theirs on the BLAKE2b chain with a node on this chain. - Their lock is unconfirmed or replaceable. Wait for confirmations before you reveal the secret: six on legacy bitcoin, more on this chain while a rented petahash can still reorg it.
- Their script is not what they claim. Wrong hash, wrong key, wrong timeout. Never trust an address; decode the script bytes and compare every field to your own numbers. The tool below refuses anything that is not the exact expected shape.
- Replay. A coin that existed before block 961640 exists on both chains, and a transaction spending it is valid on both. Fund your side only with coins created after the split: mined coins, or coins you have already split.
- "Just send a small test first", "use my escrow site", "run my swap tool". No. A test is what the swap already is. Software they hand you is the one way a correctly built swap can still be lost.
xorswap: a tool that builds the contract
xorswap is a single Python
file with no dependencies. It generates the keys and the secret, builds and
audits the script, finds the funding output through bitcoin-cli, and
signs the claim and refund transactions. The node is used only to read the
chain and broadcast; every transaction is printed as raw hex and checked with
testmempoolaccept before it is sent, so you can read what you are
about to broadcast.
The easy way is the guided mode. Tell it once how to reach a node on each chain, then run one command per step:
xorswap.py config --xor "bitcoin-cli ..." --legacy "bitcoin-cli ..."xorswap.py wizard --deal mydeal, then the same command again after each message from the other party. It prints exactly what to send them, audits what they send back, waits for their lock to have enough confirmations, and builds and checks the claim before asking you to broadcast. The matching side recovers the secret from the other party's claim by itself.xorswap.py refund-deal --deal mydealtakes your coins back after your locktime if the other side never turned up.
The individual commands underneath, for a swap where you sell BLAKE2b coins for legacy bitcoin. You are Alice. They are Bob.
- Both:
xorswap.py keygenonce each. Exchange public keys only. - Alice:
xorswap.py secret. Send Bob the hash. Keep the secret offline. - Alice:
xorswap.py contract --hash H --redeemer BOB_PUB --refunder ALICE_PUB --locktime NOW+48h. Send Bob the script hex. Fund the printed address on the BLAKE2b chain from post-split coins. - Bob:
xorswap.py audit --script HEX --hash H --redeemer BOB_PUB --refunder ALICE_PUB --min-locktime NOW+40h. Then his own contract:--redeemer ALICE_PUB --refunder BOB_PUB --locktime NOW+24h, funded on legacy bitcoin. - Alice: audit Bob's script the same way, with
--max-locktime NOW+30hso his window is shorter than yours.xorswap.py find --address BOB_CONTRACT --cli "bitcoin-cli ..."against a legacy node. Wait for six confirmations. - Alice:
xorswap.py claim --txid ... --vout ... --amount ... --script BOB_SCRIPT --secret S --key ALICE_PRIV --to ALICE_LEGACY_ADDR --cli "..." --broadcast. The legacy bitcoin is yours. The secret is now public. - Bob: reads the secret from Alice's claim transaction and runs the same
claimagainst Alice's contract on the BLAKE2b chain. - If anything stalls:
xorswap.py refundwith your own key, once your locktime has passed. The node rejects it asnon-finaluntil then.
Locktimes are unix timestamps. The tool refuses a claim whose key is not the script's redeemer, a refund whose key is not the refunder, a secret that does not hash to the script's hash, and any script that is not byte-for-byte the expected shape. It was tested on regtest for the claim path, the refund path, a refund attempted too early, and payouts to both bech32 and legacy addresses.