Damus

Recent Notes

Rizful.com · 2h
Are you sure lnbits can create a receive-only code?
m0wer profile picture
Bitcoin xpub watchdog with transaction notifications
https://gist.github.com/m0wer/498490d77fd33b5a41eb6bd480c7fae8

After the event of the last weeks, I wondered how long would it take to realize that you have been stolen from. Given that people don't manually check all of their balances daily (I guess). And that noticing fast, could help you save part of your stack (maybe).

So I made a tiny Bitcoin Core watchdog.

The idea is simple: import account-level xpubs into watch-only descriptor wallets and let Bitcoin Core notify a small Python script whenever something moves.

By default it alerts twice if appropriate: once when the transaction first appears in your node's mempool, and once when it gets confirmed.

That second alert is important because a transaction does not necessarily have to enter the public mempool. It could, for example, be submitted directly to a miner and first become visible to your node when the block arrives.

The script has no database and keeps no wallet state. Bitcoin Core does all the actual monitoring.

Install it:

```
sudo install -m 755 bitcoin_core_watchdog.py /usr/local/bin/bitcoin_core_watchdog.py
```

Store a Gotify application token somewhere readable by Bitcoin Core:

```
echo 'YOUR_GOTIFY_TOKEN' > /etc/bitcoin-watchdog.token
chmod 600 /etc/bitcoin-watchdog.token
```

Then add to `bitcoin.conf`:

```
keypool=5000
blockfilterindex=1

walletnotify=env GOTIFY_URL=https://gotify.example.com GOTIFY_TOKEN_FILE=/etc/bitcoin-watchdog.token /usr/local/bin/bitcoin_core_watchdog.py notify %w %s %b %h
```

A self-hosted Gotify instance is ideal if privacy is the goal. The Gotify-specific part is isolated in `send_notification()`, so adapting it to ntfy, Pushover, email, Telegram, etc. should be straightforward.

Test notifications:

```
GOTIFY_URL=https://gotify.example.com \
GOTIFY_TOKEN_FILE=/etc/bitcoin-watchdog.token \
bitcoin_core_watchdog.py test-gotify
```

Then add the accounts you want to watch.

Native SegWit/BIP84:

```
bitcoin_core_watchdog.py add cold-storage segwit 'zpub...' # or xpub... (also works)
```

Taproot/BIP86:

```
bitcoin_core_watchdog.py add taproot-vault taproot 'xpub...'
```

Don't put seed phrases or private keys on the monitoring node. Export an account-level xpub from the actual wallet and keep the Core wallets watch-only.

For an existing wallet, give Core an approximate birthday so it knows how far back to scan:

```
# get the epoch with `date -d "2026-01-01 00:00:00" +%s`
bitcoin_core_watchdog.py add cold-storage segwit 'zpub...' \
--timestamp 1767222000
```

Or use:

```
--timestamp 0
```

to scan the entire chain. New wallets default to `--timestamp now`.

The `add` command is only a convenience wrapper. You can do the same thing manually with `bitcoin-cli`: create a blank watch-only descriptor wallet with autoload enabled and import the appropriate descriptor.

A normal transaction might therefore produce:

```
Bitcoin mempool activity: cold-storage

sent 0.5 BTC · RBF
Unconfirmed / in local mempool
https://mempool.space/tx/...
```

followed by:

```
Bitcoin confirmed activity: cold-storage

sent 0.5 BTC
Confirmed at block 123456
https://mempool.space/tx/...
```

If the transaction bypasses your mempool and gets sent directly to a miner, you simply get the second notification.

The explorer link defaults to `https://mempool.space/tx/<txid>`. That's not ideal for privacy, but the script never queries mempool.space. It only puts the URL in the notification, so nothing is sent there unless you actually click it.

If you run your own explorer, set:

```
MEMPOOL_URL=https://mempool.example.com
```

So the whole thing is basically:

Bitcoin Core → watch-only descriptor wallets → mempool/block detection → walletnotify → Gotify

No external address lookup service, no xpubs handed to a third party, and no private keys on the monitoring machine.

https://stacker.news/items/1551517
1
Diego Valley · 5d
Is that Flo Dan?
Super Testnet · 1w
Yeah! But there are some sad aspects to this: (1) most of the rental sites do KYC on their users (2) I think 100% of them are custodial. It would be really cool to have a model where a rental website gets a "prepayment" that is locked in such a way that it only gets "unlocked" if they deliver the ex...
m0wer profile picture
One protocol level vulnerability that @/dev/fd0 found recently is https://github.com/joinmarket-ng/joinmarket-ng/issues/577

Summary: the directory nodes don't authenticate peer nicks, so peers can impersonate others and steal their connection. For example by updating the onion to their one and by being the newest connection with that nick and getting their messages. Important messages are encrypted, but the impact of "eclipsing" other makers is relevant. No loss of funds, but yes denial of service.

The fix required a protocol fix, that involves makers and directory nodes. My signet directory node is updated with the fix. Directory node runners on mainnet are encouraged to update.

The fix is only for jm-ng, but backwards compatible. So old directories and makers keep working, but are vulnerable to the attack. Good thing is that new directories still benefit old peers by not allowing new connections to steal their nick. But this is not a strong protection.

The full details are at https://github.com/joinmarket-ng/joinmarket-ng/issues/577

And the protocol improvement proposal is at https://github.com/joinmarket-ng/jmp/blob/main/jmp-0005.md

Thanks to floppy for finding it and to waxwing for helping us understand the issue and design the fix.
3❤️11❤️1👀1
Bitpower · 1w
Off topic: does seedsigner support creating “accounts” under a multi sig setup? As in can it present the correct derivation paths for it?