forked from ElementsProject/lightning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
watch.c
316 lines (271 loc) · 7.46 KB
/
watch.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
/* 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 "config.h"
#include <common/type_to_string.h>
#include <lightningd/chaintopology.h>
#include <lightningd/channel.h>
#include <lightningd/lightningd.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 bitcoin_outpoint 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;
/* May be NULL if we haven't seen it yet. */
const struct bitcoin_tx *tx;
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,
const struct bitcoin_tx *tx,
unsigned int depth);
};
const struct bitcoin_outpoint *txowatch_keyof(const struct txowatch *w)
{
return &w->out;
}
size_t txo_hash(const struct bitcoin_outpoint *out)
{
/* This hash-in-one-go trick only works if they're consecutive. */
BUILD_ASSERT(offsetof(struct bitcoin_outpoint, n)
== sizeof(((struct bitcoin_outpoint *)NULL)->txid));
return siphash24(siphash_seed(), out,
sizeof(out->txid) + sizeof(out->n));
}
bool txowatch_eq(const struct txowatch *w, const struct bitcoin_outpoint *out)
{
return bitcoin_txid_eq(&w->out.txid, &out->txid)
&& w->out.n == out->n;
}
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 *,
const struct bitcoin_txid *,
const struct bitcoin_tx *,
unsigned int depth))
{
struct txwatch *w;
w = tal(ctx, struct txwatch);
w->topo = topo;
w->depth = 0;
w->txid = *txid;
w->tx = NULL;
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 *,
const struct bitcoin_tx *,
unsigned int depth))
{
struct bitcoin_txid txid;
bitcoin_txid(tx, &txid);
/* FIXME: Save populate txwatch->tx here, too! */
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_outpoint *outpoint,
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 = *outpoint;
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;
/* We assume zero depth signals a reorganization */
log_debug(log,
"Got depth change %u->%u for %s%s",
txw->depth, depth,
type_to_string(tmpctx, struct bitcoin_txid, &txw->txid),
depth ? "" : " REORG");
txw->depth = depth;
r = txw->cb(txw->topo->bitcoind->ld, txw->channel, txid, txw->tx,
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.n,
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) {
if (!w->tx)
w->tx = wallet_transaction_get(w, topo->ld->wallet,
&w->txid);
needs_rerun |= txw_fire(w, &w->txid, depth);
}
}
} while (needs_rerun);
}
void txwatch_inform(const struct chain_topology *topo,
const struct bitcoin_txid *txid,
const struct bitcoin_tx *tx_may_steal)
{
struct txwatch *txw;
txw = txwatch_hash_get(&topo->txwatches, txid);
if (txw && !txw->tx)
txw->tx = tal_steal(txw, tx_may_steal);
}