forked from ElementsProject/lightning
-
Notifications
You must be signed in to change notification settings - Fork 0
/
watch.c
247 lines (221 loc) · 5.78 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
/* 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 "bitcoin/tx.h"
#include "bitcoind.h"
#include "chaintopology.h"
#include "lightningd.h"
#include "log.h"
#include "peer.h"
#include "pseudorand.h"
#include "timeout.h"
#include "watch.h"
#include <ccan/crypto/siphash24/siphash24.h>
#include <ccan/ptrint/ptrint.h>
#include <ccan/structeq/structeq.h>
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 structeq(&w->out.txid, &out->txid)
&& w->out.index == out->index;
}
static void destroy_txowatch(struct txowatch *w)
{
txowatch_hash_del(&w->peer->dstate->txowatches, w);
}
const struct sha256_double *txwatch_keyof(const struct txwatch *w)
{
return &w->txid;
}
size_t txid_hash(const struct sha256_double *txid)
{
return siphash24(siphash_seed(), txid->sha.u.u8, sizeof(txid->sha.u.u8));
}
bool txwatch_eq(const struct txwatch *w, const struct sha256_double *txid)
{
return structeq(&w->txid, txid);
}
static void destroy_txwatch(struct txwatch *w)
{
txwatch_hash_del(&w->dstate->txwatches, w);
}
struct txwatch *watch_txid_(const tal_t *ctx,
struct peer *peer,
const struct sha256_double *txid,
enum watch_result (*cb)(struct peer *peer,
unsigned int depth,
const struct sha256_double *,
void *arg),
void *cb_arg)
{
struct txwatch *w;
w = tal(ctx, struct txwatch);
w->depth = 0;
w->txid = *txid;
w->dstate = peer->dstate;
w->peer = peer;
w->cb = cb;
w->cbdata = cb_arg;
txwatch_hash_add(&w->dstate->txwatches, w);
tal_add_destructor(w, destroy_txwatch);
return w;
}
bool watching_txid(struct lightningd_state *dstate,
const struct sha256_double *txid)
{
return txwatch_hash_get(&dstate->txwatches, txid) != NULL;
}
struct txwatch *watch_tx_(const tal_t *ctx,
struct peer *peer,
const struct bitcoin_tx *tx,
enum watch_result (*cb)(struct peer *peer,
unsigned int depth,
const struct sha256_double *,
void *arg),
void *cb_arg)
{
struct sha256_double txid;
bitcoin_txid(tx, &txid);
return watch_txid(ctx, peer, &txid, cb, cb_arg);
}
struct txowatch *watch_txo_(const tal_t *ctx,
struct peer *peer,
const struct sha256_double *txid,
unsigned int output,
enum watch_result (*cb)(struct peer *peer,
const struct bitcoin_tx *tx,
size_t input_num,
void *),
void *cbdata)
{
struct txowatch *w = tal(ctx, struct txowatch);
w->out.txid = *txid;
w->out.index = output;
w->peer = peer;
w->cb = cb;
w->cbdata = cbdata;
txowatch_hash_add(&w->peer->dstate->txowatches, w);
tal_add_destructor(w, destroy_txowatch);
return w;
}
void txwatch_fire(struct lightningd_state *dstate,
const struct sha256_double *txid,
unsigned int depth)
{
struct txwatch *txw = txwatch_hash_get(&dstate->txwatches, txid);
if (txw && depth != txw->depth) {
enum watch_result r;
log_debug(txw->peer->log,
"Got depth change %u for %02x%02x%02x...\n",
txw->depth,
txw->txid.sha.u.u8[0],
txw->txid.sha.u.u8[1],
txw->txid.sha.u.u8[2]);
txw->depth = depth;
r = txw->cb(txw->peer, txw->depth, &txw->txid, txw->cbdata);
switch (r) {
case DELETE_WATCH:
tal_free(txw);
return;
case KEEP_WATCHING:
return;
}
fatal("txwatch callback %p returned %i\n", txw->cb, r);
}
}
void txowatch_fire(struct lightningd_state *dstate,
const struct txowatch *txow,
const struct bitcoin_tx *tx,
size_t input_num)
{
struct sha256_double txid;
enum watch_result r;
bitcoin_txid(tx, &txid);
log_debug(txow->peer->log,
"Got UTXO spend for %02x%02x%02x:%u: %02x%02x%02x%02x...\n",
txow->out.txid.sha.u.u8[0],
txow->out.txid.sha.u.u8[1],
txow->out.txid.sha.u.u8[2],
txow->out.index,
txid.sha.u.u8[0],
txid.sha.u.u8[1],
txid.sha.u.u8[2],
txid.sha.u.u8[3]);
r = txow->cb(txow->peer, tx, input_num, txow->cbdata);
switch (r) {
case DELETE_WATCH:
tal_free(txow);
return;
case KEEP_WATCHING:
return;
}
fatal("txowatch callback %p returned %i\n", txow->cb, r);
}
void watch_topology_changed(struct lightningd_state *dstate)
{
struct txwatch_hash_iter i;
struct txwatch *w;
bool needs_rerun;
again:
/* Iterating a htable during deletes is safe, but might skip entries. */
needs_rerun = false;
for (w = txwatch_hash_first(&dstate->txwatches, &i);
w;
w = txwatch_hash_next(&dstate->txwatches, &i)) {
size_t depth;
depth = get_tx_depth(dstate, &w->txid);
if (depth != w->depth) {
enum watch_result r;
w->depth = depth;
needs_rerun = true;
r = w->cb(w->peer, w->depth, &w->txid, w->cbdata);
switch (r) {
case DELETE_WATCH:
tal_free(w);
continue;
case KEEP_WATCHING:
continue;
}
fatal("txwatch callback %p returned %i\n", w->cb, r);
}
}
if (needs_rerun)
goto again;
}