/* Code to talk to bitcoind to watch for various events.
 *
 * Here's what we want to know:
 *
 * - An anchor tx:
 *   - Reached given depth
 *   - Times out.
 *   - Is unspent after reaching given depth.
 *
 * - Our own commitment tx:
 *   - Reached a given depth.
 *
 * - HTLC spend tx:
 *   - Reached a given depth.
 *
 * - Anchor tx output:
 *   - Is spent by their current tx.
 *   - Is spent by a revoked tx.
 *
 * - Commitment tx HTLC outputs:
 *   - HTLC timed out
 *   - HTLC spent
 *
 * We do this by adding the P2SH address to the wallet, and then querying
 * that using listtransactions.
 *
 * WE ASSUME NO MALLEABILITY!  This requires segregated witness.
 */
#include <bitcoin/script.h>
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/ptrint/ptrint.h>
#include <common/pseudorand.h>
#include <common/timeout.h>
#include <lightningd/bitcoind.h>
#include <lightningd/chaintopology.h>
#include <lightningd/lightningd.h>
#include <lightningd/log.h>
#include <lightningd/peer_control.h>
#include <lightningd/watch.h>

/* Watching an output */
struct txowatch {
	struct chain_topology *topo;

	/* Channel who owns us. */
	struct channel *channel;

	/* Output to watch. */
	struct txwatch_output out;

	/* A new tx. */
	enum watch_result (*cb)(struct channel *channel,
				const struct bitcoin_tx *tx,
				size_t input_num,
				const struct block *block);
};

struct txwatch {
	struct chain_topology *topo;

	/* Channel who owns us (or NULL, for wallet usage). */
	struct channel *channel;

	/* Transaction to watch. */
	struct bitcoin_txid txid;
	unsigned int depth;

	/* A new depth (0 if kicked out, otherwise 1 = tip, etc.) */
	enum watch_result (*cb)(struct lightningd *ld,
				struct channel *channel,
				const struct bitcoin_txid *txid,
				unsigned int depth);
};

const struct txwatch_output *txowatch_keyof(const struct txowatch *w)
{
	return &w->out;
}

size_t txo_hash(const struct txwatch_output *out)
{
	/* This hash-in-one-go trick only works if they're consecutive. */
	BUILD_ASSERT(offsetof(struct txwatch_output, index)
		     == sizeof(((struct txwatch_output *)NULL)->txid));
	return siphash24(siphash_seed(), &out->txid,
			 sizeof(out->txid) + sizeof(out->index));
}

bool txowatch_eq(const struct txowatch *w, const struct txwatch_output *out)
{
	return bitcoin_txid_eq(&w->out.txid, &out->txid)
		&& w->out.index == out->index;
}

static void destroy_txowatch(struct txowatch *w)
{
	txowatch_hash_del(&w->topo->txowatches, w);
}

const struct bitcoin_txid *txwatch_keyof(const struct txwatch *w)
{
	return &w->txid;
}

size_t txid_hash(const struct bitcoin_txid *txid)
{
	return siphash24(siphash_seed(),
			 txid->shad.sha.u.u8, sizeof(txid->shad.sha.u.u8));
}

bool txwatch_eq(const struct txwatch *w, const struct bitcoin_txid *txid)
{
	return bitcoin_txid_eq(&w->txid, txid);
}

static void destroy_txwatch(struct txwatch *w)
{
	txwatch_hash_del(&w->topo->txwatches, w);
}

struct txwatch *watch_txid(const tal_t *ctx,
			   struct chain_topology *topo,
			   struct channel *channel,
			   const struct bitcoin_txid *txid,
			   enum watch_result (*cb)(struct lightningd *ld,
						   struct channel *channel,
						   const struct bitcoin_txid *,
						   unsigned int depth))
{
	struct txwatch *w;

	w = tal(ctx, struct txwatch);
	w->topo = topo;
	w->depth = 0;
	w->txid = *txid;
	w->channel = channel;
	w->cb = cb;

	txwatch_hash_add(&w->topo->txwatches, w);
	tal_add_destructor(w, destroy_txwatch);

	return w;
}

struct txwatch *find_txwatch(struct chain_topology *topo,
			     const struct bitcoin_txid *txid,
			     const struct channel *channel)
{
	struct txwatch_hash_iter i;
	struct txwatch *w;

	/* We could have more than one channel watching same txid, though we
	 * don't for onchaind. */
	for (w = txwatch_hash_getfirst(&topo->txwatches, txid, &i);
	     w;
	     w = txwatch_hash_getnext(&topo->txwatches, txid, &i)) {
		if (w->channel == channel)
			break;
	}
	return w;
}

bool watching_txid(const struct chain_topology *topo,
		   const struct bitcoin_txid *txid)
{
	return txwatch_hash_get(&topo->txwatches, txid) != NULL;
}

struct txwatch *watch_tx(const tal_t *ctx,
			 struct chain_topology *topo,
			 struct channel *channel,
			 const struct bitcoin_tx *tx,
			 enum watch_result (*cb)(struct lightningd *ld,
						 struct channel *channel,
						 const struct bitcoin_txid *,
						 unsigned int depth))
{
	struct bitcoin_txid txid;

	bitcoin_txid(tx, &txid);
	return watch_txid(ctx, topo, channel, &txid, cb);
}

struct txowatch *watch_txo(const tal_t *ctx,
			   struct chain_topology *topo,
			   struct channel *channel,
			   const struct bitcoin_txid *txid,
			   unsigned int output,
			   enum watch_result (*cb)(struct channel *channel,
						   const struct bitcoin_tx *tx,
						   size_t input_num,
						   const struct block *block))
{
	struct txowatch *w = tal(ctx, struct txowatch);

	w->topo = topo;
	w->out.txid = *txid;
	w->out.index = output;
	w->channel = channel;
	w->cb = cb;

	txowatch_hash_add(&w->topo->txowatches, w);
	tal_add_destructor(w, destroy_txowatch);

	return w;
}

/* Returns true if we fired a callback */
static bool txw_fire(struct txwatch *txw,
		     const struct bitcoin_txid *txid,
		     unsigned int depth)
{
	enum watch_result r;
	struct log *log;

	if (depth == txw->depth)
		return false;
	if (txw->channel)
		log = txw->channel->log;
	else
		log = txw->topo->log;

	log_debug(log,
		  "Got depth change %u->%u for %s",
		  txw->depth, depth,
		  type_to_string(tmpctx, struct bitcoin_txid, &txw->txid));
	txw->depth = depth;
	r = txw->cb(txw->topo->bitcoind->ld, txw->channel, txid, txw->depth);
	switch (r) {
	case DELETE_WATCH:
		tal_free(txw);
		return true;
	case KEEP_WATCHING:
		return true;
	}
	fatal("txwatch callback %p returned %i\n", txw->cb, r);
}

void txwatch_fire(struct chain_topology *topo,
		  const struct bitcoin_txid *txid,
		  unsigned int depth)
{
	struct txwatch *txw;

	txw = txwatch_hash_get(&topo->txwatches, txid);

	if (txw)
		txw_fire(txw, txid, depth);
}

void txowatch_fire(const struct txowatch *txow,
		   const struct bitcoin_tx *tx,
		   size_t input_num,
		   const struct block *block)
{
	struct bitcoin_txid txid;
	enum watch_result r;

	bitcoin_txid(tx, &txid);
	log_debug(txow->channel->log,
		  "Got UTXO spend for %s:%u: %s",
		  type_to_string(tmpctx, struct bitcoin_txid, &txow->out.txid),
		  txow->out.index,
		  type_to_string(tmpctx, struct bitcoin_txid, &txid));

	r = txow->cb(txow->channel, tx, input_num, block);
	switch (r) {
	case DELETE_WATCH:
		tal_free(txow);
		return;
	case KEEP_WATCHING:
		return;
	}
	fatal("txowatch callback %p returned %i", txow->cb, r);
}

void watch_topology_changed(struct chain_topology *topo)
{
	struct txwatch_hash_iter i;
	struct txwatch *w;
	bool needs_rerun;
	do {
		/* Iterating a htable during deletes is safe, but might skip entries. */
		needs_rerun = false;
		for (w = txwatch_hash_first(&topo->txwatches, &i);
		     w;
		     w = txwatch_hash_next(&topo->txwatches, &i)) {
			u32 depth;

			depth = get_tx_depth(topo, &w->txid);
			if (depth)
				needs_rerun |= txw_fire(w, &w->txid, depth);
		}
	} while (needs_rerun);
}