watchtower

package
v0.0.0-...-8c3d3b4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2022 License: MIT Imports: 13 Imported by: 3

README

watchtower - watch channels for fraudulent transactions

The watchtower package implements unlinkable channel monitoring and recovery for lightning network channels.

Design

To nodes directly dealing with channels, channels are identified by their outpoint (hash + 32 bit index), or by some stand in with a 1:1 mapping to outpoints. Since the goal of this design is that the watchtower never learns the outpoint, channels must be identified another way: by penalty output address.

(Note that you could not identify channels at all, and simply have a mapping of commitment transactions to penalty transactions without grouping them. This could work, but has several problems - it uses ~3-4X more data, and is very difficult to delete and recover old space. Anonymity could be somewhat improved though, so it's could be worth looking into.)

The 20-byte penalty output pubkey hash identifies a watched channel. There are 3 data stores associated with the channel: static, elkrem, and sigidx. (Note that these aren't stored in the same place in the database)

static

Data that stays the same for the duration of the channel.

DestPKH : the destination pub key hash of the penalty transaction. Also the "name" of the channel.

HAKDBase : The HAKD base point which becomes the revocable pubkey in the commitment script. This is the key which the watchtower receives signatures for.

TimeBase : The timeout base point, which becomes the timeout pubkey in the commitment script. The watchtower never deals with signatures from this key, and only needs to know it to build the script hash pre-image.

Delay / fee : Delay should stay the same for the duration of the channel. Dealing with changing fees is... TBD; it's static for now.

elkrem

Stores the customer's elkrem receiver associated with the channel. Overwritten each time, but never gets too big.

sigidx

Stores signatures and partial txids. This is where most of the data is. This is stored in a separate database / tree which is sorted by txid. The value associated with each txid is the signature, along with the commitment number so that the proper elkrem points can be generated.

database

The database is structured based on the assumptions that fraudulent channel closes basically never happen. But that transactions come in very often. And there are lots of sigs per channel.

These are assumptions that seem reasonable, but if actual usage doesn't match these assumptions, it will still work but not be optimal.

operations and costs

C = number of channels being watched S = total number of stored signatures (channels * sigs per channel)

Create a new channel: O(C)

Update, adding a state to existing channel: O(log(S))

Ingest tx: O(log(S)) (binary search over partial txids)

Delete a channel: O(Slog(S)) (slow! loglinear w/ total number of sigs!)

Deleting is tough, but we assume channel creation / deletion is infrequent compared to adding sigs and txs coming in. For ingesting txs, there's 2 options : Waiting for a block and ingesting all the txs that way, or ingesting for every tx seen in the mempool. I'm not sure which is better. It's a small change so I can just test that.

cache before send

A design goal of lit is to maximize the information that can be safely forgotten. By default nodes don't remember how much money they had in the previous states. Because of this, based on the data they have, they can't create ComMsgs to send to watchtowers (they can't make the tx to make the sig). Instead, they create sigs for the watchtower and cache them locally to later export.

Every lit node has the watchtower code built in. You could make a stand-alone watchtower I suppose, but there's not much to save. If the watchtower functionality is active, lit nodes must download full blocks (hard mode)

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	BUCKETPKHMap   = []byte("pkm") // bucket for idx:pkh mapping
	BUCKETChandata = []byte("cda") // bucket for channel data (elks, points)
	BUCKETTxid     = []byte("txi") // big bucket with every txid

	KEYStatic = []byte("sta") // static per channel data as value
	KEYElkRcv = []byte("elk") // elkrem receiver
	KEYIdx    = []byte("idx") // index mapping
)

Functions

This section is empty.

Types

type IdxSig

type IdxSig struct {
	PKHIdx   uint32   // Who
	StateIdx uint64   // When
	Sig      [64]byte // What
}

IdxSig is what we save in the DB for each txid

func BuildIdxSig

func BuildIdxSig(who uint32, when uint64, sig [64]byte) IdxSig

don't use this? inline is OK...

func IdxSigFromBytes

func IdxSigFromBytes(b []byte) (*IdxSig, error)

type WatchTower

type WatchTower struct {
	Path string // where the DB goes?  needed?

	WatchDB *bolt.DB // single DB with everything in it

	Accepting bool // true if new channels and sigs are allowed in
	Watching  bool // true if there are txids to watch for

	SyncHeight int32 // last block we've sync'd to.  Not needed?

	// map of cointypes to chainhooks
	Hooks map[uint32]uspv.ChainHook
}

The main watchtower struct

func (*WatchTower) BlockHandler

func (w *WatchTower) BlockHandler(
	cointype uint32, bchan chan *wire.MsgBlock)

func (*WatchTower) BuildJusticeTx

func (w *WatchTower) BuildJusticeTx(
	cointype uint32, badTx *wire.MsgTx) (*wire.MsgTx, error)

BuildJusticeTx takes the badTx and IdxSig found by IngestTx, and returns a Justice transaction moving funds with great vengeance & furious anger. Re-opens the DB which just was closed by IngestTx, but since this almost never happens, we need to end IngestTx as quickly as possible. Note that you should flag the channel for deletion after the JusticeTx is broadcast.

func (*WatchTower) DeleteChannel

func (w *WatchTower) DeleteChannel(m lnutil.WatchDelMsg) error

TODO implement DeleteChannel. Would be nice to delete old channels.

func (w *WatchTower) HookLink(dbPath string, param *coinparam.Params,
	hook uspv.ChainHook) error

Hooklink is the connection between the watchtower and the blockchain Takes in a channel of blocks, and the cointype. Immediately returns a channel which it will send justice transactions to.

func (*WatchTower) MatchTxids

func (w *WatchTower) MatchTxids(
	cointype uint32, txids []chainhash.Hash) ([]chainhash.Hash, error)

MatchTxid takes in a txid, checks against the DB, and if there's a hit, returns a IdxSig with which to make a JusticeTx. Hits should be rare.

func (*WatchTower) NewChannel

func (w *WatchTower) NewChannel(m lnutil.WatchDescMsg) error

AddNewChannel puts a new channel into the watchtower db. Probably need some way to prevent overwrites.

func (*WatchTower) OpenDB

func (w *WatchTower) OpenDB(filepath string) error

Opens the DB file for the LnNode

func (*WatchTower) UpdateChannel

func (w *WatchTower) UpdateChannel(m lnutil.WatchStateMsg) error

AddMsg adds a new message describing a penalty tx to the db. optimization would be to add a bunch of messages at once. Not a huge speedup though.

type Watcher

type Watcher interface {
	// Links to the blockchain.
	// Uses the same chainhook interface as the wallit does.  But only uses
	// 2 of the functions: PushTx() and RawBlocks()
	// Blocks come in from the chainhook, and justice transactions come out.
	// The uint32 is the cointype, the string is the folder to put all db files.
	HookLink(string, *coinparam.Params, uspv.ChainHook) error

	// New Channel to watch
	NewChannel(lnutil.WatchDescMsg) error

	// Update a channel being watched
	UpdateChannel(lnutil.WatchStateMsg) error

	// Delete a channel being watched
	DeleteChannel(lnutil.WatchDelMsg) error
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL