Skip to content

Commit 90b393c

Browse files
niftyneicdecker
authored andcommitted
hsmd/db: backfill pubkey information so that psbts signing works
the way we use PSBTs to sign things requires that we have the scriptpubkey available on the utxo so we can populate the witness-utxo field with it. this causes problems if we don't already have the scriptpubkey cached in the database, as in *some* cases we require a round trip to the HSM to populate them to get over this hump, we backfill any and all missing scriptpubkey information for the utxo's that we hold in our wallet. this will allow us to clean up the NULL handling of missing scriptpubkeys.
1 parent 65c2bac commit 90b393c

File tree

8 files changed

+217
-0
lines changed

8 files changed

+217
-0
lines changed

hsmd/hsm_wire.csv

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,13 @@ msgdata,hsm_sign_message,msg,u8,len
185185

186186
msgtype,hsm_sign_message_reply,123
187187
msgdata,hsm_sign_message_reply,sig,secp256k1_ecdsa_recoverable_signature,
188+
189+
# lightningd needs to get a scriptPubkey for a utxo with closeinfo
190+
msgtype,hsm_get_output_scriptpubkey,24
191+
msgdata,hsm_get_output_scriptpubkey,channel_id,u64,
192+
msgdata,hsm_get_output_scriptpubkey,peer_id,node_id,
193+
msgdata,hsm_get_output_scriptpubkey,commitment_point,?pubkey,
194+
195+
msgtype,hsm_get_output_scriptpubkey_reply,124
196+
msgdata,hsm_get_output_scriptpubkey_reply,script_len,u16,
197+
msgdata,hsm_get_output_scriptpubkey_reply,script,u8,script_len

hsmd/hsmd.c

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,31 @@ static struct io_plan *handle_sign_withdrawal_tx(struct io_conn *conn,
15711571
take(towire_hsm_sign_withdrawal_reply(NULL, psbt)));
15721572
}
15731573

1574+
static struct io_plan *handle_get_output_scriptpubkey(struct io_conn *conn,
1575+
struct client *c,
1576+
const u8 *msg_in)
1577+
{
1578+
struct pubkey pubkey;
1579+
struct privkey privkey;
1580+
struct unilateral_close_info info;
1581+
u8 *scriptPubkey;
1582+
1583+
info.commitment_point = NULL;
1584+
if (!fromwire_hsm_get_output_scriptpubkey(tmpctx, msg_in,
1585+
&info.channel_id,
1586+
&info.peer_id,
1587+
&info.commitment_point))
1588+
return bad_req(conn, c, msg_in);
1589+
1590+
hsm_unilateral_close_privkey(&privkey, &info);
1591+
pubkey_from_privkey(&privkey, &pubkey);
1592+
scriptPubkey = scriptpubkey_p2wpkh(tmpctx, &pubkey);
1593+
1594+
return req_reply(conn, c,
1595+
take(towire_hsm_get_output_scriptpubkey_reply(NULL,
1596+
scriptPubkey)));
1597+
}
1598+
15741599
/*~ Lightning invoices, defined by BOLT 11, are signed. This has been
15751600
* surprisingly controversial; it means a node needs to be online to create
15761601
* invoices. However, it seems clear to me that in a world without
@@ -1799,6 +1824,7 @@ static bool check_client_capabilities(struct client *client,
17991824
case WIRE_HSM_GET_CHANNEL_BASEPOINTS:
18001825
case WIRE_HSM_DEV_MEMLEAK:
18011826
case WIRE_HSM_SIGN_MESSAGE:
1827+
case WIRE_HSM_GET_OUTPUT_SCRIPTPUBKEY:
18021828
return (client->capabilities & HSM_CAP_MASTER) != 0;
18031829

18041830
/*~ These are messages sent by the HSM so we should never receive them. */
@@ -1820,6 +1846,7 @@ static bool check_client_capabilities(struct client *client,
18201846
case WIRE_HSM_GET_CHANNEL_BASEPOINTS_REPLY:
18211847
case WIRE_HSM_DEV_MEMLEAK_REPLY:
18221848
case WIRE_HSM_SIGN_MESSAGE_REPLY:
1849+
case WIRE_HSM_GET_OUTPUT_SCRIPTPUBKEY_REPLY:
18231850
break;
18241851
}
18251852
return false;
@@ -1849,6 +1876,9 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c)
18491876
case WIRE_HSM_GET_CHANNEL_BASEPOINTS:
18501877
return handle_get_channel_basepoints(conn, c, c->msg_in);
18511878

1879+
case WIRE_HSM_GET_OUTPUT_SCRIPTPUBKEY:
1880+
return handle_get_output_scriptpubkey(conn, c, c->msg_in);
1881+
18521882
case WIRE_HSM_ECDH_REQ:
18531883
return handle_ecdh(conn, c, c->msg_in);
18541884

@@ -1921,6 +1951,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c)
19211951
case WIRE_HSM_GET_CHANNEL_BASEPOINTS_REPLY:
19221952
case WIRE_HSM_DEV_MEMLEAK_REPLY:
19231953
case WIRE_HSM_SIGN_MESSAGE_REPLY:
1954+
case WIRE_HSM_GET_OUTPUT_SCRIPTPUBKEY_REPLY:
19241955
break;
19251956
}
19261957

tests/data/pubkey_regen.sqlite.xz

17.6 KB
Binary file not shown.
17.6 KB
Binary file not shown.

tests/test_db.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,76 @@ def test_last_tx_psbt_upgrade(node_factory, bitcoind):
180180
bitcoind.rpc.decoderawtransaction(last_txs[1].hex())
181181

182182

183+
@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "This test is based on a sqlite3 snapshot")
184+
@unittest.skipIf(TEST_NETWORK != 'regtest', "The network must match the DB snapshot")
185+
def test_backfill_scriptpubkeys(node_factory, bitcoind):
186+
bitcoind.generate_block(214)
187+
188+
script_map = [
189+
{
190+
"txid": "2513F3340D493489811EAB440AC05650B5BC06290358972EB6A55533A9EED96A",
191+
"scriptpubkey": "001438C10854C11E10CB3786460143C963C8530DF891",
192+
}, {
193+
"txid": "E380E18B6E810A464634B3A94B95AAA06B36A8982FD9D9D294982726EDC77DD3",
194+
"scriptpubkey": "001407DB91DA65EF06B385F4EA20BA05FAF286165C0B",
195+
}, {
196+
"txid": "E9AE7C9A346F9B9E35868176F311F3F2EE5DB8B94A065963E26954E119C49A79",
197+
"scriptpubkey": "00147E5B5C8F4FC1A9484E259F92CA4CBB7FA2814EA4",
198+
}, {
199+
"txid": "4C88F50BF00518E4FE3434ACA42351D5AC5FEEE17C35595DFBC3D1F4279F6EC1",
200+
"scriptpubkey": "0014D0EAC62FDCEE2D1881259BE9CDA4C43DE9050DB8",
201+
}, {
202+
"txid": "55265C3CAFE98C355FE0A440DCC005CF5C3145280EAD44D6B903A45D2DF3619C",
203+
"scriptpubkey": "0014D0EAC62FDCEE2D1881259BE9CDA4C43DE9050DB8",
204+
}, {
205+
"txid": "06F6D1D29B175146381EAB59924EC438572D18A3701F8E4FDF4EE17DE78D31E3",
206+
"scriptpubkey": "A9149551336F1E360F5AFB977F24CE72C744A82463D187",
207+
}, {
208+
"txid": "91BCEC7867F3F97F4F575D1D9DEDF5CF22BDDE643B36C2D9E6097048334EE32A",
209+
"scriptpubkey": "0014DFA9D65F06088E922A661C29797EE616F793C863",
210+
},
211+
]
212+
213+
# Test the first time, all entries are with option_static_remotekey
214+
l1 = node_factory.get_node(node_id=3, dbfile='pubkey_regen.sqlite.xz')
215+
results = l1.db_query('SELECT hex(prev_out_tx) AS txid, hex(scriptpubkey) AS script FROM outputs')
216+
scripts = [{'txid': x['txid'], 'scriptpubkey': x['script']} for x in results]
217+
for exp, actual in zip(script_map, scripts):
218+
assert exp == actual
219+
220+
# Test again, without option_static_remotekey
221+
script_map_2 = [
222+
{
223+
"txid": "FF89677793AC6F39E4AEB9D393B45F1E3D902CBFA26B521C5C438345A6D36E54",
224+
"scriptpubkey": "001438C10854C11E10CB3786460143C963C8530DF891",
225+
}, {
226+
"txid": "0F0685CCEE067638629B1CB27111EB0E15E19B75B1F5D368FC10D216D48FF4A5",
227+
"scriptpubkey": "001407DB91DA65EF06B385F4EA20BA05FAF286165C0B",
228+
}, {
229+
"txid": "822466946527F940A53B823C507A319FDC91CCE55E455D916C9FE13B982058FA",
230+
"scriptpubkey": "00144A94D23CD5A438531AADD86A0237FE11B9EA4E09",
231+
}, {
232+
"txid": "383145E40C8A9F45A0409E080DA5861C9E754B1EC8DD5EFA8A84DEB158E61C88",
233+
"scriptpubkey": "0014D0EAC62FDCEE2D1881259BE9CDA4C43DE9050DB8",
234+
}, {
235+
"txid": "D221BE9B7CDB5FDB58B34D59B30304B7C4C2DF9C3BF73A4AE0E0265642FEC560",
236+
"scriptpubkey": "0014D0EAC62FDCEE2D1881259BE9CDA4C43DE9050DB8",
237+
}, {
238+
"txid": "420F06E91CEE996D8E75E0565D776A96E8959ECA11E799FFE14522C2D43CCFA5",
239+
"scriptpubkey": "A9149551336F1E360F5AFB977F24CE72C744A82463D187",
240+
}, {
241+
"txid": "9F6127316EBED57E7702A4DF19D6FC0EC23A8FAB9BC0D4AD82C29D3F93C525CD",
242+
"scriptpubkey": "0014E445493A382C798AF195724DFF67DE4C9250AEC6",
243+
}
244+
]
245+
246+
l2 = node_factory.get_node(node_id=3, dbfile='pubkey_regen_commitment_point.sqlite3.xz')
247+
results = l2.db_query('SELECT hex(prev_out_tx) AS txid, hex(scriptpubkey) AS script FROM outputs')
248+
scripts = [{'txid': x['txid'], 'scriptpubkey': x['script']} for x in results]
249+
for exp, actual in zip(script_map_2, scripts):
250+
assert exp == actual
251+
252+
183253
@unittest.skipIf(VALGRIND and not DEVELOPER, "Without developer valgrind will complain about debug symbols missing")
184254
def test_optimistic_locking(node_factory, bitcoind):
185255
"""Have a node run against a DB, then change it under its feet, crashing it.

wallet/db.c

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,20 @@
55
#include <ccan/array_size/array_size.h>
66
#include <ccan/tal/str/str.h>
77
#include <common/derive_basepoints.h>
8+
#include <common/key_derive.h>
89
#include <common/node_id.h>
910
#include <common/onionreply.h>
1011
#include <common/version.h>
12+
#include <hsmd/gen_hsm_wire.h>
1113
#include <inttypes.h>
1214
#include <lightningd/channel.h>
1315
#include <lightningd/lightningd.h>
1416
#include <lightningd/log.h>
1517
#include <lightningd/plugin_hook.h>
1618
#include <wallet/db_common.h>
19+
#include <wallet/wallet.h>
1720
#include <wally_bip32.h>
21+
#include <wire/wire_sync.h>
1822

1923
#define NSEC_IN_SEC 1000000000
2024

@@ -33,6 +37,8 @@ static void migrate_our_funding(struct lightningd *ld, struct db *db,
3337
static void migrate_last_tx_to_psbt(struct lightningd *ld, struct db *db,
3438
const struct ext_key *bip32_base);
3539

40+
static void fillin_missing_scriptpubkeys(struct lightningd *ld, struct db *db,
41+
const struct ext_key *bip32_base);
3642

3743
/* Do not reorder or remove elements from this array, it is used to
3844
* migrate existing databases from a previous state, based on the
@@ -626,6 +632,7 @@ static struct migration dbmigrations[] = {
626632
{SQL("INSERT INTO vars (name, intval) VALUES ('coin_moves_count', 0);"), NULL},
627633
{NULL, migrate_last_tx_to_psbt},
628634
{SQL("ALTER TABLE outputs ADD reserved_til INTEGER DEFAULT NULL;"), NULL},
635+
{NULL, fillin_missing_scriptpubkeys},
629636
};
630637

631638
/* Leak tracking. */
@@ -1128,6 +1135,87 @@ static void migrate_our_funding(struct lightningd *ld, struct db *db,
11281135
tal_free(stmt);
11291136
}
11301137

1138+
void fillin_missing_scriptpubkeys(struct lightningd *ld, struct db *db,
1139+
const struct ext_key *bip32_base)
1140+
{
1141+
struct db_stmt *stmt;
1142+
1143+
stmt = db_prepare_v2(db, SQL("SELECT"
1144+
" type"
1145+
", keyindex"
1146+
", prev_out_tx"
1147+
", prev_out_index"
1148+
", channel_id"
1149+
", peer_id"
1150+
", commitment_point"
1151+
" FROM outputs"
1152+
" WHERE scriptpubkey IS NULL;"));
1153+
1154+
db_query_prepared(stmt);
1155+
while (db_step(stmt)) {
1156+
int type;
1157+
u8 *scriptPubkey;
1158+
struct bitcoin_txid txid;
1159+
u32 outnum, keyindex;
1160+
struct pubkey key;
1161+
struct db_stmt *update_stmt;
1162+
1163+
type = db_column_int(stmt, 0);
1164+
keyindex = db_column_int(stmt, 1);
1165+
db_column_txid(stmt, 2, &txid);
1166+
outnum = db_column_int(stmt, 3);
1167+
1168+
/* This indiciates whether or not we have 'close_info' */
1169+
if (!db_column_is_null(stmt, 4)) {
1170+
struct pubkey *commitment_point;
1171+
struct node_id peer_id;
1172+
u64 channel_id;
1173+
u8 *msg;
1174+
1175+
channel_id = db_column_u64(stmt, 4);
1176+
db_column_node_id(stmt, 5, &peer_id);
1177+
if (!db_column_is_null(stmt, 6)) {
1178+
commitment_point = tal(stmt, struct pubkey);
1179+
db_column_pubkey(stmt, 6, commitment_point);
1180+
} else
1181+
commitment_point = NULL;
1182+
1183+
/* Have to go ask the HSM to derive the pubkey for us */
1184+
msg = towire_hsm_get_output_scriptpubkey(NULL,
1185+
channel_id,
1186+
&peer_id,
1187+
commitment_point);
1188+
if (!wire_sync_write(ld->hsm_fd, take(msg)))
1189+
fatal("Could not write to HSM: %s", strerror(errno));
1190+
msg = wire_sync_read(stmt, ld->hsm_fd);
1191+
if (!fromwire_hsm_get_output_scriptpubkey_reply(stmt, msg,
1192+
&scriptPubkey))
1193+
fatal("HSM gave bad hsm_get_output_scriptpubkey_reply %s",
1194+
tal_hex(msg, msg));
1195+
} else {
1196+
/* Build from bip32_base */
1197+
bip32_pubkey(bip32_base, &key, keyindex);
1198+
if (type == p2sh_wpkh) {
1199+
u8 *redeemscript = bitcoin_redeem_p2sh_p2wpkh(stmt, &key);
1200+
scriptPubkey = scriptpubkey_p2sh(tmpctx, redeemscript);
1201+
} else
1202+
scriptPubkey = scriptpubkey_p2wpkh(stmt, &key);
1203+
}
1204+
1205+
update_stmt = db_prepare_v2(db, SQL("UPDATE outputs"
1206+
" SET scriptpubkey = ?"
1207+
" WHERE prev_out_tx = ? "
1208+
" AND prev_out_index = ?"));
1209+
db_bind_blob(update_stmt, 0, scriptPubkey, tal_bytelen(scriptPubkey));
1210+
db_bind_txid(update_stmt, 1, &txid);
1211+
db_bind_int(update_stmt, 2, outnum);
1212+
db_exec_prepared_v2(update_stmt);
1213+
tal_free(update_stmt);
1214+
}
1215+
1216+
tal_free(stmt);
1217+
}
1218+
11311219
/* We're moving everything over to PSBTs from tx's, particularly our last_tx's
11321220
* which are commitment transactions for channels.
11331221
* This migration loads all of the last_tx's and 're-formats' them into psbts,

wallet/test/run-db.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ static void db_log_(struct log *log UNUSED, enum log_level level UNUSED, const s
2121
/* Generated stub for fatal */
2222
void fatal(const char *fmt UNNEEDED, ...)
2323
{ fprintf(stderr, "fatal called!\n"); abort(); }
24+
/* Generated stub for fromwire_hsm_get_output_scriptpubkey_reply */
25+
bool fromwire_hsm_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED)
26+
{ fprintf(stderr, "fromwire_hsm_get_output_scriptpubkey_reply called!\n"); abort(); }
2427
/* Generated stub for get_channel_basepoints */
2528
void get_channel_basepoints(struct lightningd *ld UNNEEDED,
2629
const struct node_id *peer_id UNNEEDED,
@@ -33,6 +36,15 @@ struct log *new_log(const tal_t *ctx UNNEEDED, struct log_book *record UNNEEDED,
3336
const struct node_id *default_node_id UNNEEDED,
3437
const char *fmt UNNEEDED, ...)
3538
{ fprintf(stderr, "new_log called!\n"); abort(); }
39+
/* Generated stub for towire_hsm_get_output_scriptpubkey */
40+
u8 *towire_hsm_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED)
41+
{ fprintf(stderr, "towire_hsm_get_output_scriptpubkey called!\n"); abort(); }
42+
/* Generated stub for wire_sync_read */
43+
u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED)
44+
{ fprintf(stderr, "wire_sync_read called!\n"); abort(); }
45+
/* Generated stub for wire_sync_write */
46+
bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED)
47+
{ fprintf(stderr, "wire_sync_write called!\n"); abort(); }
3648
/* AUTOGENERATED MOCKS END */
3749

3850
static char *db_err;

wallet/test/run-wallet.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ bool fromwire_custommsg_in(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8
135135
/* Generated stub for fromwire_gossip_get_stripped_cupdate_reply */
136136
bool fromwire_gossip_get_stripped_cupdate_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **stripped_update UNNEEDED)
137137
{ fprintf(stderr, "fromwire_gossip_get_stripped_cupdate_reply called!\n"); abort(); }
138+
/* Generated stub for fromwire_hsm_get_output_scriptpubkey_reply */
139+
bool fromwire_hsm_get_output_scriptpubkey_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **script UNNEEDED)
140+
{ fprintf(stderr, "fromwire_hsm_get_output_scriptpubkey_reply called!\n"); abort(); }
138141
/* Generated stub for fromwire_hsm_sign_commitment_tx_reply */
139142
bool fromwire_hsm_sign_commitment_tx_reply(const void *p UNNEEDED, struct bitcoin_signature *sig UNNEEDED)
140143
{ fprintf(stderr, "fromwire_hsm_sign_commitment_tx_reply called!\n"); abort(); }
@@ -692,6 +695,9 @@ u8 *towire_final_incorrect_htlc_amount(const tal_t *ctx UNNEEDED, struct amount_
692695
/* Generated stub for towire_gossip_get_stripped_cupdate */
693696
u8 *towire_gossip_get_stripped_cupdate(const tal_t *ctx UNNEEDED, const struct short_channel_id *channel_id UNNEEDED)
694697
{ fprintf(stderr, "towire_gossip_get_stripped_cupdate called!\n"); abort(); }
698+
/* Generated stub for towire_hsm_get_output_scriptpubkey */
699+
u8 *towire_hsm_get_output_scriptpubkey(const tal_t *ctx UNNEEDED, u64 channel_id UNNEEDED, const struct node_id *peer_id UNNEEDED, const struct pubkey *commitment_point UNNEEDED)
700+
{ fprintf(stderr, "towire_hsm_get_output_scriptpubkey called!\n"); abort(); }
695701
/* Generated stub for towire_hsm_sign_commitment_tx */
696702
u8 *towire_hsm_sign_commitment_tx(const tal_t *ctx UNNEEDED, const struct node_id *peer_id UNNEEDED, u64 channel_dbid UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const struct pubkey *remote_funding_key UNNEEDED)
697703
{ fprintf(stderr, "towire_hsm_sign_commitment_tx called!\n"); abort(); }

0 commit comments

Comments
 (0)