diff --git a/bitcoin/psbt.c b/bitcoin/psbt.c index df76d4e6cd34..00b88cd307ec 100644 --- a/bitcoin/psbt.c +++ b/bitcoin/psbt.c @@ -848,3 +848,27 @@ struct amount_sat psbt_compute_fee(const struct wally_psbt *psbt) return fee; } + +size_t psbt_input_weight(const struct wally_psbt *psbt, size_t in) +{ + size_t weight; + + /* txid + txout + sequence */ + weight = (32 + 4 + 4) * 4; + weight += + (psbt->inputs[in].redeem_script_len + + (varint_t) varint_size(psbt->inputs[in].redeem_script_len)) * 4; + + /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #3: + * + * The minimum witness weight for an input is 110. + */ + weight += 110; + return weight; +} + +size_t psbt_output_weight(const struct wally_psbt *psbt, size_t outnum) +{ + return (8 + psbt->tx->outputs[outnum].script_len + + varint_size(psbt->tx->outputs[outnum].script_len)) * 4; +} diff --git a/bitcoin/psbt.h b/bitcoin/psbt.h index e1fa9b4068e8..4d5351456d8a 100644 --- a/bitcoin/psbt.h +++ b/bitcoin/psbt.h @@ -272,4 +272,8 @@ struct wally_psbt *psbt_from_bytes(const tal_t *ctx, const u8 *bytes, void towire_wally_psbt(u8 **pptr, const struct wally_psbt *psbt); struct wally_psbt *fromwire_wally_psbt(const tal_t *ctx, const u8 **cursor, size_t *max); + +/* Calculate the weight of a psbt input or output */ +size_t psbt_input_weight(const struct wally_psbt *psbt, size_t in); +size_t psbt_output_weight(const struct wally_psbt *psbt, size_t outnum); #endif /* LIGHTNING_BITCOIN_PSBT_H */ diff --git a/bitcoin/script.c b/bitcoin/script.c index 2ca0e1da3b5b..a63855ac6f15 100644 --- a/bitcoin/script.c +++ b/bitcoin/script.c @@ -14,6 +14,8 @@ /* To push 0-75 bytes onto stack. */ #define OP_PUSHBYTES(val) (val) +#define max(a, b) ((a) > (b) ? (a) : (b)) + /* Bitcoin's OP_HASH160 is RIPEMD(SHA256()) */ static void hash160(struct ripemd160 *redeemhash, const void *mem, size_t len) { @@ -327,13 +329,24 @@ u8 *scriptpubkey_witness_raw(const tal_t *ctx, u8 version, * block csv lock. * OP_CHECKSIGVERIFY 1 OP_CHECKSEQUENCEVERIFY */ +/* BOLT- #3 + * ##### Leased channel (`option_will_fund`) + * + * If a `lease` applies to the channel, the `to_remote` output + * of the `initiator` ensures the `leasor` funds are not + * spendable until the lease expires. + * + * OP_CHECKSIGVERIFY MAX(1, lease_end - blockheight) OP_CHECKSEQUENCEVERIFY + */ + u8 *anchor_to_remote_redeem(const tal_t *ctx, - const struct pubkey *remote_key) + const struct pubkey *remote_key, + u32 csv_lock) { u8 *script = tal_arr(ctx, u8, 0); add_push_key(&script, remote_key); add_op(&script, OP_CHECKSIGVERIFY); - add_number(&script, 1); + add_number(&script, csv_lock); add_op(&script, OP_CHECKSEQUENCEVERIFY); assert(is_anchor_witness_script(script, tal_bytelen(script))); @@ -342,15 +355,21 @@ u8 *anchor_to_remote_redeem(const tal_t *ctx, bool is_anchor_witness_script(const u8 *script, size_t script_len) { - if (script_len != 34 + 1 + 1 + 1) + size_t len = 34 + 1 + 1 + 1; + /* With option_will_fund, the pushbytes can be up to 2 bytes more + * + * OP_CHECKSIGVERIFY + * MAX(1, lease_end - blockheight) + * OP_CHECKSEQUENCEVERIFY + */ + if (script_len < len || script_len > len + 2) return false; if (script[0] != OP_PUSHBYTES(33)) return false; if (script[34] != OP_CHECKSIGVERIFY) return false; - if (script[35] != 0x51) - return false; - if (script[36] != OP_CHECKSEQUENCEVERIFY) + /* FIXME: check for push value */ + if (script[script_len - 1] != OP_CHECKSEQUENCEVERIFY) return false; return true; } @@ -510,7 +529,27 @@ u8 **bitcoin_witness_sig_and_element(const tal_t *ctx, * OP_ENDIF * OP_CHECKSIG */ -u8 *bitcoin_wscript_to_local(const tal_t *ctx, u16 to_self_delay, +/* BOLT- #3 + * ##### Leased channel (`option_will_fund`) + * If a `lease` applies to the channel, the `to_local` output of the `accepter` + * ensures the `leasor` funds are not spendable until the lease expires. + * + * In a leased channel, the `to_local` output that pays the `accepter` node + * is modified so that its CSV is equal to the greater of the + * `to_self_delay` or the `lease_end` - `blockheight`. + * + * OP_IF + * # Penalty transaction + * + * OP_ELSE + * MAX(`to_self_delay`, `lease_end` - `blockheight`) + * OP_CHECKSEQUENCEVERIFY + * OP_DROP + * + * OP_ENDIF + * OP_CHECKSIG + */ +u8 *bitcoin_wscript_to_local(const tal_t *ctx, u16 to_self_delay, u32 csv, const struct pubkey *revocation_pubkey, const struct pubkey *local_delayedkey) { @@ -518,7 +557,7 @@ u8 *bitcoin_wscript_to_local(const tal_t *ctx, u16 to_self_delay, add_op(&script, OP_IF); add_push_key(&script, revocation_pubkey); add_op(&script, OP_ELSE); - add_number(&script, to_self_delay); + add_number(&script, max(csv, to_self_delay)); add_op(&script, OP_CHECKSEQUENCEVERIFY); add_op(&script, OP_DROP); add_push_key(&script, local_delayedkey); diff --git a/bitcoin/script.h b/bitcoin/script.h index 939ecdc9c332..3566092224c9 100644 --- a/bitcoin/script.h +++ b/bitcoin/script.h @@ -67,9 +67,10 @@ u8 *scriptpubkey_p2wpkh_derkey(const tal_t *ctx, const u8 der[33]); u8 *scriptpubkey_witness_raw(const tal_t *ctx, u8 version, const u8 *wprog, size_t wprog_size); -/* To-remotekey with csv 1 delay. */ +/* To-remotekey with csv max(lease_expiry - blockheight, 1) delay. */ u8 *anchor_to_remote_redeem(const tal_t *ctx, - const struct pubkey *remote_key); + const struct pubkey *remote_key, + u32 csv_lock); /* Create a witness which spends the 2of2. */ u8 **bitcoin_witness_2of2(const tal_t *ctx, @@ -90,8 +91,7 @@ u8 **bitcoin_witness_sig_and_element(const tal_t *ctx, const u8 *witnessscript); /* BOLT #3 to-local output */ -u8 *bitcoin_wscript_to_local(const tal_t *ctx, - u16 to_self_delay, +u8 *bitcoin_wscript_to_local(const tal_t *ctx, u16 to_self_delay, u32 csv, const struct pubkey *revocation_pubkey, const struct pubkey *local_delayedkey); diff --git a/channeld/Makefile b/channeld/Makefile index c3b9b16114c2..8fb4cc12ff87 100644 --- a/channeld/Makefile +++ b/channeld/Makefile @@ -35,6 +35,7 @@ CHANNELD_COMMON_OBJS := \ common/billboard.o \ common/bip32.o \ common/blinding.o \ + common/blockheight_states.o \ common/channel_config.o \ common/channel_id.o \ common/crypto_state.o \ diff --git a/channeld/channeld.c b/channeld/channeld.c index 305de7e9eb34..0b9bb2863ba6 100644 --- a/channeld/channeld.c +++ b/channeld/channeld.c @@ -134,6 +134,9 @@ struct peer { /* The feerate we want. */ u32 desired_feerate; + /* Current blockheight */ + u32 our_blockheight; + /* Announcement related information */ struct node_id node_ids[NUM_SIDES]; struct short_channel_id short_channel_ids[NUM_SIDES]; @@ -837,6 +840,75 @@ static void handle_peer_feechange(struct peer *peer, const u8 *msg) status_debug("peer updated fee to %u", feerate); } +static void handle_peer_blockheight_change(struct peer *peer, const u8 *msg) +{ + struct channel_id channel_id; + u32 blockheight, current; + + if (!fromwire_update_blockheight(msg, &channel_id, &blockheight)) + peer_failed_warn(peer->pps, &peer->channel_id, + "Bad update_blockheight %s", + tal_hex(msg, msg)); + + /* BOLT- #2: + * A receiving node: + * ... + * - if the sender is not the initiator: + * - MUST fail the channel. + */ + if (peer->channel->opener != REMOTE) + peer_failed_warn(peer->pps, &peer->channel_id, + "update_blockheight from non-opener?"); + + current = get_blockheight(peer->channel->blockheight_states, + peer->channel->opener, LOCAL); + + status_debug("update_blockheight %u. last update height %u," + " our current height %u", + blockheight, current, peer->our_blockheight); + + /* BOLT- #2: + * A receiving node: + * - if the `update_blockheight` is less than the last + * received `blockheight`: + * - SHOULD fail the channel. + * ... + * - if `blockheight` is more than 1008 blocks behind + * the current blockheight: + * - SHOULD fail the channel + */ + /* Overflow check */ + if (blockheight + 1008 < blockheight) + peer_failed_warn(peer->pps, &peer->channel_id, + "blockheight + 1008 overflow (%u)", + blockheight); + + /* If they're behind the last one they sent, we just warn and + * reconnect, as they might be catching up */ + /* FIXME: track for how long they send backwards blockheight? */ + if (blockheight < current) + peer_failed_warn(peer->pps, &peer->channel_id, + "update_blockheight %u older than previous %u", + blockheight, current); + + /* BOLT- #2: + * A receiving node: + * ... + * - if `blockheight` is more than 1008 blocks behind + * the current blockheight: + * - SHOULD fail the channel + */ + assert(blockheight < blockheight + 1008); + if (blockheight + 1008 < peer->our_blockheight) + peer_failed_err(peer->pps, &peer->channel_id, + "update_blockheight %u outside" + " permissible range", blockheight); + + channel_update_blockheight(peer->channel, blockheight); + + status_debug("peer updated blockheight to %u", blockheight); +} + static struct changed_htlc *changed_htlc_arr(const tal_t *ctx, const struct htlc **changed_htlcs) { @@ -855,6 +927,7 @@ static u8 *sending_commitsig_msg(const tal_t *ctx, u64 remote_commit_index, struct penalty_base *pbase, const struct fee_states *fee_states, + const struct height_states *blockheight_states, const struct htlc **changed_htlcs, const struct bitcoin_signature *commit_sig, const struct bitcoin_signature *htlc_sigs) @@ -865,8 +938,10 @@ static u8 *sending_commitsig_msg(const tal_t *ctx, /* We tell master what (of our) HTLCs peer will now be * committed to. */ changed = changed_htlc_arr(tmpctx, changed_htlcs); - msg = towire_channeld_sending_commitsig(ctx, remote_commit_index, pbase, fee_states, changed, - commit_sig, htlc_sigs); + msg = towire_channeld_sending_commitsig(ctx, remote_commit_index, + pbase, fee_states, + blockheight_states, changed, + commit_sig, htlc_sigs); return msg; } @@ -1122,6 +1197,41 @@ static bool want_fee_update(const struct peer *peer, u32 *target) return val != channel_feerate(peer->channel, REMOTE); } +/* Do we want to update blockheight? */ +static bool want_blockheight_update(const struct peer *peer, u32 *height) +{ + u32 last; + + if (peer->channel->opener != LOCAL) + return false; + + if (peer->channel->lease_expiry == 0) + return false; + +#if EXPERIMENTAL_FEATURES + /* No fee update while quiescing! */ + if (peer->stfu) + return false; +#endif + /* What's the current blockheight */ + last = get_blockheight(peer->channel->blockheight_states, + peer->channel->opener, LOCAL); + + if (peer->our_blockheight < last) { + status_broken("current blockheight %u less than last %u", + peer->our_blockheight, last); + return false; + } + + if (peer->our_blockheight == last) + return false; + + if (height) + *height = peer->our_blockheight; + + return true; +} + static void send_commit(struct peer *peer) { u8 *msg; @@ -1132,6 +1242,7 @@ static void send_commit(struct peer *peer) const struct htlc **htlc_map; struct wally_tx_output *direct_outputs[NUM_SIDES]; struct penalty_base *pbase; + u32 our_blockheight; u32 feerate_target; #if DEVELOPER @@ -1200,6 +1311,22 @@ static void send_commit(struct peer *peer) } } + if (want_blockheight_update(peer, &our_blockheight)) { + if (blockheight_changes_done(peer->channel->blockheight_states, + false)) { + u8 *msg; + + channel_update_blockheight(peer->channel, + our_blockheight); + + msg = towire_update_blockheight(NULL, + &peer->channel_id, + our_blockheight); + + sync_crypto_write(peer->pps, take(msg)); + } + } + /* BOLT #2: * * A sending node: @@ -1208,9 +1335,13 @@ static void send_commit(struct peer *peer) */ changed_htlcs = tal_arr(tmpctx, const struct htlc *, 0); if (!channel_sending_commit(peer->channel, &changed_htlcs)) { - status_debug("Can't send commit: nothing to send, feechange %s (%s)", + status_debug("Can't send commit: nothing to send," + " feechange %s (%s)" + " blockheight %s (%s)", want_fee_update(peer, NULL) ? "wanted": "not wanted", - type_to_string(tmpctx, struct fee_states, peer->channel->fee_states)); + type_to_string(tmpctx, struct fee_states, peer->channel->fee_states), + want_blockheight_update(peer, NULL) ? "wanted" : "not wanted", + type_to_string(tmpctx, struct height_states, peer->channel->blockheight_states)); /* Covers the case where we've just been told to shutdown. */ maybe_send_shutdown(peer); @@ -1242,6 +1373,7 @@ static void send_commit(struct peer *peer) msg = sending_commitsig_msg(NULL, peer->next_index[REMOTE], pbase, peer->channel->fee_states, + peer->channel->blockheight_states, changed_htlcs, &commit_sig, htlc_sigs); @@ -1416,6 +1548,7 @@ static void send_revocation(struct peer *peer, = towire_channeld_got_commitsig(NULL, peer->next_index[LOCAL] - 1, peer->channel->fee_states, + peer->channel->blockheight_states, commit_sig, htlc_sigs, added, fulfilled, @@ -1594,7 +1727,8 @@ static u8 *got_revoke_msg(struct peer *peer, u64 revoke_num, const struct secret *per_commitment_secret, const struct pubkey *next_per_commit_point, const struct htlc **changed_htlcs, - const struct fee_states *fee_states) + const struct fee_states *fee_states, + const struct height_states *blockheight_states) { u8 *msg; struct penalty_base *pbase; @@ -1626,7 +1760,8 @@ static u8 *got_revoke_msg(struct peer *peer, u64 revoke_num, msg = towire_channeld_got_revoke(peer, revoke_num, per_commitment_secret, next_per_commit_point, fee_states, - changed, pbase, ptx); + blockheight_states, changed, + pbase, ptx); tal_free(ptx); return msg; } @@ -1683,7 +1818,8 @@ static void handle_peer_revoke_and_ack(struct peer *peer, const u8 *msg) msg = got_revoke_msg(peer, peer->revocations_received++, &old_commit_secret, &next_per_commit, changed_htlcs, - peer->channel->fee_states); + peer->channel->fee_states, + peer->channel->blockheight_states); master_wait_sync_reply(tmpctx, peer, take(msg), WIRE_CHANNELD_GOT_REVOKE_REPLY); @@ -1843,6 +1979,9 @@ static void handle_peer_shutdown(struct peer *peer, const u8 *shutdown) peer_failed_warn(peer->pps, &peer->channel_id, "Bad shutdown %s", tal_hex(peer, shutdown)); + /* FIXME: We shouldn't let them initiate a shutdown while the + * channel is active (if we leased funds) */ + /* BOLT #2: * * - if both nodes advertised the `option_upfront_shutdown_script` @@ -2084,6 +2223,9 @@ static void peer_in(struct peer *peer, const u8 *msg) case WIRE_UPDATE_FEE: handle_peer_feechange(peer, msg); return; + case WIRE_UPDATE_BLOCKHEIGHT: + handle_peer_blockheight_change(peer, msg); + return; case WIRE_REVOKE_AND_ACK: handle_peer_revoke_and_ack(peer, msg); return; @@ -2208,9 +2350,12 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) const struct htlc **htlc_map; struct wally_tx_output *direct_outputs[NUM_SIDES]; - status_debug("Retransmitting commitment, feerate LOCAL=%u REMOTE=%u", + status_debug("Retransmitting commitment, feerate LOCAL=%u REMOTE=%u," + " blockheight LOCAL=%u REMOTE=%u", channel_feerate(peer->channel, LOCAL), - channel_feerate(peer->channel, REMOTE)); + channel_feerate(peer->channel, REMOTE), + channel_blockheight(peer->channel, LOCAL), + channel_blockheight(peer->channel, REMOTE)); /* Note that HTLCs must be *added* in order. Simplest thing to do * is to sort them all into ascending ID order here (we could do @@ -2286,11 +2431,15 @@ static void resend_commitment(struct peer *peer, struct changed_htlc *last) } } - /* Make sure they have the correct fee. */ + /* Make sure they have the correct fee and blockheight. */ if (peer->channel->opener == LOCAL) { msg = towire_update_fee(NULL, &peer->channel_id, channel_feerate(peer->channel, REMOTE)); sync_crypto_write(peer->pps, take(msg)); + + msg = towire_update_blockheight(NULL, &peer->channel_id, + channel_blockheight(peer->channel, REMOTE)); + sync_crypto_write(peer->pps, take(msg)); } /* Re-send the commitment_signed itself. */ @@ -3124,6 +3273,49 @@ static void handle_feerates(struct peer *peer, const u8 *inmsg) } } +static void handle_blockheight(struct peer *peer, const u8 *inmsg) +{ + u32 blockheight; + + if (!fromwire_channeld_blockheight(inmsg, &blockheight)) + master_badmsg(WIRE_CHANNELD_BLOCKHEIGHT, inmsg); + + /* Save it, so we know */ + peer->our_blockheight = blockheight; + if (peer->channel->opener == LOCAL) + start_commit_timer(peer); + else { + u32 peer_height = get_blockheight(peer->channel->blockheight_states, + peer->channel->opener, + REMOTE); + /* BOLT- #2: + * The node _not responsible_ for initiating the channel: + * ... + * - if last received `blockheight` is > 1008 behind + * currently known blockheight: + * - SHOULD fail he channel + */ + assert(peer_height + 1008 > peer_height); + if (peer_height + 1008 < blockheight) + peer_failed_err(peer->pps, &peer->channel_id, + "Peer is too far behind, terminating" + " leased channel. Our current" + " %u, theirs %u", + blockheight, peer_height); + /* We're behind them... what do. It's possible they're lying, + * but if we're in a lease this is actually in our favor so + * we log it but otherwise continue on unchanged */ + if (peer_height > blockheight + && peer_height > blockheight + 100) + status_unusual("Peer reporting we've fallen %u" + " blocks behind. Our height %u," + " their height %u", + peer_height - blockheight, + blockheight, peer_height); + + } +} + static void handle_specific_feerates(struct peer *peer, const u8 *inmsg) { u32 base_old = peer->fee_base; @@ -3302,6 +3494,11 @@ static void req_in(struct peer *peer, const u8 *msg) return; handle_feerates(peer, msg); return; + case WIRE_CHANNELD_BLOCKHEIGHT: + if (handle_master_request_later(peer, msg)) + return; + handle_blockheight(peer, msg); + return; case WIRE_CHANNELD_FULFILL_HTLC: if (handle_master_request_later(peer, msg)) return; @@ -3394,7 +3591,8 @@ static void init_channel(struct peer *peer) u8 *fwd_msg; const u8 *msg; struct fee_states *fee_states; - u32 minimum_depth; + struct height_states *blockheight_states; + u32 minimum_depth, lease_expiry; struct secret last_remote_per_commit_secret; secp256k1_ecdsa_signature *remote_ann_node_sig; secp256k1_ecdsa_signature *remote_ann_bitcoin_sig; @@ -3414,6 +3612,9 @@ static void init_channel(struct peer *peer) &funding_txid, &funding_txout, &funding, &minimum_depth, + &peer->our_blockheight, + &blockheight_states, + &lease_expiry, &conf[LOCAL], &conf[REMOTE], &fee_states, &peer->feerate_min, @@ -3483,7 +3684,8 @@ static void init_channel(struct peer *peer) " next_idx_local = %"PRIu64 " next_idx_remote = %"PRIu64 " revocations_received = %"PRIu64 - " feerates %s range %u-%u", + " feerates %s range %u-%u" + " blockheights %s, our current %u", side_to_str(opener), type_to_string(tmpctx, struct pubkey, &peer->remote_per_commit), @@ -3492,7 +3694,9 @@ static void init_channel(struct peer *peer) peer->next_index[LOCAL], peer->next_index[REMOTE], peer->revocations_received, type_to_string(tmpctx, struct fee_states, fee_states), - peer->feerate_min, peer->feerate_max); + peer->feerate_min, peer->feerate_max, + type_to_string(tmpctx, struct height_states, blockheight_states), + peer->our_blockheight); status_debug("option_static_remotekey = %u", option_static_remotekey); @@ -3522,6 +3726,8 @@ static void init_channel(struct peer *peer) &funding_txid, funding_txout, minimum_depth, + take(blockheight_states), + lease_expiry, funding, local_msat, take(fee_states), diff --git a/channeld/channeld_wire.csv b/channeld/channeld_wire.csv index 8af0756a34a1..2df499fe50e6 100644 --- a/channeld/channeld_wire.csv +++ b/channeld/channeld_wire.csv @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -17,6 +18,9 @@ msgdata,channeld_init,funding_txid,bitcoin_txid, msgdata,channeld_init,funding_txout,u16, msgdata,channeld_init,funding_satoshi,amount_sat, msgdata,channeld_init,minimum_depth,u32, +msgdata,channeld_init,our_blockheight,u32, +msgdata,channeld_init,blockheight_states,height_states, +msgdata,channeld_init,lease_expiry,u32, msgdata,channeld_init,our_config,channel_config, msgdata,channeld_init,their_config,channel_config, msgdata,channeld_init,fee_states,fee_states, @@ -115,6 +119,7 @@ msgtype,channeld_sending_commitsig,1020 msgdata,channeld_sending_commitsig,commitnum,u64, msgdata,channeld_sending_commitsig,pbase,?penalty_base, msgdata,channeld_sending_commitsig,fee_states,fee_states, +msgdata,channeld_sending_commitsig,blockheight_states,height_states, # SENT_ADD_COMMIT, SENT_REMOVE_ACK_COMMIT, SENT_ADD_ACK_COMMIT, SENT_REMOVE_COMMIT msgdata,channeld_sending_commitsig,num_changed,u16, msgdata,channeld_sending_commitsig,changed,changed_htlc,num_changed @@ -129,6 +134,7 @@ msgtype,channeld_sending_commitsig_reply,1120 msgtype,channeld_got_commitsig,1021 msgdata,channeld_got_commitsig,commitnum,u64, msgdata,channeld_got_commitsig,fee_states,fee_states, +msgdata,channeld_got_commitsig,blockheight_states,height_states, msgdata,channeld_got_commitsig,signature,bitcoin_signature, msgdata,channeld_got_commitsig,num_htlcs,u16, msgdata,channeld_got_commitsig,htlc_signature,bitcoin_signature,num_htlcs @@ -156,6 +162,7 @@ msgdata,channeld_got_revoke,per_commitment_secret,secret, msgdata,channeld_got_revoke,next_per_commit_point,pubkey, # RCVD_ADD_ACK_REVOCATION, RCVD_REMOVE_ACK_REVOCATION, RCVD_ADD_REVOCATION, RCVD_REMOVE_REVOCATION msgdata,channeld_got_revoke,fee_states,fee_states, +msgdata,channeld_got_revoke,blockheight_states,height_states, msgdata,channeld_got_revoke,num_changed,u16, msgdata,channeld_got_revoke,changed,changed_htlc,num_changed msgdata,channeld_got_revoke,pbase,?penalty_base, @@ -225,3 +232,7 @@ msgtype,channeld_dev_quiesce_reply,1109 # Tell master we're upgrading the commitment tx. msgtype,channeld_upgraded,1011 msgdata,channeld_upgraded,option_static_remotekey,bool, + +# Tell peer about our latest and greatest blockheight. +msgtype,channeld_blockheight,1012 +msgdata,channeld_blockheight,blockheight,u32, diff --git a/channeld/channeld_wiregen.c b/channeld/channeld_wiregen.c index 0a4d375bc327..6e8a61adc8f3 100644 --- a/channeld/channeld_wiregen.c +++ b/channeld/channeld_wiregen.c @@ -49,6 +49,7 @@ const char *channeld_wire_name(int e) case WIRE_CHANNELD_DEV_QUIESCE: return "WIRE_CHANNELD_DEV_QUIESCE"; case WIRE_CHANNELD_DEV_QUIESCE_REPLY: return "WIRE_CHANNELD_DEV_QUIESCE_REPLY"; case WIRE_CHANNELD_UPGRADED: return "WIRE_CHANNELD_UPGRADED"; + case WIRE_CHANNELD_BLOCKHEIGHT: return "WIRE_CHANNELD_BLOCKHEIGHT"; } snprintf(invalidbuf, sizeof(invalidbuf), "INVALID %i", e); @@ -87,6 +88,7 @@ bool channeld_wire_is_defined(u16 type) case WIRE_CHANNELD_DEV_QUIESCE:; case WIRE_CHANNELD_DEV_QUIESCE_REPLY:; case WIRE_CHANNELD_UPGRADED:; + case WIRE_CHANNELD_BLOCKHEIGHT:; return true; } return false; @@ -98,7 +100,7 @@ bool channeld_wire_is_defined(u16 type) /* WIRE: CHANNELD_INIT */ /* Begin! (passes gossipd-client fd) */ -u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, u32 minimum_depth, const struct channel_config *our_config, const struct channel_config *their_config, const struct fee_states *fee_states, u32 feerate_min, u32 feerate_max, u32 feerate_penalty, const struct bitcoin_signature *first_commit_sig, const struct per_peer_state *per_peer_state, const struct pubkey *remote_fundingkey, const struct basepoints *remote_basepoints, const struct pubkey *remote_per_commit, const struct pubkey *old_remote_per_commit, enum side opener, u32 fee_base, u32 fee_proportional, struct amount_msat local_msatoshi, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct node_id *local_node_id, const struct node_id *remote_node_id, u32 commit_msec, u16 cltv_delta, bool last_was_revoke, const struct changed_htlc *last_sent_commit, u64 next_index_local, u64 next_index_remote, u64 revocations_received, u64 next_htlc_id, const struct existing_htlc **htlcs, bool local_funding_locked, bool remote_funding_locked, const struct short_channel_id *funding_short_id, bool reestablish, bool send_shutdown, bool remote_shutdown_received, const u8 *final_scriptpubkey, u8 flags, const u8 *init_peer_pkt, bool reached_announce_depth, const struct secret *last_remote_secret, const u8 *their_features, const u8 *upfront_shutdown_script, const secp256k1_ecdsa_signature *remote_ann_node_sig, const secp256k1_ecdsa_signature *remote_ann_bitcoin_sig, bool option_static_remotekey, bool option_anchor_outputs, bool dev_fast_gossip, bool dev_fail_process_onionpacket, const struct penalty_base *pbases) +u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, u32 minimum_depth, u32 our_blockheight, const struct height_states *blockheight_states, u32 lease_expiry, const struct channel_config *our_config, const struct channel_config *their_config, const struct fee_states *fee_states, u32 feerate_min, u32 feerate_max, u32 feerate_penalty, const struct bitcoin_signature *first_commit_sig, const struct per_peer_state *per_peer_state, const struct pubkey *remote_fundingkey, const struct basepoints *remote_basepoints, const struct pubkey *remote_per_commit, const struct pubkey *old_remote_per_commit, enum side opener, u32 fee_base, u32 fee_proportional, struct amount_msat local_msatoshi, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct node_id *local_node_id, const struct node_id *remote_node_id, u32 commit_msec, u16 cltv_delta, bool last_was_revoke, const struct changed_htlc *last_sent_commit, u64 next_index_local, u64 next_index_remote, u64 revocations_received, u64 next_htlc_id, const struct existing_htlc **htlcs, bool local_funding_locked, bool remote_funding_locked, const struct short_channel_id *funding_short_id, bool reestablish, bool send_shutdown, bool remote_shutdown_received, const u8 *final_scriptpubkey, u8 flags, const u8 *init_peer_pkt, bool reached_announce_depth, const struct secret *last_remote_secret, const u8 *their_features, const u8 *upfront_shutdown_script, const secp256k1_ecdsa_signature *remote_ann_node_sig, const secp256k1_ecdsa_signature *remote_ann_bitcoin_sig, bool option_static_remotekey, bool option_anchor_outputs, bool dev_fast_gossip, bool dev_fail_process_onionpacket, const struct penalty_base *pbases) { u16 num_last_sent_commit = tal_count(last_sent_commit); u16 num_existing_htlcs = tal_count(htlcs); @@ -117,6 +119,9 @@ u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams towire_u16(&p, funding_txout); towire_amount_sat(&p, funding_satoshi); towire_u32(&p, minimum_depth); + towire_u32(&p, our_blockheight); + towire_height_states(&p, blockheight_states); + towire_u32(&p, lease_expiry); towire_channel_config(&p, our_config); towire_channel_config(&p, their_config); towire_fee_states(&p, fee_states); @@ -189,7 +194,7 @@ u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams return memcheck(p, tal_count(p)); } -bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, u32 *minimum_depth, struct channel_config *our_config, struct channel_config *their_config, struct fee_states **fee_states, u32 *feerate_min, u32 *feerate_max, u32 *feerate_penalty, struct bitcoin_signature *first_commit_sig, struct per_peer_state **per_peer_state, struct pubkey *remote_fundingkey, struct basepoints *remote_basepoints, struct pubkey *remote_per_commit, struct pubkey *old_remote_per_commit, enum side *opener, u32 *fee_base, u32 *fee_proportional, struct amount_msat *local_msatoshi, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct node_id *local_node_id, struct node_id *remote_node_id, u32 *commit_msec, u16 *cltv_delta, bool *last_was_revoke, struct changed_htlc **last_sent_commit, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u64 *next_htlc_id, struct existing_htlc ***htlcs, bool *local_funding_locked, bool *remote_funding_locked, struct short_channel_id *funding_short_id, bool *reestablish, bool *send_shutdown, bool *remote_shutdown_received, u8 **final_scriptpubkey, u8 *flags, u8 **init_peer_pkt, bool *reached_announce_depth, struct secret *last_remote_secret, u8 **their_features, u8 **upfront_shutdown_script, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig, bool *option_static_remotekey, bool *option_anchor_outputs, bool *dev_fast_gossip, bool *dev_fail_process_onionpacket, struct penalty_base **pbases) +bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, u32 *minimum_depth, u32 *our_blockheight, struct height_states **blockheight_states, u32 *lease_expiry, struct channel_config *our_config, struct channel_config *their_config, struct fee_states **fee_states, u32 *feerate_min, u32 *feerate_max, u32 *feerate_penalty, struct bitcoin_signature *first_commit_sig, struct per_peer_state **per_peer_state, struct pubkey *remote_fundingkey, struct basepoints *remote_basepoints, struct pubkey *remote_per_commit, struct pubkey *old_remote_per_commit, enum side *opener, u32 *fee_base, u32 *fee_proportional, struct amount_msat *local_msatoshi, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct node_id *local_node_id, struct node_id *remote_node_id, u32 *commit_msec, u16 *cltv_delta, bool *last_was_revoke, struct changed_htlc **last_sent_commit, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u64 *next_htlc_id, struct existing_htlc ***htlcs, bool *local_funding_locked, bool *remote_funding_locked, struct short_channel_id *funding_short_id, bool *reestablish, bool *send_shutdown, bool *remote_shutdown_received, u8 **final_scriptpubkey, u8 *flags, u8 **init_peer_pkt, bool *reached_announce_depth, struct secret *last_remote_secret, u8 **their_features, u8 **upfront_shutdown_script, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig, bool *option_static_remotekey, bool *option_anchor_outputs, bool *dev_fast_gossip, bool *dev_fail_process_onionpacket, struct penalty_base **pbases) { u16 num_last_sent_commit; u16 num_existing_htlcs; @@ -211,6 +216,9 @@ bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainp *funding_txout = fromwire_u16(&cursor, &plen); *funding_satoshi = fromwire_amount_sat(&cursor, &plen); *minimum_depth = fromwire_u32(&cursor, &plen); + *our_blockheight = fromwire_u32(&cursor, &plen); + *blockheight_states = fromwire_height_states(ctx, &cursor, &plen); + *lease_expiry = fromwire_u32(&cursor, &plen); fromwire_channel_config(&cursor, &plen, our_config); fromwire_channel_config(&cursor, &plen, their_config); *fee_states = fromwire_fee_states(ctx, &cursor, &plen); @@ -474,7 +482,7 @@ bool fromwire_channeld_got_funding_locked(const void *p, struct pubkey *next_per /* WIRE: CHANNELD_SENDING_COMMITSIG */ /* When we send a commitment_signed message */ -u8 *towire_channeld_sending_commitsig(const tal_t *ctx, u64 commitnum, const struct penalty_base *pbase, const struct fee_states *fee_states, const struct changed_htlc *changed, const struct bitcoin_signature *commit_sig, const struct bitcoin_signature *htlc_sigs) +u8 *towire_channeld_sending_commitsig(const tal_t *ctx, u64 commitnum, const struct penalty_base *pbase, const struct fee_states *fee_states, const struct height_states *blockheight_states, const struct changed_htlc *changed, const struct bitcoin_signature *commit_sig, const struct bitcoin_signature *htlc_sigs) { u16 num_changed = tal_count(changed); u16 num_htlc_sigs = tal_count(htlc_sigs); @@ -489,6 +497,7 @@ u8 *towire_channeld_sending_commitsig(const tal_t *ctx, u64 commitnum, const str towire_penalty_base(&p, pbase); } towire_fee_states(&p, fee_states); + towire_height_states(&p, blockheight_states); /* SENT_ADD_COMMIT */ towire_u16(&p, num_changed); for (size_t i = 0; i < num_changed; i++) @@ -500,7 +509,7 @@ u8 *towire_channeld_sending_commitsig(const tal_t *ctx, u64 commitnum, const str return memcheck(p, tal_count(p)); } -bool fromwire_channeld_sending_commitsig(const tal_t *ctx, const void *p, u64 *commitnum, struct penalty_base **pbase, struct fee_states **fee_states, struct changed_htlc **changed, struct bitcoin_signature *commit_sig, struct bitcoin_signature **htlc_sigs) +bool fromwire_channeld_sending_commitsig(const tal_t *ctx, const void *p, u64 *commitnum, struct penalty_base **pbase, struct fee_states **fee_states, struct height_states **blockheight_states, struct changed_htlc **changed, struct bitcoin_signature *commit_sig, struct bitcoin_signature **htlc_sigs) { u16 num_changed; u16 num_htlc_sigs; @@ -518,6 +527,7 @@ bool fromwire_channeld_sending_commitsig(const tal_t *ctx, const void *p, u64 *c fromwire_penalty_base(&cursor, &plen, *pbase); } *fee_states = fromwire_fee_states(ctx, &cursor, &plen); + *blockheight_states = fromwire_height_states(ctx, &cursor, &plen); /* SENT_ADD_COMMIT */ num_changed = fromwire_u16(&cursor, &plen); // 2nd case changed @@ -555,7 +565,7 @@ bool fromwire_channeld_sending_commitsig_reply(const void *p) /* WIRE: CHANNELD_GOT_COMMITSIG */ /* When we have a commitment_signed message */ -u8 *towire_channeld_got_commitsig(const tal_t *ctx, u64 commitnum, const struct fee_states *fee_states, const struct bitcoin_signature *signature, const struct bitcoin_signature *htlc_signature, const struct added_htlc *added, const struct fulfilled_htlc *fulfilled, const struct failed_htlc **failed, const struct changed_htlc *changed, const struct bitcoin_tx *tx) +u8 *towire_channeld_got_commitsig(const tal_t *ctx, u64 commitnum, const struct fee_states *fee_states, const struct height_states *blockheight_states, const struct bitcoin_signature *signature, const struct bitcoin_signature *htlc_signature, const struct added_htlc *added, const struct fulfilled_htlc *fulfilled, const struct failed_htlc **failed, const struct changed_htlc *changed, const struct bitcoin_tx *tx) { u16 num_htlcs = tal_count(htlc_signature); u16 num_added = tal_count(added); @@ -567,6 +577,7 @@ u8 *towire_channeld_got_commitsig(const tal_t *ctx, u64 commitnum, const struct towire_u16(&p, WIRE_CHANNELD_GOT_COMMITSIG); towire_u64(&p, commitnum); towire_fee_states(&p, fee_states); + towire_height_states(&p, blockheight_states); towire_bitcoin_signature(&p, signature); towire_u16(&p, num_htlcs); for (size_t i = 0; i < num_htlcs; i++) @@ -590,7 +601,7 @@ u8 *towire_channeld_got_commitsig(const tal_t *ctx, u64 commitnum, const struct return memcheck(p, tal_count(p)); } -bool fromwire_channeld_got_commitsig(const tal_t *ctx, const void *p, u64 *commitnum, struct fee_states **fee_states, struct bitcoin_signature *signature, struct bitcoin_signature **htlc_signature, struct added_htlc **added, struct fulfilled_htlc **fulfilled, struct failed_htlc ***failed, struct changed_htlc **changed, struct bitcoin_tx **tx) +bool fromwire_channeld_got_commitsig(const tal_t *ctx, const void *p, u64 *commitnum, struct fee_states **fee_states, struct height_states **blockheight_states, struct bitcoin_signature *signature, struct bitcoin_signature **htlc_signature, struct added_htlc **added, struct fulfilled_htlc **fulfilled, struct failed_htlc ***failed, struct changed_htlc **changed, struct bitcoin_tx **tx) { u16 num_htlcs; u16 num_added; @@ -605,6 +616,7 @@ bool fromwire_channeld_got_commitsig(const tal_t *ctx, const void *p, u64 *commi return false; *commitnum = fromwire_u64(&cursor, &plen); *fee_states = fromwire_fee_states(ctx, &cursor, &plen); + *blockheight_states = fromwire_height_states(ctx, &cursor, &plen); fromwire_bitcoin_signature(&cursor, &plen, signature); num_htlcs = fromwire_u16(&cursor, &plen); // 2nd case htlc_signature @@ -659,7 +671,7 @@ bool fromwire_channeld_got_commitsig_reply(const void *p) } /* WIRE: CHANNELD_GOT_REVOKE */ -u8 *towire_channeld_got_revoke(const tal_t *ctx, u64 revokenum, const struct secret *per_commitment_secret, const struct pubkey *next_per_commit_point, const struct fee_states *fee_states, const struct changed_htlc *changed, const struct penalty_base *pbase, const struct bitcoin_tx *penalty_tx) +u8 *towire_channeld_got_revoke(const tal_t *ctx, u64 revokenum, const struct secret *per_commitment_secret, const struct pubkey *next_per_commit_point, const struct fee_states *fee_states, const struct height_states *blockheight_states, const struct changed_htlc *changed, const struct penalty_base *pbase, const struct bitcoin_tx *penalty_tx) { u16 num_changed = tal_count(changed); u8 *p = tal_arr(ctx, u8, 0); @@ -670,6 +682,7 @@ u8 *towire_channeld_got_revoke(const tal_t *ctx, u64 revokenum, const struct sec towire_pubkey(&p, next_per_commit_point); /* RCVD_ADD_ACK_REVOCATION */ towire_fee_states(&p, fee_states); + towire_height_states(&p, blockheight_states); towire_u16(&p, num_changed); for (size_t i = 0; i < num_changed; i++) towire_changed_htlc(&p, changed + i); @@ -688,7 +701,7 @@ u8 *towire_channeld_got_revoke(const tal_t *ctx, u64 revokenum, const struct sec return memcheck(p, tal_count(p)); } -bool fromwire_channeld_got_revoke(const tal_t *ctx, const void *p, u64 *revokenum, struct secret *per_commitment_secret, struct pubkey *next_per_commit_point, struct fee_states **fee_states, struct changed_htlc **changed, struct penalty_base **pbase, struct bitcoin_tx **penalty_tx) +bool fromwire_channeld_got_revoke(const tal_t *ctx, const void *p, u64 *revokenum, struct secret *per_commitment_secret, struct pubkey *next_per_commit_point, struct fee_states **fee_states, struct height_states **blockheight_states, struct changed_htlc **changed, struct penalty_base **pbase, struct bitcoin_tx **penalty_tx) { u16 num_changed; @@ -702,6 +715,7 @@ bool fromwire_channeld_got_revoke(const tal_t *ctx, const void *p, u64 *revokenu fromwire_pubkey(&cursor, &plen, next_per_commit_point); /* RCVD_ADD_ACK_REVOCATION */ *fee_states = fromwire_fee_states(ctx, &cursor, &plen); + *blockheight_states = fromwire_height_states(ctx, &cursor, &plen); num_changed = fromwire_u16(&cursor, &plen); // 2nd case changed *changed = num_changed ? tal_arr(ctx, struct changed_htlc, num_changed) : NULL; @@ -1137,4 +1151,26 @@ bool fromwire_channeld_upgraded(const void *p, bool *option_static_remotekey) *option_static_remotekey = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:2d7b763e89512ad8c5921b90c13f37ac83ab0016384c38e8c8e831683d668651 + +/* WIRE: CHANNELD_BLOCKHEIGHT */ +/* Tell peer about our latest and greatest blockheight. */ +u8 *towire_channeld_blockheight(const tal_t *ctx, u32 blockheight) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_CHANNELD_BLOCKHEIGHT); + towire_u32(&p, blockheight); + + return memcheck(p, tal_count(p)); +} +bool fromwire_channeld_blockheight(const void *p, u32 *blockheight) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_CHANNELD_BLOCKHEIGHT) + return false; + *blockheight = fromwire_u32(&cursor, &plen); + return cursor != NULL; +} +// SHA256STAMP:8abda9aa7ebc8598eba2e8827f4214d63f1188fefab17b28d77dce0198e3dfc6 diff --git a/channeld/channeld_wiregen.h b/channeld/channeld_wiregen.h index 3dde409668f6..9edca74464b2 100644 --- a/channeld/channeld_wiregen.h +++ b/channeld/channeld_wiregen.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,8 @@ enum channeld_wire { WIRE_CHANNELD_DEV_QUIESCE_REPLY = 1109, /* Tell master we're upgrading the commitment tx. */ WIRE_CHANNELD_UPGRADED = 1011, + /* Tell peer about our latest and greatest blockheight. */ + WIRE_CHANNELD_BLOCKHEIGHT = 1012, }; const char *channeld_wire_name(int e); @@ -91,8 +94,8 @@ bool channeld_wire_is_defined(u16 type); /* WIRE: CHANNELD_INIT */ /* Begin! (passes gossipd-client fd) */ -u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, u32 minimum_depth, const struct channel_config *our_config, const struct channel_config *their_config, const struct fee_states *fee_states, u32 feerate_min, u32 feerate_max, u32 feerate_penalty, const struct bitcoin_signature *first_commit_sig, const struct per_peer_state *per_peer_state, const struct pubkey *remote_fundingkey, const struct basepoints *remote_basepoints, const struct pubkey *remote_per_commit, const struct pubkey *old_remote_per_commit, enum side opener, u32 fee_base, u32 fee_proportional, struct amount_msat local_msatoshi, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct node_id *local_node_id, const struct node_id *remote_node_id, u32 commit_msec, u16 cltv_delta, bool last_was_revoke, const struct changed_htlc *last_sent_commit, u64 next_index_local, u64 next_index_remote, u64 revocations_received, u64 next_htlc_id, const struct existing_htlc **htlcs, bool local_funding_locked, bool remote_funding_locked, const struct short_channel_id *funding_short_id, bool reestablish, bool send_shutdown, bool remote_shutdown_received, const u8 *final_scriptpubkey, u8 flags, const u8 *init_peer_pkt, bool reached_announce_depth, const struct secret *last_remote_secret, const u8 *their_features, const u8 *upfront_shutdown_script, const secp256k1_ecdsa_signature *remote_ann_node_sig, const secp256k1_ecdsa_signature *remote_ann_bitcoin_sig, bool option_static_remotekey, bool option_anchor_outputs, bool dev_fast_gossip, bool dev_fail_process_onionpacket, const struct penalty_base *pbases); -bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, u32 *minimum_depth, struct channel_config *our_config, struct channel_config *their_config, struct fee_states **fee_states, u32 *feerate_min, u32 *feerate_max, u32 *feerate_penalty, struct bitcoin_signature *first_commit_sig, struct per_peer_state **per_peer_state, struct pubkey *remote_fundingkey, struct basepoints *remote_basepoints, struct pubkey *remote_per_commit, struct pubkey *old_remote_per_commit, enum side *opener, u32 *fee_base, u32 *fee_proportional, struct amount_msat *local_msatoshi, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct node_id *local_node_id, struct node_id *remote_node_id, u32 *commit_msec, u16 *cltv_delta, bool *last_was_revoke, struct changed_htlc **last_sent_commit, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u64 *next_htlc_id, struct existing_htlc ***htlcs, bool *local_funding_locked, bool *remote_funding_locked, struct short_channel_id *funding_short_id, bool *reestablish, bool *send_shutdown, bool *remote_shutdown_received, u8 **final_scriptpubkey, u8 *flags, u8 **init_peer_pkt, bool *reached_announce_depth, struct secret *last_remote_secret, u8 **their_features, u8 **upfront_shutdown_script, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig, bool *option_static_remotekey, bool *option_anchor_outputs, bool *dev_fast_gossip, bool *dev_fail_process_onionpacket, struct penalty_base **pbases); +u8 *towire_channeld_init(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_features, const struct channel_id *channel_id, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshi, u32 minimum_depth, u32 our_blockheight, const struct height_states *blockheight_states, u32 lease_expiry, const struct channel_config *our_config, const struct channel_config *their_config, const struct fee_states *fee_states, u32 feerate_min, u32 feerate_max, u32 feerate_penalty, const struct bitcoin_signature *first_commit_sig, const struct per_peer_state *per_peer_state, const struct pubkey *remote_fundingkey, const struct basepoints *remote_basepoints, const struct pubkey *remote_per_commit, const struct pubkey *old_remote_per_commit, enum side opener, u32 fee_base, u32 fee_proportional, struct amount_msat local_msatoshi, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct node_id *local_node_id, const struct node_id *remote_node_id, u32 commit_msec, u16 cltv_delta, bool last_was_revoke, const struct changed_htlc *last_sent_commit, u64 next_index_local, u64 next_index_remote, u64 revocations_received, u64 next_htlc_id, const struct existing_htlc **htlcs, bool local_funding_locked, bool remote_funding_locked, const struct short_channel_id *funding_short_id, bool reestablish, bool send_shutdown, bool remote_shutdown_received, const u8 *final_scriptpubkey, u8 flags, const u8 *init_peer_pkt, bool reached_announce_depth, const struct secret *last_remote_secret, const u8 *their_features, const u8 *upfront_shutdown_script, const secp256k1_ecdsa_signature *remote_ann_node_sig, const secp256k1_ecdsa_signature *remote_ann_bitcoin_sig, bool option_static_remotekey, bool option_anchor_outputs, bool dev_fast_gossip, bool dev_fail_process_onionpacket, const struct penalty_base *pbases); +bool fromwire_channeld_init(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_features, struct channel_id *channel_id, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshi, u32 *minimum_depth, u32 *our_blockheight, struct height_states **blockheight_states, u32 *lease_expiry, struct channel_config *our_config, struct channel_config *their_config, struct fee_states **fee_states, u32 *feerate_min, u32 *feerate_max, u32 *feerate_penalty, struct bitcoin_signature *first_commit_sig, struct per_peer_state **per_peer_state, struct pubkey *remote_fundingkey, struct basepoints *remote_basepoints, struct pubkey *remote_per_commit, struct pubkey *old_remote_per_commit, enum side *opener, u32 *fee_base, u32 *fee_proportional, struct amount_msat *local_msatoshi, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct node_id *local_node_id, struct node_id *remote_node_id, u32 *commit_msec, u16 *cltv_delta, bool *last_was_revoke, struct changed_htlc **last_sent_commit, u64 *next_index_local, u64 *next_index_remote, u64 *revocations_received, u64 *next_htlc_id, struct existing_htlc ***htlcs, bool *local_funding_locked, bool *remote_funding_locked, struct short_channel_id *funding_short_id, bool *reestablish, bool *send_shutdown, bool *remote_shutdown_received, u8 **final_scriptpubkey, u8 *flags, u8 **init_peer_pkt, bool *reached_announce_depth, struct secret *last_remote_secret, u8 **their_features, u8 **upfront_shutdown_script, secp256k1_ecdsa_signature **remote_ann_node_sig, secp256k1_ecdsa_signature **remote_ann_bitcoin_sig, bool *option_static_remotekey, bool *option_anchor_outputs, bool *dev_fast_gossip, bool *dev_fail_process_onionpacket, struct penalty_base **pbases); /* WIRE: CHANNELD_FUNDING_DEPTH */ /* master->channeld funding hit new depth(funding locked if >= lock depth) */ @@ -126,8 +129,8 @@ bool fromwire_channeld_got_funding_locked(const void *p, struct pubkey *next_per /* WIRE: CHANNELD_SENDING_COMMITSIG */ /* When we send a commitment_signed message */ -u8 *towire_channeld_sending_commitsig(const tal_t *ctx, u64 commitnum, const struct penalty_base *pbase, const struct fee_states *fee_states, const struct changed_htlc *changed, const struct bitcoin_signature *commit_sig, const struct bitcoin_signature *htlc_sigs); -bool fromwire_channeld_sending_commitsig(const tal_t *ctx, const void *p, u64 *commitnum, struct penalty_base **pbase, struct fee_states **fee_states, struct changed_htlc **changed, struct bitcoin_signature *commit_sig, struct bitcoin_signature **htlc_sigs); +u8 *towire_channeld_sending_commitsig(const tal_t *ctx, u64 commitnum, const struct penalty_base *pbase, const struct fee_states *fee_states, const struct height_states *blockheight_states, const struct changed_htlc *changed, const struct bitcoin_signature *commit_sig, const struct bitcoin_signature *htlc_sigs); +bool fromwire_channeld_sending_commitsig(const tal_t *ctx, const void *p, u64 *commitnum, struct penalty_base **pbase, struct fee_states **fee_states, struct height_states **blockheight_states, struct changed_htlc **changed, struct bitcoin_signature *commit_sig, struct bitcoin_signature **htlc_sigs); /* WIRE: CHANNELD_SENDING_COMMITSIG_REPLY */ /* Wait for reply */ @@ -136,8 +139,8 @@ bool fromwire_channeld_sending_commitsig_reply(const void *p); /* WIRE: CHANNELD_GOT_COMMITSIG */ /* When we have a commitment_signed message */ -u8 *towire_channeld_got_commitsig(const tal_t *ctx, u64 commitnum, const struct fee_states *fee_states, const struct bitcoin_signature *signature, const struct bitcoin_signature *htlc_signature, const struct added_htlc *added, const struct fulfilled_htlc *fulfilled, const struct failed_htlc **failed, const struct changed_htlc *changed, const struct bitcoin_tx *tx); -bool fromwire_channeld_got_commitsig(const tal_t *ctx, const void *p, u64 *commitnum, struct fee_states **fee_states, struct bitcoin_signature *signature, struct bitcoin_signature **htlc_signature, struct added_htlc **added, struct fulfilled_htlc **fulfilled, struct failed_htlc ***failed, struct changed_htlc **changed, struct bitcoin_tx **tx); +u8 *towire_channeld_got_commitsig(const tal_t *ctx, u64 commitnum, const struct fee_states *fee_states, const struct height_states *blockheight_states, const struct bitcoin_signature *signature, const struct bitcoin_signature *htlc_signature, const struct added_htlc *added, const struct fulfilled_htlc *fulfilled, const struct failed_htlc **failed, const struct changed_htlc *changed, const struct bitcoin_tx *tx); +bool fromwire_channeld_got_commitsig(const tal_t *ctx, const void *p, u64 *commitnum, struct fee_states **fee_states, struct height_states **blockheight_states, struct bitcoin_signature *signature, struct bitcoin_signature **htlc_signature, struct added_htlc **added, struct fulfilled_htlc **fulfilled, struct failed_htlc ***failed, struct changed_htlc **changed, struct bitcoin_tx **tx); /* WIRE: CHANNELD_GOT_COMMITSIG_REPLY */ /* Wait for reply */ @@ -145,8 +148,8 @@ u8 *towire_channeld_got_commitsig_reply(const tal_t *ctx); bool fromwire_channeld_got_commitsig_reply(const void *p); /* WIRE: CHANNELD_GOT_REVOKE */ -u8 *towire_channeld_got_revoke(const tal_t *ctx, u64 revokenum, const struct secret *per_commitment_secret, const struct pubkey *next_per_commit_point, const struct fee_states *fee_states, const struct changed_htlc *changed, const struct penalty_base *pbase, const struct bitcoin_tx *penalty_tx); -bool fromwire_channeld_got_revoke(const tal_t *ctx, const void *p, u64 *revokenum, struct secret *per_commitment_secret, struct pubkey *next_per_commit_point, struct fee_states **fee_states, struct changed_htlc **changed, struct penalty_base **pbase, struct bitcoin_tx **penalty_tx); +u8 *towire_channeld_got_revoke(const tal_t *ctx, u64 revokenum, const struct secret *per_commitment_secret, const struct pubkey *next_per_commit_point, const struct fee_states *fee_states, const struct height_states *blockheight_states, const struct changed_htlc *changed, const struct penalty_base *pbase, const struct bitcoin_tx *penalty_tx); +bool fromwire_channeld_got_revoke(const tal_t *ctx, const void *p, u64 *revokenum, struct secret *per_commitment_secret, struct pubkey *next_per_commit_point, struct fee_states **fee_states, struct height_states **blockheight_states, struct changed_htlc **changed, struct penalty_base **pbase, struct bitcoin_tx **penalty_tx); /* WIRE: CHANNELD_GOT_REVOKE_REPLY */ /* Wait for reply */ @@ -230,6 +233,11 @@ bool fromwire_channeld_dev_quiesce_reply(const void *p); u8 *towire_channeld_upgraded(const tal_t *ctx, bool option_static_remotekey); bool fromwire_channeld_upgraded(const void *p, bool *option_static_remotekey); +/* WIRE: CHANNELD_BLOCKHEIGHT */ +/* Tell peer about our latest and greatest blockheight. */ +u8 *towire_channeld_blockheight(const tal_t *ctx, u32 blockheight); +bool fromwire_channeld_blockheight(const void *p, u32 *blockheight); + #endif /* LIGHTNING_CHANNELD_CHANNELD_WIREGEN_H */ -// SHA256STAMP:2d7b763e89512ad8c5921b90c13f37ac83ab0016384c38e8c8e831683d668651 +// SHA256STAMP:8abda9aa7ebc8598eba2e8827f4214d63f1188fefab17b28d77dce0198e3dfc6 diff --git a/channeld/commit_tx.c b/channeld/commit_tx.c index 1b4fd94cfc6e..8718d7577e18 100644 --- a/channeld/commit_tx.c +++ b/channeld/commit_tx.c @@ -90,6 +90,8 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, const struct pubkey *remote_funding_key, enum side opener, u16 to_self_delay, + u32 lease_expiry, + u32 blockheight, const struct keyset *keyset, u32 feerate_per_kw, struct amount_sat dust_limit, @@ -106,6 +108,8 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, struct amount_msat total_pay; struct bitcoin_tx *tx; size_t i, n, untrimmed; + /* Is this the lessor ? */ + enum side lessor = !opener; u32 *cltvs; bool to_local, to_remote; struct htlc *dummy_to_local = (struct htlc *)0x01, @@ -113,6 +117,8 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, const u8 *funding_wscript = bitcoin_redeem_2of2(tmpctx, local_funding_key, remote_funding_key); + u32 csv_lock = lease_expiry > blockheight ? + lease_expiry - blockheight : 1; if (!amount_msat_add(&total_pay, self_pay, other_pay)) abort(); @@ -234,7 +240,16 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * output](#to_local-output). */ if (amount_msat_greater_eq_sat(self_pay, dust_limit)) { - u8 *wscript = to_self_wscript(tmpctx, to_self_delay,keyset); + /* BOLT- #3: + * In a leased channel, the `to_local` output that + * pays the `accepter` node is modified so that its + * CSV is equal to the greater of the + * `to_self_delay` or the `lease_end` - `blockheight`. + */ + u8 *wscript = to_self_wscript(tmpctx, + to_self_delay, + side == lessor ? csv_lock : 0, + keyset); u8 *p2wsh = scriptpubkey_p2wsh(tx, wscript); struct amount_sat amount = amount_msat_to_sat_round_down(self_pay); @@ -275,8 +290,24 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, * Otherwise, this output is a simple P2WPKH to `remotepubkey`. */ if (option_anchor_outputs) { - scriptpubkey = scriptpubkey_p2wsh(tmpctx, - anchor_to_remote_redeem(tmpctx, &keyset->other_payment_key)); + const u8 *redeem + = anchor_to_remote_redeem(tmpctx, + &keyset->other_payment_key, + (!side) == lessor ? + csv_lock : 1); + /* BOLT- #3: + * ##### Leased channel (`option_will_fund`) + * + * If a `lease` applies to the channel, the + * `to_remote` output of the `initiator` + * ensures the `leasor` funds are not + * spendable until the lease expires. + * + * OP_CHECKSIGVERIFY + * MAX(1, lease_end - blockheight) + * OP_CHECKSEQUENCEVERIFY + */ + scriptpubkey = scriptpubkey_p2wsh(tmpctx, redeem); } else { scriptpubkey = scriptpubkey_p2wpkh(tmpctx, &keyset->other_payment_key); diff --git a/channeld/commit_tx.h b/channeld/commit_tx.h index 54ebcebd2b8b..31fe5ac6e4e3 100644 --- a/channeld/commit_tx.h +++ b/channeld/commit_tx.h @@ -58,6 +58,8 @@ struct bitcoin_tx *commit_tx(const tal_t *ctx, const struct pubkey *remote_funding_key, enum side opener, u16 to_self_delay, + u32 lease_expiry, + u32 blockheight, const struct keyset *keyset, u32 feerate_per_kw, struct amount_sat dust_limit, diff --git a/channeld/full_channel.c b/channeld/full_channel.c index 59665eeb4136..f9291a8d7e09 100644 --- a/channeld/full_channel.c +++ b/channeld/full_channel.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,8 @@ struct channel *new_full_channel(const tal_t *ctx, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, u32 minimum_depth, + const struct height_states *blockheight_states, + u32 lease_expiry, struct amount_sat funding, struct amount_msat local_msat, const struct fee_states *fee_states, @@ -113,6 +116,8 @@ struct channel *new_full_channel(const tal_t *ctx, funding_txid, funding_txout, minimum_depth, + blockheight_states, + lease_expiry, funding, local_msat, fee_states, @@ -336,8 +341,10 @@ struct bitcoin_tx **channel_txs(const tal_t *ctx, &channel->funding_pubkey[side], &channel->funding_pubkey[!side], channel->opener, - channel->config[!side].to_self_delay, &keyset, - channel_feerate(channel, side), + channel->config[!side].to_self_delay, + channel->lease_expiry, + channel_blockheight(channel, side), + &keyset, channel_feerate(channel, side), channel->config[side].dust_limit, channel->view[side].owed[side], channel->view[side].owed[!side], committed, htlcmap, direct_outputs, commitment_number ^ channel->commitment_number_obscurer, @@ -977,6 +984,34 @@ static bool fee_incstate(struct channel *channel, return true; } +static bool blockheight_incstate(struct channel *channel, + enum side sidechanged, + enum htlc_state hstate) +{ + int preflags, postflags; + + preflags = htlc_state_flags(hstate); + postflags = htlc_state_flags(hstate + 1); + + /* You can't change sides. */ + assert((preflags & (HTLC_LOCAL_F_OWNER|HTLC_REMOTE_F_OWNER)) + == (postflags & (HTLC_LOCAL_F_OWNER|HTLC_REMOTE_F_OWNER))); + + /* These only advance through ADDING states. */ + if (!(htlc_state_flags(hstate) & HTLC_ADDING)) + return false; + + if (!inc_height_state(channel->blockheight_states, hstate)) + return false; + + status_debug("Blockheight: %s->%s %s now %u", + htlc_state_name(hstate), + htlc_state_name(hstate+1), + side_to_str(sidechanged), + *channel->blockheight_states->height[hstate+1]); + return true; +} + /* Returns flags which were changed. */ static int change_htlcs(struct channel *channel, enum side sidechanged, @@ -1020,11 +1055,16 @@ static int change_htlcs(struct channel *channel, } } - /* Update fees (do backwards, to avoid double-increment!). */ + /* Update fees and blockheight (do backwards, to avoid + * double-increment!). */ for (i = n_hstates - 1; i >= 0; i--) { if (fee_incstate(channel, sidechanged, htlc_states[i])) cflags |= (htlc_state_flags(htlc_states[i]) ^ htlc_state_flags(htlc_states[i]+1)); + + if (blockheight_incstate(channel, sidechanged, htlc_states[i])) + cflags |= (htlc_state_flags(htlc_states[i]) + ^ htlc_state_flags(htlc_states[i]+1)); } return cflags; @@ -1152,6 +1192,16 @@ bool channel_update_feerate(struct channel *channel, u32 feerate_per_kw) return true; } +void channel_update_blockheight(struct channel *channel, + u32 blockheight) +{ + status_debug("Setting %s blockheight to %u", + side_to_str(!channel->opener), blockheight); + + start_height_update(channel->blockheight_states, channel->opener, + blockheight); +} + bool channel_sending_commit(struct channel *channel, const struct htlc ***htlcs) { @@ -1276,10 +1326,14 @@ bool pending_updates(const struct channel *channel, struct htlc_map_iter it; const struct htlc *htlc; - /* Initiator might have fee changes in play. */ + /* Initiator might have fee changes or blockheight updates in play. */ if (side == channel->opener) { if (!feerate_changes_done(channel->fee_states, uncommitted_ok)) return true; + + if (!blockheight_changes_done(channel->blockheight_states, + uncommitted_ok)) + return true; } for (htlc = htlc_map_first(channel->htlcs, &it); diff --git a/channeld/full_channel.h b/channeld/full_channel.h index e23deb3b11fa..56b7c1514aa1 100644 --- a/channeld/full_channel.h +++ b/channeld/full_channel.h @@ -17,6 +17,8 @@ struct existing_htlc; * @funding_txid: The commitment transaction id. * @funding_txout: The commitment transaction output number. * @minimum_depth: The minimum confirmations needed for funding transaction. + * @blockheight_states: The blockheight update states. + * @lease_expiry: The block the lease on this channel expires at; 0 if no lease. * @funding: The commitment transaction amount. * @local_msat: The amount for the local side (remainder goes to remote) * @fee_states: The fee update states. @@ -37,6 +39,8 @@ struct channel *new_full_channel(const tal_t *ctx, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, u32 minimum_depth, + const struct height_states *blockheight_states, + u32 lease_expiry, struct amount_sat funding, struct amount_msat local_msat, const struct fee_states *fee_states, @@ -182,6 +186,13 @@ bool can_opener_afford_feerate(const struct channel *channel, u32 feerate); */ bool channel_update_feerate(struct channel *channel, u32 feerate_per_kw); +/* + * channel_update_blockheight: Change blockheight on non-opener side. + * @channel: The channel + * @blockheight: current blockheight + */ +void channel_update_blockheight(struct channel *channel, u32 blockheight); + /** * channel_feerate: Get fee rate for this side of channel. * @channel: The channel diff --git a/channeld/test/run-commit_tx.c b/channeld/test/run-commit_tx.c index 66590b49f336..2006d7194320 100644 --- a/channeld/test/run-commit_tx.c +++ b/channeld/test/run-commit_tx.c @@ -800,6 +800,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -815,6 +816,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, REMOTE, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -863,6 +865,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -878,6 +881,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, REMOTE, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -914,6 +918,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -930,6 +935,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, REMOTE, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -972,6 +978,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw-1, dust_limit, @@ -1020,6 +1027,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -1094,6 +1102,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -1148,6 +1157,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, @@ -1163,6 +1173,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, REMOTE, to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw, dust_limit, diff --git a/channeld/test/run-full_channel.c b/channeld/test/run-full_channel.c index 89eb791e758b..c2c4eb7ac58a 100644 --- a/channeld/test/run-full_channel.c +++ b/channeld/test/run-full_channel.c @@ -1,3 +1,4 @@ +#include "../../common/blockheight_states.c" #include "../../common/channel_id.c" #include "../../common/fee_states.c" #include "../../common/initial_channel.c" @@ -370,6 +371,7 @@ int main(int argc, const char *argv[]) const struct htlc **htlc_map, **htlcs; const u8 *funding_wscript, *funding_wscript_alt; bool option_anchor_outputs = false; + u32 blockheight = 0; size_t i; chainparams = chainparams_for_network("bitcoin"); @@ -481,6 +483,8 @@ int main(int argc, const char *argv[]) derive_channel_id(&cid, &funding_txid, funding_output_index); lchannel = new_full_channel(tmpctx, &cid, &funding_txid, funding_output_index, 0, + take(new_height_states(NULL, LOCAL, &blockheight)), + 0, /* No channel lease */ funding_amount, to_local, take(new_fee_states(NULL, LOCAL, &feerate_per_kw[LOCAL])), @@ -492,6 +496,8 @@ int main(int argc, const char *argv[]) false, false, LOCAL); rchannel = new_full_channel(tmpctx, &cid, &funding_txid, funding_output_index, 0, + take(new_height_states(NULL, LOCAL, &blockheight)), + 0, /* No channel lease */ funding_amount, to_remote, take(new_fee_states(NULL, REMOTE, &feerate_per_kw[REMOTE])), @@ -528,6 +534,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, remote_config->to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw[LOCAL], local_config->dust_limit, @@ -658,6 +665,7 @@ int main(int argc, const char *argv[]) &local_funding_pubkey, &remote_funding_pubkey, LOCAL, remote_config->to_self_delay, + 0, 0, /* No lease */ &keyset, feerate_per_kw[LOCAL], local_config->dust_limit, to_local, to_remote, htlcs, &htlc_map, NULL, 0x2bb038521914 ^ 42, diff --git a/channeld/watchtower.c b/channeld/watchtower.c index ded35186eddd..994f643d0980 100644 --- a/channeld/watchtower.c +++ b/channeld/watchtower.c @@ -63,7 +63,8 @@ penalty_tx_create(const tal_t *ctx, status_failed(STATUS_FAIL_INTERNAL_ERROR, "Failed deriving keyset"); - wscript = bitcoin_wscript_to_local(tmpctx, remote_to_self_delay, + /* FIXME: csv_lock */ + wscript = bitcoin_wscript_to_local(tmpctx, remote_to_self_delay, 1, &keyset.self_revocation_key, &keyset.self_delayed_payment_key); diff --git a/closingd/closingd_wiregen.c b/closingd/closingd_wiregen.c index d1a94dfb55f4..0fc850d97416 100644 --- a/closingd/closingd_wiregen.c +++ b/closingd/closingd_wiregen.c @@ -213,4 +213,4 @@ bool fromwire_closingd_complete(const void *p) return false; return cursor != NULL; } -// SHA256STAMP:8a13df246be151bcef3dae15a9853016119248d330e76ab79d7013a11d5ecd23 +// SHA256STAMP:e1b60c5fe13077c645ab61650c9bd697148b169aa813f6f0053cf46783154260 diff --git a/closingd/closingd_wiregen.h b/closingd/closingd_wiregen.h index 9f46a12b97fe..f203da6cd0e6 100644 --- a/closingd/closingd_wiregen.h +++ b/closingd/closingd_wiregen.h @@ -56,4 +56,4 @@ bool fromwire_closingd_complete(const void *p); #endif /* LIGHTNING_CLOSINGD_CLOSINGD_WIREGEN_H */ -// SHA256STAMP:8a13df246be151bcef3dae15a9853016119248d330e76ab79d7013a11d5ecd23 +// SHA256STAMP:e1b60c5fe13077c645ab61650c9bd697148b169aa813f6f0053cf46783154260 diff --git a/common/amount.c b/common/amount.c index 3736d2913216..ddea6569c4f9 100644 --- a/common/amount.c +++ b/common/amount.c @@ -357,6 +357,11 @@ bool amount_sat_eq(struct amount_sat a, struct amount_sat b) return a.satoshis == b.satoshis; } +bool amount_sat_zero(struct amount_sat a) +{ + return a.satoshis == 0; +} + bool amount_msat_eq(struct amount_msat a, struct amount_msat b) { return a.millisatoshis == b.millisatoshis; diff --git a/common/amount.h b/common/amount.h index 58df61c5c32b..46d95d99de03 100644 --- a/common/amount.h +++ b/common/amount.h @@ -94,6 +94,9 @@ struct amount_sat amount_sat_div(struct amount_sat sat, u64 div); bool amount_sat_eq(struct amount_sat a, struct amount_sat b); bool amount_msat_eq(struct amount_msat a, struct amount_msat b); +/* Is a zero? */ +bool amount_sat_zero(struct amount_sat a); + /* Is a > b? */ bool amount_sat_greater(struct amount_sat a, struct amount_sat b); bool amount_msat_greater(struct amount_msat a, struct amount_msat b); diff --git a/common/blockheight_states.c b/common/blockheight_states.c new file mode 100644 index 000000000000..3a5a151d13e2 --- /dev/null +++ b/common/blockheight_states.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include + +struct height_states *new_height_states(const tal_t *ctx, + enum side opener, + const u32 *blockheight) +{ + struct height_states *states = tal(ctx, struct height_states); + + /* Set to NULL except terminal value */ + for (size_t i = 0; i < ARRAY_SIZE(states->height); i++) + states->height[i] = NULL; + + if (blockheight) + /* We reuse fee states! */ + states->height[last_fee_state(opener)] + = tal_dup(states, u32, blockheight); + return states; +} + +u32 get_blockheight(const struct height_states *height_states, + enum side opener, + enum side side) +{ + /* The first non-NULL blockheight committed to this side is current */ + /* We use the same states as update_fee */ + for (enum htlc_state i = first_fee_state(opener); + i <= last_fee_state(opener); + i++) { + if (!height_states->height[i]) + continue; + if (!(htlc_state_flags(i) & HTLC_FLAG(side, HTLC_F_COMMITTED))) + continue; + return *height_states->height[i]; + } + + /* Some blockheight should always be set! */ + abort(); +} + +void start_height_update(struct height_states *height_states, + enum side opener, + u32 blockheight) +{ + /* Same as the feerate states */ + enum htlc_state start = first_fee_state(opener); + + /* BOLT #2: + * Unlike an HTLC, `update_fee` is never closed but simply replaced. + */ + if (height_states->height[start] == NULL) + height_states->height[start] = tal(height_states, u32); + *height_states->height[start] = blockheight; +} + + +/* Are blockheights all agreed by both sides? */ +bool blockheight_changes_done(const struct height_states *height_states, + bool ignore_uncommitted) +{ + size_t num_blockheights = 0; + for (size_t i = 0; i < ARRAY_SIZE(height_states->height); i++) { + if (ignore_uncommitted + && (i == RCVD_ADD_HTLC || i == SENT_ADD_HTLC)) + continue; + num_blockheights += (height_states->height[i] != NULL); + } + return num_blockheights == 1; +} + +bool inc_height_state(struct height_states *height_states, + enum htlc_state hstate) +{ + /* These only advance through ADDING states. */ + assert(htlc_state_flags(hstate) & HTLC_ADDING); + + if (!height_states->height[hstate]) + return false; + + /* FIXME: We can never clash, except at final state unless someone + * has violated protocol (eg, send two revoke_and_ack back-to-back) */ + tal_free(height_states->height[hstate+1]); + height_states->height[hstate+1] = height_states->height[hstate]; + height_states->height[hstate] = NULL; + return true; +} + +struct height_states *dup_height_states(const tal_t *ctx, + const struct height_states *states TAKES) +{ + struct height_states *n; + + if (taken(states)) + return cast_const(struct height_states *, + tal_steal(ctx, states)); + + n = tal_dup(ctx, struct height_states, states); + for (size_t i = 0; i < ARRAY_SIZE(n->height); i++) { + if (n->height[i]) + n->height[i] = tal_dup(n, u32, n->height[i]); + + } + return n; +} + +/* FIXME: we don't know opener inside fromwire_height_states, so can't do + * this there :( */ +bool height_states_valid(const struct height_states *states, enum side opener) +{ + /* We use the same states as update fee */ + return states->height[last_fee_state(opener)] != NULL; +} + +void towire_height_states(u8 **pptr, const struct height_states *states) +{ + for (enum htlc_state i = 0; i < ARRAY_SIZE(states->height); i++) { + /* We don't send uncommitted feestates */ + if (!(htlc_state_flags(i) & (HTLC_REMOTE_F_COMMITTED + | HTLC_LOCAL_F_COMMITTED)) + || states->height[i] == NULL) { + towire_bool(pptr, false); + continue; + } + towire_bool(pptr, true); + towire_u32(pptr, *states->height[i]); + } +} + +struct height_states *fromwire_height_states(const tal_t *ctx, const u8 **cursor, size_t *max) +{ + struct height_states *states = tal(ctx, struct height_states); + + for (enum htlc_state i = 0; i < ARRAY_SIZE(states->height); i++) { + if (fromwire_bool(cursor, max)) { + states->height[i] = tal(states, u32); + *states->height[i] = fromwire_u32(cursor, max); + } else { + states->height[i] = NULL; + } + } + if (!*cursor) + return tal_free(states); + return states; +} + +static const char *fmt_height_states(const tal_t *ctx, + const struct height_states *states) +{ + char *ret = tal_strdup(ctx, "{"); + for (enum htlc_state i = 0; i < ARRAY_SIZE(states->height); i++) { + if (states->height[i] != NULL) + tal_append_fmt(&ret, " %s:%u", + htlc_state_name(i), + *states->height[i]); + } + tal_append_fmt(&ret, " }"); + return ret; +} +REGISTER_TYPE_TO_STRING(height_states, fmt_height_states); diff --git a/common/blockheight_states.h b/common/blockheight_states.h new file mode 100644 index 000000000000..715517533a84 --- /dev/null +++ b/common/blockheight_states.h @@ -0,0 +1,79 @@ +#ifndef LIGHTNING_COMMON_BLOCKHEIGHT_STATES_H +#define LIGHTNING_COMMON_BLOCKHEIGHT_STATES_H +#include "config.h" +#include +#include + +struct height_states { + /* Current blockheight: goes through same + * state machine as feestate addition. + * + * We need to know if there's an actual change pending though (even if + * it's a "change" to an idential feerate!) so we use pointers. + */ + u32 *height[HTLC_STATE_INVALID]; +}; + +/** + * new_height_states: Initialize a height_states structure as at + * open-of-channel. + * @ctx: the tal ctx to allocate off + * @opener: which side opened the channel + * (and thus, proposes blockheight updates). + * @blockheight: the initial blockheight (if any). + */ +struct height_states *new_height_states(const tal_t *ctx, + enum side opener, + const u32 *blockheight); + +/** + * get_blockheight: Get the current blockheight + * @height_states: the blockheight state machine + * @opener: which side opened the channel + * (and thus, proposes blockheight updates). + * @side: which side to get the blockheight for + */ +u32 get_blockheight(const struct height_states *height_states, + enum side opener, + enum side side); + +/** + * start_height_update: feed a new blockheight update into state machine. + * @height_states: the height state machine + * @opener: which side opened the channel (and thus, proposes + * blockheight updates). + * @blockheight: the new blockheight. + */ +void start_height_update(struct height_states *height_states, + enum side opener, + u32 blockheight); +/** + * inc_height_state: move this blockheight to the next state. + * @height_states: the blockheight state machine + * @hstate: state + * + * Moves height_states[hstate] to height_states[hstate+1], if not NULL. + * Returns true if it wasn't NULL. + */ +bool inc_height_state(struct height_states *height_states, + enum htlc_state hstate); + +/* Are blockheights all agreed by both sides? */ +bool blockheight_changes_done(const struct height_states *height_states, + bool ignore_uncommitted); + +/* Duplicate a set of height states */ +struct height_states *dup_height_states(const tal_t *ctx, + const struct height_states *states TAKES); + +/* Marshal and unmarshal */ +void towire_height_states(u8 **pptr, const struct height_states *height_states); +/* FIXME: You must check that height_states_valid! */ +struct height_states *fromwire_height_states(const tal_t *ctx, + const u8 **cursor, size_t *max); + +/** + * is this height_state struct valid for this side? + */ +bool height_states_valid(const struct height_states *states, enum side opener); +#endif /* LIGHTNING_COMMON_BLOCKHEIGHT_STATES_H */ diff --git a/common/gossip_constants.h b/common/gossip_constants.h index e5e652092070..dac2562eb9fb 100644 --- a/common/gossip_constants.h +++ b/common/gossip_constants.h @@ -47,6 +47,10 @@ #define GOSSIP_MIN_INTERVAL(dev_fast_gossip_flag) \ DEV_FAST_GOSSIP(dev_fast_gossip_flag, 5, 300) +/* How long to wait at start for the plugin to callback with liquidity ad */ +#define GOSSIP_NANN_STARTUP_DELAY(dev_fast_gossip_flag) \ + DEV_FAST_GOSSIP(dev_fast_gossip_flag, 8, 60) + /* BOLT #7: * * - SHOULD flush outgoing gossip messages once every 60 seconds, diff --git a/common/initial_channel.c b/common/initial_channel.c index 8270f4a1bf16..5cf9d2edc5c3 100644 --- a/common/initial_channel.c +++ b/common/initial_channel.c @@ -6,11 +6,13 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -20,6 +22,8 @@ struct channel *new_initial_channel(const tal_t *ctx, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, u32 minimum_depth, + const struct height_states *height_states TAKES, + u32 lease_expiry, struct amount_sat funding, struct amount_msat local_msatoshi, const struct fee_states *fee_states TAKES, @@ -41,6 +45,7 @@ struct channel *new_initial_channel(const tal_t *ctx, channel->funding_txout = funding_txout; channel->funding = funding; channel->minimum_depth = minimum_depth; + channel->lease_expiry = lease_expiry; if (!amount_sat_sub_msat(&remote_msatoshi, channel->funding, local_msatoshi)) return tal_free(channel); @@ -55,6 +60,9 @@ struct channel *new_initial_channel(const tal_t *ctx, /* takes() if necessary */ channel->fee_states = dup_fee_states(channel, fee_states); + /* takes() if necessary */ + channel->blockheight_states = dup_height_states(channel, height_states); + channel->view[LOCAL].owed[LOCAL] = channel->view[REMOTE].owed[LOCAL] = local_msatoshi; @@ -87,6 +95,7 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, { struct keyset keyset; struct bitcoin_tx *init_tx; + u32 csv_lock; /* This assumes no HTLCs! */ assert(!channel->htlcs); @@ -100,6 +109,16 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, return NULL; } + /* Figure out the csv_lock (if there's a lease) */ + if (channel->lease_expiry == 0) + csv_lock = 1; + else + /* For the initial commitment, starts max lease */ + csv_lock = channel->lease_expiry + - get_blockheight(channel->blockheight_states, + channel->opener, + side); + *wscript = bitcoin_redeem_2of2(ctx, &channel->funding_pubkey[side], &channel->funding_pubkey[!side]); @@ -119,7 +138,7 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, channel->config[!side].channel_reserve, 0 ^ channel->commitment_number_obscurer, direct_outputs, - side, + side, csv_lock, channel->option_anchor_outputs, err_reason); @@ -138,6 +157,12 @@ u32 channel_feerate(const struct channel *channel, enum side side) return get_feerate(channel->fee_states, channel->opener, side); } +u32 channel_blockheight(const struct channel *channel, enum side side) +{ + return get_blockheight(channel->blockheight_states, + channel->opener, side); +} + #if EXPERIMENTAL_FEATURES /* BOLT-upgrade_protocol #2: * Channel features are explicitly enumerated as `channel_type` bitfields, diff --git a/common/initial_channel.h b/common/initial_channel.h index 09f2e86a16b2..13dcb5e49f2c 100644 --- a/common/initial_channel.h +++ b/common/initial_channel.h @@ -62,6 +62,10 @@ struct channel { /* Fee changes, some which may be in transit */ struct fee_states *fee_states; + /* Blockheight changes, some which may be in transit + * (option_will_fund)*/ + struct height_states *blockheight_states; + /* What it looks like to each side. */ struct channel_view view[NUM_SIDES]; @@ -70,6 +74,9 @@ struct channel { /* Is this using option_anchor_outputs? */ bool option_anchor_outputs; + + /* When the lease expires for the funds in this channel */ + u32 lease_expiry; }; /** @@ -79,6 +86,8 @@ struct channel { * @funding_txid: The commitment transaction id. * @funding_txout: The commitment transaction output number. * @minimum_depth: The minimum confirmations needed for funding transaction. + * @height_states: The blockheight update states. + * @lease_expiry: Block the lease expires. * @funding_satoshis: The commitment transaction amount. * @local_msatoshi: The amount for the local side (remainder goes to remote) * @fee_states: The fee update states. @@ -99,6 +108,8 @@ struct channel *new_initial_channel(const tal_t *ctx, const struct bitcoin_txid *funding_txid, unsigned int funding_txout, u32 minimum_depth, + const struct height_states *height_states TAKES, + u32 lease_expiry, struct amount_sat funding, struct amount_msat local_msatoshi, const struct fee_states *fee_states TAKES, @@ -140,6 +151,13 @@ struct bitcoin_tx *initial_channel_tx(const tal_t *ctx, */ u32 channel_feerate(const struct channel *channel, enum side side); +/** + * channel_blockheight: Get blockheight for this side of channel. + * @channel: The channel + * @side: the side + */ +u32 channel_blockheight(const struct channel *channel, enum side side); + #if EXPERIMENTAL_FEATURES /* BOLT-upgrade_protocol #2: * Channel features are explicitly enumerated as `channel_type` bitfields, diff --git a/common/initial_commit_tx.c b/common/initial_commit_tx.c index c18201b4261b..0330a7801cf0 100644 --- a/common/initial_commit_tx.c +++ b/common/initial_commit_tx.c @@ -51,9 +51,10 @@ bool try_subtract_fee(enum side opener, enum side side, u8 *to_self_wscript(const tal_t *ctx, u16 to_self_delay, + u32 csv, const struct keyset *keyset) { - return bitcoin_wscript_to_local(ctx, to_self_delay, + return bitcoin_wscript_to_local(ctx, to_self_delay, csv, &keyset->self_revocation_key, &keyset->self_delayed_payment_key); } @@ -87,6 +88,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, u64 obscured_commitment_number, struct wally_tx_output *direct_outputs[NUM_SIDES], enum side side, + u32 csv_lock, bool option_anchor_outputs, char** err_reason) { @@ -96,6 +98,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, bool to_local, to_remote; struct amount_msat total_pay; struct amount_sat amount; + enum side lessor = !opener; u32 sequence; void *dummy_local = (void *)LOCAL, *dummy_remote = (void *)REMOTE; const void *output_order[NUM_SIDES]; @@ -208,7 +211,10 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, * output](#to_local-output). */ if (amount_msat_greater_eq_sat(self_pay, dust_limit)) { - u8 *wscript = to_self_wscript(tmpctx, to_self_delay, keyset); + u8 *wscript = to_self_wscript(tmpctx, + to_self_delay, + side == lessor ? csv_lock : 0, + keyset); amount = amount_msat_to_sat_round_down(self_pay); int pos = bitcoin_tx_add_output( tx, scriptpubkey_p2wsh(tx, wscript), wscript, amount); @@ -241,8 +247,11 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, amount = amount_msat_to_sat_round_down(other_pay); if (option_anchor_outputs) { - scriptpubkey = scriptpubkey_p2wsh(tmpctx, - anchor_to_remote_redeem(tmpctx, &keyset->other_payment_key)); + const u8 *redeem + = anchor_to_remote_redeem(tmpctx, + &keyset->other_payment_key, + (!side) == lessor ? csv_lock : 1); + scriptpubkey = scriptpubkey_p2wsh(tmpctx, redeem); } else { scriptpubkey = scriptpubkey_p2wpkh(tmpctx, &keyset->other_payment_key); diff --git a/common/initial_commit_tx.h b/common/initial_commit_tx.h index 8ff4b0155dd4..90f7dbda426a 100644 --- a/common/initial_commit_tx.h +++ b/common/initial_commit_tx.h @@ -121,6 +121,7 @@ struct bitcoin_tx *initial_commit_tx(const tal_t *ctx, u64 obscured_commitment_number, struct wally_tx_output *direct_outputs[NUM_SIDES], enum side side, + u32 csv_lock, bool option_anchor_outputs, char** err_reason); @@ -134,6 +135,7 @@ bool try_subtract_fee(enum side opener, enum side side, * scriptpubkey_p2wsh(ctx, wscript) gives the scriptpubkey */ u8 *to_self_wscript(const tal_t *ctx, u16 to_self_delay, + u32 csv, const struct keyset *keyset); /* To-other is simply: scriptpubkey_p2wpkh(tx, keyset->other_payment_key) */ diff --git a/common/json_helpers.c b/common/json_helpers.c index 61450a302fa1..924820c78c7f 100644 --- a/common/json_helpers.c +++ b/common/json_helpers.c @@ -13,6 +13,7 @@ #include #include #include +#include bool json_to_bitcoin_amount(const char *buffer, const jsmntok_t *tok, uint64_t *satoshi) @@ -369,3 +370,16 @@ void json_add_preimage(struct json_stream *result, const char *fieldname, json_add_hex(result, fieldname, preimage, sizeof(*preimage)); } +void json_add_lease_rates(struct json_stream *result, + const struct lease_rates *rates) +{ + json_add_amount_sat_only(result, "lease_fee_base_msat", + amount_sat(rates->lease_fee_base_sat)); + json_add_num(result, "lease_fee_basis", rates->lease_fee_basis); + json_add_num(result, "funding_weight", rates->funding_weight); + json_add_amount_msat_only(result, + "channel_fee_max_base_msat", + amount_msat(rates->channel_fee_max_base_msat)); + json_add_num(result, "channel_fee_max_proportional_thousandths", + rates->channel_fee_max_proportional_thousandths); +} diff --git a/common/json_helpers.h b/common/json_helpers.h index 5d0b568f4e34..cfe12c5fd998 100644 --- a/common/json_helpers.h +++ b/common/json_helpers.h @@ -11,6 +11,7 @@ struct amount_msat; struct amount_sat; struct bip340sig; struct channel_id; +struct lease_rates; struct node_id; struct preimage; struct pubkey; @@ -179,4 +180,8 @@ void json_add_psbt(struct json_stream *stream, const char *fieldname, const struct wally_psbt *psbt); +/* Add fields from the lease_rates to a json stream. + * Note that field names are set */ +void json_add_lease_rates(struct json_stream *result, + const struct lease_rates *rates); #endif /* LIGHTNING_COMMON_JSON_HELPERS_H */ diff --git a/common/json_tok.c b/common/json_tok.c index e30dfc35bfb4..ca9e9cc44cb6 100644 --- a/common/json_tok.c +++ b/common/json_tok.c @@ -542,3 +542,19 @@ struct command_result *param_outpoint_arr(struct command *cmd, } return NULL; } + +struct command_result *param_lease_hex(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct lease_rates **rates) +{ + if (!lease_rates_fromhex(cmd, buffer + tok->start, + tok->end - tok->start, + rates)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Could not decode '%s' %.*s", + name, json_tok_full_len(tok), + json_tok_full(buffer, tok)); + return NULL; +} diff --git a/common/json_tok.h b/common/json_tok.h index 394b2d7c09a0..13343c952dd7 100644 --- a/common/json_tok.h +++ b/common/json_tok.h @@ -4,6 +4,7 @@ #include "config.h" #include #include +#include #include #include #include @@ -189,4 +190,12 @@ struct command_result *param_outpoint_arr(struct command *cmd, const char *buffer, const jsmntok_t *tok, struct bitcoin_outpoint **outpoints); +/** + * Parse a 'compact-lease' (serialized lease_rates) back into lease_rates + */ +struct command_result *param_lease_hex(struct command *cmd, + const char *name, + const char *buffer, + const jsmntok_t *tok, + struct lease_rates **rates); #endif /* LIGHTNING_COMMON_JSON_TOK_H */ diff --git a/common/lease_rates.c b/common/lease_rates.c new file mode 100644 index 000000000000..4ab08c9d2691 --- /dev/null +++ b/common/lease_rates.c @@ -0,0 +1,156 @@ +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +/* FIXME: Is there a better way to do this ? */ +bool lease_rates_eq(struct lease_rates *l1, + struct lease_rates *l2) +{ + if (!l1 != !l2) + return false; + + if (!l1) + return true; + + return l1->funding_weight == l2->funding_weight + && l1->channel_fee_max_base_msat == l2->channel_fee_max_base_msat + && l1->channel_fee_max_proportional_thousandths == l2->channel_fee_max_proportional_thousandths + && l1->lease_fee_base_sat == l2->lease_fee_base_sat + && l1->lease_fee_basis == l2->lease_fee_basis; +} + +bool lease_rates_empty(struct lease_rates *rates) +{ + if (!rates) + return true; + + /* FIXME: why can't i do memeqzero? */ + return rates->funding_weight == 0 + && rates->channel_fee_max_base_msat == 0 + && rates->channel_fee_max_proportional_thousandths == 0 + && rates->lease_fee_base_sat == 0 + && rates->lease_fee_basis == 0; +} + +void lease_rates_get_commitment(struct pubkey *pubkey, + u32 lease_expiry, + u32 chan_fee_msat, + u16 chan_fee_ppt, + struct sha256 *sha) +{ + struct sha256_ctx sctx = SHA256_INIT; + u8 der[PUBKEY_CMPR_LEN]; + /* BOLT- #2: + * - MUST set `signature` to the ECDSA signature of + * SHA256("option_will_fund" + * || `funding_pubkey` + * || `blockheight` + 4032 + * || `channel_fee_max_base_msat` + * || `channel_fee_max_proportional_thousandths`) + * using the node_id key. + */ + pubkey_to_der(der, pubkey); + sha256_update(&sctx, "option_will_fund", strlen("option_will_fund")); + sha256_update(&sctx, der, PUBKEY_CMPR_LEN); + sha256_be32(&sctx, lease_expiry); + sha256_be32(&sctx, chan_fee_msat); + sha256_be16(&sctx, chan_fee_ppt); + sha256_done(&sctx, sha); +} + +bool lease_rates_calc_fee(struct lease_rates *rates, + struct amount_sat accept_funding_sats, + struct amount_sat requested_sats, + u32 onchain_feerate, + struct amount_sat *fee) +{ + struct amount_sat lease_fee, basis_sat, tx_fee; + /* BOLT- #2: + * The lease fee is calculated as: + * `lease_fee_base_sat` + + * min(`accept_channel2`.`funding_satoshis`, `open_channel2`.`requested_sats`) * `lease_fee_basis` / 10_000 + + * `funding_weight` * `funding_feerate_perkw` / 1000 + */ + + lease_fee = amount_sat(rates->lease_fee_base_sat); + basis_sat = amount_sat_less(accept_funding_sats, requested_sats) + ? accept_funding_sats : requested_sats; + + if (!amount_sat_scale(&basis_sat, basis_sat, + rates->lease_fee_basis)) + return false; + + basis_sat = amount_sat_div(basis_sat, 10000); + + if (!amount_sat_add(&lease_fee, lease_fee, basis_sat)) + return false; + + tx_fee = amount_tx_fee(onchain_feerate, rates->funding_weight); + if (!amount_sat_add(&lease_fee, lease_fee, tx_fee)) + return false; + + *fee = lease_fee; + return true; +} + +bool lease_rates_set_chan_fee_base_msat(struct lease_rates *rates, + struct amount_msat amt) +{ + rates->channel_fee_max_base_msat = amt.millisatoshis; /* Raw: conversion */ + return rates->channel_fee_max_base_msat == amt.millisatoshis; /* Raw: comparsion */ +} + +bool lease_rates_set_lease_fee_sat(struct lease_rates *rates, + struct amount_sat amt) +{ + rates->lease_fee_base_sat = amt.satoshis; /* Raw: conversion */ + return rates->lease_fee_base_sat == amt.satoshis; /* Raw: comparsion */ +} + +char *lease_rates_tohex(const tal_t *ctx, const struct lease_rates *rates) +{ + char *hex; + u8 *data = tal_arr(NULL, u8, 0); + towire_lease_rates(&data, rates); + hex = tal_hex(ctx, data); + tal_free(data); + return hex; +} + +bool lease_rates_fromhex(const tal_t *ctx, + const char *hexdata, size_t hexlen, + struct lease_rates **rates) +{ + const u8 *data = tal_hexdata(ctx, hexdata, hexlen); + size_t len = tal_bytelen(data); + + *rates = tal(ctx, struct lease_rates); + fromwire_lease_rates(&data, &len, *rates); + + if (data == NULL) { + tal_free(*rates); + return false; + } + + return true; +} + +char *lease_rates_fmt(const tal_t *ctx, const struct lease_rates *rates) +{ + return tal_fmt(ctx, "{channel_fee_max_base_msat=%u," + "channel_fee_max_ppt=%u," + "funding_weight=%u," + "lease_fee_base_sat=%u," + "lease_fee_basis=%u}", + rates->channel_fee_max_base_msat, + rates->channel_fee_max_proportional_thousandths, + rates->funding_weight, + rates->lease_fee_base_sat, + rates->lease_fee_basis); +} diff --git a/common/lease_rates.h b/common/lease_rates.h new file mode 100644 index 000000000000..d6e8b5afbf1c --- /dev/null +++ b/common/lease_rates.h @@ -0,0 +1,45 @@ +#ifndef LIGHTNING_COMMON_LEASE_RATES_H +#define LIGHTNING_COMMON_LEASE_RATES_H +#include "config.h" +#include + +struct amount_msat; +struct amount_sat; +struct lease_rates; +struct pubkey; +struct sha256; + +#define LEASE_RATE_DURATION 4032 + +bool lease_rates_empty(struct lease_rates *rates); + +void lease_rates_get_commitment(struct pubkey *pubkey, + u32 lease_expiry, + u32 chan_fee_msat, + u16 chan_fee_ppt, + struct sha256 *sha); + +bool lease_rates_eq(struct lease_rates *l1, + struct lease_rates *l2); + +bool lease_rates_calc_fee(struct lease_rates *rates, + struct amount_sat accept_funding_sats, + struct amount_sat requested_sats, + u32 onchain_feerate, + struct amount_sat *fee); + +WARN_UNUSED_RESULT bool lease_rates_set_chan_fee_base_msat(struct lease_rates *rates, struct amount_msat amt); + +WARN_UNUSED_RESULT bool lease_rates_set_lease_fee_sat(struct lease_rates *rates, struct amount_sat amt); + +/* Convert 'lease_rates' into a hexstring */ +char *lease_rates_tohex(const tal_t *ctx, const struct lease_rates *rates); + +/* Convert 'lease_rates' from a hexstring */ +bool lease_rates_fromhex(const tal_t *ctx, + const char *hexdata, size_t len, + struct lease_rates **rates); + +/* Format a string describing the passed in lease_rates */ +char *lease_rates_fmt(const tal_t *ctx, const struct lease_rates *rates); +#endif /* LIGHTNING_COMMON_LEASE_RATES_H */ diff --git a/common/peer_status_wiregen.c b/common/peer_status_wiregen.c index 0f12fa92a76f..3a0340416ad6 100644 --- a/common/peer_status_wiregen.c +++ b/common/peer_status_wiregen.c @@ -80,4 +80,4 @@ bool fromwire_status_peer_error(const tal_t *ctx, const void *p, struct channel_ fromwire_u8_array(&cursor, &plen, *error_for_them, len); return cursor != NULL; } -// SHA256STAMP:9d6739d97294bd0ec0691772616c4d3d0328d399ed2bef6c943f912aca7d438a +// SHA256STAMP:19118af89075707a68f9a89456d1bcf818da972931454dfa30700b769351e560 diff --git a/common/peer_status_wiregen.h b/common/peer_status_wiregen.h index 52d7941aecff..9f78de9bc966 100644 --- a/common/peer_status_wiregen.h +++ b/common/peer_status_wiregen.h @@ -34,4 +34,4 @@ bool fromwire_status_peer_error(const tal_t *ctx, const void *p, struct channel_ #endif /* LIGHTNING_COMMON_PEER_STATUS_WIREGEN_H */ -// SHA256STAMP:9d6739d97294bd0ec0691772616c4d3d0328d399ed2bef6c943f912aca7d438a +// SHA256STAMP:19118af89075707a68f9a89456d1bcf818da972931454dfa30700b769351e560 diff --git a/common/status_wiregen.c b/common/status_wiregen.c index d97b9ee0f765..d8b3be818458 100644 --- a/common/status_wiregen.c +++ b/common/status_wiregen.c @@ -214,4 +214,4 @@ bool fromwire_status_version(const tal_t *ctx, const void *p, wirestring **versi *version = fromwire_wirestring(ctx, &cursor, &plen); return cursor != NULL; } -// SHA256STAMP:3164c82c28124ba916aebd075baa2315cd82cee0d785908da25c6aa6c5b11f22 +// SHA256STAMP:deb2f38c55e6ef91420aa811b86351d972576bfc47f02cfe3014c46fed5962c3 diff --git a/common/status_wiregen.h b/common/status_wiregen.h index 9a45697e02bc..c67bf81e1709 100644 --- a/common/status_wiregen.h +++ b/common/status_wiregen.h @@ -58,4 +58,4 @@ bool fromwire_status_version(const tal_t *ctx, const void *p, wirestring **versi #endif /* LIGHTNING_COMMON_STATUS_WIREGEN_H */ -// SHA256STAMP:3164c82c28124ba916aebd075baa2315cd82cee0d785908da25c6aa6c5b11f22 +// SHA256STAMP:deb2f38c55e6ef91420aa811b86351d972576bfc47f02cfe3014c46fed5962c3 diff --git a/common/test/Makefile b/common/test/Makefile index 596801d82d53..00413f5aee0e 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -25,12 +25,17 @@ common/test/run-json: \ common/amount.o \ common/base32.o \ common/bigsize.o \ + common/channel_id.o \ common/json.o \ common/json_stream.o \ + common/lease_rates.o \ + common/node_id.o \ + common/pseudorand.o \ common/wireaddr.o \ common/type_to_string.o \ wire/fromwire.o \ wire/onion$(EXP)_wiregen.o \ + wire/peer$(EXP)_wiregen.o \ wire/towire.o common/test/run-route common/test/run-route-specific: \ @@ -48,6 +53,7 @@ common/test/run-route common/test/run-route-specific: \ common/test/run-gossmap_local: \ wire/fromwire.o \ wire/peer$(EXP)_wiregen.o \ + wire/tlvstream.o \ wire/towire.o check-units: $(COMMON_TEST_PROGRAMS:%=unittest/%) diff --git a/common/test/run-gossmap_local.c b/common/test/run-gossmap_local.c index 2e23651dfb32..c624f93d11b2 100644 --- a/common/test/run-gossmap_local.c +++ b/common/test/run-gossmap_local.c @@ -9,6 +9,7 @@ #include #include #include +#include /* AUTOGENERATED MOCKS START */ /* Generated stub for fromwire_bigsize */ @@ -18,25 +19,12 @@ bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) void fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "fromwire_channel_id called!\n"); abort(); } -/* Generated stub for fromwire_tlv */ -bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, - const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - void *record UNNEEDED, struct tlv_field **fields UNNEEDED) -{ fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for tlv_fields_valid */ -bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, size_t *err_index UNNEEDED) -{ fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } /* Generated stub for towire_bigsize */ void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) { fprintf(stderr, "towire_bigsize called!\n"); abort(); } /* Generated stub for towire_channel_id */ void towire_channel_id(u8 **pptr UNNEEDED, const struct channel_id *channel_id UNNEEDED) { fprintf(stderr, "towire_channel_id called!\n"); abort(); } -/* Generated stub for towire_tlv */ -void towire_tlv(u8 **pptr UNNEEDED, - const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, - const void *record UNNEEDED) -{ fprintf(stderr, "towire_tlv called!\n"); abort(); } /* Generated stub for type_to_string_ */ const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, union printable_types u UNNEEDED) @@ -290,13 +278,16 @@ static void check_nannounce(const u8 *nannounce, u32 timestamp; u8 rgb_color[3], alias[32]; struct node_id node_id; + struct tlv_node_ann_tlvs *na_tlvs + = tlv_node_ann_tlvs_new(tmpctx); assert(fromwire_node_announcement(nannounce, nannounce, &sig, &features, ×tamp, &node_id, rgb_color, alias, - &addresses)); + &addresses, + na_tlvs)); assert(node_id_eq(&node_id, n)); } diff --git a/common/test/run-json.c b/common/test/run-json.c index 599808f08f36..dae35b4028db 100644 --- a/common/test/run-json.c +++ b/common/test/run-json.c @@ -15,9 +15,6 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, const struct tlv_record_type *types UNNEEDED, size_t num_types UNNEEDED, void *record UNNEEDED, struct tlv_field **fields UNNEEDED) { fprintf(stderr, "fromwire_tlv called!\n"); abort(); } -/* Generated stub for node_id_from_hexstr */ -bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED) -{ fprintf(stderr, "node_id_from_hexstr called!\n"); abort(); } /* Generated stub for tlv_fields_valid */ bool tlv_fields_valid(const struct tlv_field *fields UNNEEDED, size_t *err_index UNNEEDED) { fprintf(stderr, "tlv_fields_valid called!\n"); abort(); } diff --git a/common/test/run-lease_rates.c b/common/test/run-lease_rates.c new file mode 100644 index 000000000000..8e6d307408c6 --- /dev/null +++ b/common/test/run-lease_rates.c @@ -0,0 +1,142 @@ +#include "../amount.c" +#include "../lease_rates.c" +#include +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_lease_rates */ +void fromwire_lease_rates(const u8 **cursor UNNEEDED, size_t *plen UNNEEDED, struct lease_rates *lease_rates UNNEEDED) +{ fprintf(stderr, "fromwire_lease_rates called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u16 */ +u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u16 called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_lease_rates */ +void towire_lease_rates(u8 **p UNNEEDED, const struct lease_rates *lease_rates UNNEEDED) +{ fprintf(stderr, "towire_lease_rates called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u16 */ +void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) +{ fprintf(stderr, "towire_u16 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +static void check_lease_rate_commitment_hash(void) +{ + struct pubkey p; + struct sha256 sha; + u8 *expected; + assert(pubkey_from_hexstr("032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e", strlen("032cf15d1ad9c4a08d26eab1918f732d8ef8fdc6abb9640bf3db174372c491304e"), &p)); + lease_rates_get_commitment(&p, 0, 0, 0, &sha); + + expected = tal_hexdata(tmpctx, "adc2973c151fecf212019dc3228a6765277cec6ab958959923839238a01cc1ce", strlen("adc2973c151fecf212019dc3228a6765277cec6ab958959923839238a01cc1ce")); + + assert(memeq(expected, tal_bytelen(expected), + &sha.u.u8, sizeof(sha.u.u8))); + + lease_rates_get_commitment(&p, 255, 100, 50, &sha); + expected = tal_hexdata(tmpctx, "d363b338db30a4abd184e576773befb6a05ba61cec4169998ccbf546a720e421", strlen("d363b338db30a4abd184e576773befb6a05ba61cec4169998ccbf546a720e421")); + + assert(memeq(expected, tal_bytelen(expected), + &sha.u.u8, sizeof(sha.u.u8))); + +} + +static void check_lease_rate_fees(void) +{ + /* BOLT- #2: + * E.g. + * An node requests 1_000_000sats at a feerate of 2500perkw. They + * are contributing 500_000sats. Their weight contribution to the + * funding transaction will be 720. + * + * The accepter adds 1,100,000sats and charges a base funding fee of + * 233sats with a lease fee basis of 22. Their funding weight is 444. + * The lease fee is as follows: + * 233 + min(1_000_000,1_100_000) * 22 / 10_000 + 444 * 2500 / 1000 + * The total lease fee for this open is 3543sats. + */ + struct amount_sat request_sats = amount_sat(1000000); + struct amount_sat accepter_contrib = amount_sat(1100000); + struct amount_sat fee; + struct lease_rates rates; + u32 feerate = 2500; + + rates.lease_fee_basis = 22; + rates.lease_fee_base_sat = 233; + rates.funding_weight = 444; + + assert(lease_rates_calc_fee(&rates, accepter_contrib, + request_sats, feerate, + &fee)); + assert(amount_sat_eq(fee, amount_sat(3543))); +} + +int main(int argc, char *argv[]) +{ + common_setup(argv[0]); + + check_lease_rate_commitment_hash(); + check_lease_rate_fees(); + + common_shutdown(); +} diff --git a/common/type_to_string.h b/common/type_to_string.h index e82dbd31b32c..fda0df7d106d 100644 --- a/common/type_to_string.h +++ b/common/type_to_string.h @@ -37,6 +37,7 @@ union printable_types { const struct amount_msat *amount_msat; const struct amount_sat *amount_sat; const struct fee_states *fee_states; + const struct height_states *height_states; const char *charp_; const struct wally_psbt *wally_psbt; const struct wally_tx *wally_tx; diff --git a/common/utxo.c b/common/utxo.c index b561d42b9733..22777320555c 100644 --- a/common/utxo.c +++ b/common/utxo.c @@ -31,6 +31,7 @@ void towire_utxo(u8 **pptr, const struct utxo *utxo) if (utxo->close_info->commitment_point) towire_pubkey(pptr, utxo->close_info->commitment_point); towire_bool(pptr, utxo->close_info->option_anchor_outputs); + towire_u32(pptr, utxo->close_info->csv); } } @@ -59,6 +60,7 @@ struct utxo *fromwire_utxo(const tal_t *ctx, const u8 **ptr, size_t *max) utxo->close_info->commitment_point = NULL; utxo->close_info->option_anchor_outputs = fromwire_bool(ptr, max); + utxo->close_info->csv = fromwire_u32(ptr, max); } else { utxo->close_info = NULL; } diff --git a/common/utxo.h b/common/utxo.h index bcf07fb6f3ca..11910af5b07f 100644 --- a/common/utxo.h +++ b/common/utxo.h @@ -1,6 +1,7 @@ #ifndef LIGHTNING_COMMON_UTXO_H #define LIGHTNING_COMMON_UTXO_H #include "config.h" +#include #include #include #include @@ -20,6 +21,7 @@ struct unilateral_close_info { bool option_anchor_outputs; /* NULL if this is an option_static_remotekey commitment */ struct pubkey *commitment_point; + u32 csv; }; /* Possible states for tracked outputs in the database. Not sure yet @@ -70,6 +72,17 @@ static inline bool utxo_is_reserved(const struct utxo *utxo, u32 current_height) return utxo->reserved_til > current_height; } +static inline bool utxo_is_csv_locked(const struct utxo *utxo, u32 current_height) +{ + if (!utxo->close_info) + return false; + /* All close outputs are csv locked for option_anchor_outputs */ + if (!utxo->blockheight && utxo->close_info->option_anchor_outputs) + return true; + assert(*utxo->blockheight + utxo->close_info->csv > *utxo->blockheight); + return *utxo->blockheight + utxo->close_info->csv > current_height; +} + void towire_utxo(u8 **pptr, const struct utxo *utxo); struct utxo *fromwire_utxo(const tal_t *ctx, const u8 **ptr, size_t *max); diff --git a/connectd/connectd_gossipd_wiregen.c b/connectd/connectd_gossipd_wiregen.c index ac2856c3abe5..89114222e106 100644 --- a/connectd/connectd_gossipd_wiregen.c +++ b/connectd/connectd_gossipd_wiregen.c @@ -161,4 +161,4 @@ bool fromwire_gossipd_get_addrs_reply(const tal_t *ctx, const void *p, struct wi fromwire_wireaddr(&cursor, &plen, *addrs + i); return cursor != NULL; } -// SHA256STAMP:6bfe0677cb910aba63f79cfc4164ce26034da95e16341eab3aac6fddcc04e3e9 +// SHA256STAMP:82c3831eab6ef72f96d6ab25b11861f33f3abea148d8739cd03bb8626678c84e diff --git a/connectd/connectd_gossipd_wiregen.h b/connectd/connectd_gossipd_wiregen.h index 5c391775adcd..d0cba543c711 100644 --- a/connectd/connectd_gossipd_wiregen.h +++ b/connectd/connectd_gossipd_wiregen.h @@ -54,4 +54,4 @@ bool fromwire_gossipd_get_addrs_reply(const tal_t *ctx, const void *p, struct wi #endif /* LIGHTNING_CONNECTD_CONNECTD_GOSSIPD_WIREGEN_H */ -// SHA256STAMP:6bfe0677cb910aba63f79cfc4164ce26034da95e16341eab3aac6fddcc04e3e9 +// SHA256STAMP:82c3831eab6ef72f96d6ab25b11861f33f3abea148d8739cd03bb8626678c84e diff --git a/connectd/connectd_wiregen.c b/connectd/connectd_wiregen.c index 3ee9ea506f2e..f4ab67805103 100644 --- a/connectd/connectd_wiregen.c +++ b/connectd/connectd_wiregen.c @@ -443,4 +443,4 @@ bool fromwire_connectd_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:042c0ae692c223da86af3f09977fdc5f19655e99b928ab05812dd4c1ed95f1c5 +// SHA256STAMP:bb3410ed32fbe0fcb0246b4c00782bd6b4c63fb778cce28e40cfcb2ad579d65a diff --git a/connectd/connectd_wiregen.h b/connectd/connectd_wiregen.h index 34ad8ca34f41..5fa803707326 100644 --- a/connectd/connectd_wiregen.h +++ b/connectd/connectd_wiregen.h @@ -110,4 +110,4 @@ bool fromwire_connectd_dev_memleak_reply(const void *p, bool *leak); #endif /* LIGHTNING_CONNECTD_CONNECTD_WIREGEN_H */ -// SHA256STAMP:042c0ae692c223da86af3f09977fdc5f19655e99b928ab05812dd4c1ed95f1c5 +// SHA256STAMP:bb3410ed32fbe0fcb0246b4c00782bd6b4c63fb778cce28e40cfcb2ad579d65a diff --git a/contrib/pyln-client/pyln/client/lightning.py b/contrib/pyln-client/pyln/client/lightning.py index f4e53f31935d..6945134781f7 100644 --- a/contrib/pyln-client/pyln/client/lightning.py +++ b/contrib/pyln-client/pyln/client/lightning.py @@ -515,17 +515,23 @@ def check(self, command_to_check, **kwargs): payload.update({k: v for k, v in kwargs.items()}) return self.call("check", payload) - def close(self, peer_id, unilateraltimeout=None, destination=None, fee_negotiation_step=None): + def close(self, peer_id, unilateraltimeout=None, destination=None, fee_negotiation_step=None, force_lease_closed=False): """ Close the channel with peer {id}, forcing a unilateral close after {unilateraltimeout} seconds if non-zero, and the to-local output will be sent to {destination}. + + If channel funds have been leased to the peer and the + lease has not yet expired, you can force a close with + {force_lease_closed}. Note that your funds will still be + locked until the lease expires. """ payload = { "id": peer_id, "unilateraltimeout": unilateraltimeout, "destination": destination, - "fee_negotiation_step": fee_negotiation_step + "fee_negotiation_step": fee_negotiation_step, + "force_lease_closed": force_lease_closed, } return self.call("close", payload) @@ -693,7 +699,7 @@ def feerates(self, style, urgent=None, normal=None, slow=None): } return self.call("feerates", payload) - def fundchannel(self, node_id, amount, feerate=None, announce=True, minconf=None, utxos=None, push_msat=None, close_to=None): + def fundchannel(self, node_id, amount, feerate=None, announce=True, minconf=None, utxos=None, push_msat=None, close_to=None, request_amt=None, compact_lease=None): """ Fund channel with {id} using {amount} satoshis with feerate of {feerate} (uses default feerate if unset). @@ -702,6 +708,10 @@ def fundchannel(self, node_id, amount, feerate=None, announce=True, minconf=None If {utxos} is specified (as a list of 'txid:vout' strings), fund a channel from these specifics utxos. {close_to} is a valid Bitcoin address. + + {request_amt} is the lease amount to request from the peer. Only + valid if peer is advertising a liquidity ad + supports v2 channel opens + (dual-funding) """ payload = { "id": node_id, @@ -712,6 +722,8 @@ def fundchannel(self, node_id, amount, feerate=None, announce=True, minconf=None "utxos": utxos, "push_msat": push_msat, "close_to": close_to, + "request_amt": request_amt, + "compact_lease": compact_lease, } return self.call("fundchannel", payload) @@ -1006,7 +1018,18 @@ def pay(self, bolt11, msatoshi=None, label=None, riskfactor=None, } return self.call("pay", payload) - def openchannel_init(self, node_id, channel_amount, psbt, feerate=None, funding_feerate=None, announce=True, close_to=None, *args, **kwargs): + def dev_queryrates(self, node_id, channel_amount, request_amt, commitment_feerate=None, funding_feerate=None): + """Ask a peer how much they'd charge for a given liquidity amount """ + payload = { + "id": node_id, + "amount": channel_amount, + "request_amt": request_amt, + "commitment_feerate": commitment_feerate, + "funding_feerate": funding_feerate, + } + return self.call("dev-queryrates", payload) + + def openchannel_init(self, node_id, channel_amount, psbt, feerate=None, funding_feerate=None, announce=True, close_to=None, request_amt=None, *args, **kwargs): """Initiate an openchannel with a peer """ payload = { "id": node_id, @@ -1016,6 +1039,7 @@ def openchannel_init(self, node_id, channel_amount, psbt, feerate=None, funding_ "funding_feerate": funding_feerate, "announce": announce, "close_to": close_to, + "request_amt": request_amt, } return self.call("openchannel_init", payload) diff --git a/contrib/startup_regtest.sh b/contrib/startup_regtest.sh index 2f9640c24762..280ef85f95ad 100755 --- a/contrib/startup_regtest.sh +++ b/contrib/startup_regtest.sh @@ -101,13 +101,15 @@ start_nodes() { funder-min-their-funding=10000 funder-per-channel-max=100000 funder-fuzz-percent=0 + lease-fee-base-msat=2sat + lease-fee-basis=50 EOF fi # Start the lightning nodes test -f "/tmp/l$i-$network/lightningd-$network.pid" || \ - "$LIGHTNINGD" "--lightning-dir=/tmp/l$i-$network" --daemon + "$LIGHTNINGD" "--lightning-dir=/tmp/l$i-$network" & # shellcheck disable=SC2139 disable=SC2086 alias l$i-cli="$LCLI --lightning-dir=/tmp/l$i-$network" # shellcheck disable=SC2139 disable=SC2086 diff --git a/devtools/Makefile b/devtools/Makefile index 247f853a3301..66c0ac681e63 100644 --- a/devtools/Makefile +++ b/devtools/Makefile @@ -18,6 +18,7 @@ DEVTOOLS_COMMON_OBJS := \ common/bech32_util.o \ common/bigsize.o \ common/bolt11.o \ + common/blockheight_states.o \ common/channel_id.o \ common/crypto_state.o \ common/decode_array.o \ diff --git a/devtools/create-gossipstore.c b/devtools/create-gossipstore.c index 42c9db3958e1..8fde975a59df 100644 --- a/devtools/create-gossipstore.c +++ b/devtools/create-gossipstore.c @@ -88,9 +88,12 @@ static u32 get_node_announce_timestamp(const u8 *msg) struct node_id id; u8 rgb_color[3], alias[32]; u8 *features, *addresses; + struct tlv_node_ann_tlvs *na_tlvs; + na_tlvs = tlv_node_ann_tlvs_new(tmpctx); if (fromwire_node_announcement(tmpctx, msg, &sig, &features, ×tamp, - &id, rgb_color, alias, &addresses)) + &id, rgb_color, alias, &addresses, + na_tlvs)) return timestamp; errx(1, "Invalid node_announcement"); diff --git a/devtools/mkcommit.c b/devtools/mkcommit.c index 2b1eccaf0816..3abf94cf6f5b 100644 --- a/devtools/mkcommit.c +++ b/devtools/mkcommit.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -271,6 +272,7 @@ int main(int argc, char *argv[]) struct pubkey local_htlc_pubkey, remote_htlc_pubkey; bool option_static_remotekey = false, option_anchor_outputs = false; struct sha256_double hash; + u32 blockheight = 0; setup_locale(); chainparams = chainparams_for_network("bitcoin"); @@ -394,6 +396,9 @@ int main(int argc, char *argv[]) channel = new_full_channel(NULL, &cid, &funding_txid, funding_outnum, 1, + take(new_height_states(NULL, fee_payer, + &blockheight)), + 0, /* Defaults to no lease */ funding_amount, local_msat, take(new_fee_states(NULL, fee_payer, diff --git a/devtools/mkgossip.c b/devtools/mkgossip.c index 63d7b51b8127..5e732e483bf3 100644 --- a/devtools/mkgossip.c +++ b/devtools/mkgossip.c @@ -201,14 +201,17 @@ static void print_nannounce(const struct node_id *nodeid, struct sha256_double hash; secp256k1_ecdsa_signature sig; char alias[33]; + struct tlv_node_ann_tlvs *tlvs; u8 *nannounce; memset(&sig, 0, sizeof(sig)); assert(hex_str_size(sizeof(*nodeid)) >= sizeof(alias)); hex_encode(nodeid, hex_data_size(sizeof(alias)), alias, sizeof(alias)); + tlvs = tlv_node_ann_tlvs_new(NULL); nannounce = towire_node_announcement(NULL, &sig, NULL, opts->timestamp, nodeid, nodeid->k, (u8 *)alias, - opts->addresses); + opts->addresses, + tlvs); sha256_double(&hash, nannounce + node_announcement_offset, tal_count(nannounce) - node_announcement_offset); sign_hash(privkey, &hash, &sig); @@ -221,6 +224,22 @@ static void print_nannounce(const struct node_id *nodeid, printf(" rgb_color=%s\n", tal_hexstr(NULL, nodeid->k, 3)); printf(" alias=%s\n", tal_hexstr(NULL, alias, 32)); printf(" addresses=%s\n", tal_hex(NULL, opts->addresses)); + + if (tlvs->option_will_fund) { + struct lease_rates *rates = tlvs->option_will_fund; + printf(" TLV option_will_fund\n"); + printf(" lease_fee_basis=%d\n", + rates->lease_fee_basis); + printf(" lease_fee_base_sat=%d\n", + rates->lease_fee_base_sat); + printf(" funding_weight=%d\n", + rates->funding_weight); + printf(" channel_fee_max_proportional_thousandths=%d\n", + rates->channel_fee_max_proportional_thousandths); + printf(" channel_fee_max_base_msat=%d\n", + rates->channel_fee_max_base_msat); + } + tal_free(tlvs); } int main(int argc, char *argv[]) diff --git a/doc/Makefile b/doc/Makefile index 30f4804d7168..17afdc3751bc 100644 --- a/doc/Makefile +++ b/doc/Makefile @@ -55,6 +55,7 @@ MANPAGES := doc/lightning-cli.1 \ doc/lightning-openchannel_signed.7 \ doc/lightning-openchannel_update.7 \ doc/lightning-pay.7 \ + doc/lightning-parsefeerate.7 \ doc/lightning-plugin.7 \ doc/lightning-reserveinputs.7 \ doc/lightning-sendinvoice.7 \ diff --git a/doc/PLUGINS.md b/doc/PLUGINS.md index fc0102452fdc..c28a2c110ac6 100644 --- a/doc/PLUGINS.md +++ b/doc/PLUGINS.md @@ -1100,7 +1100,10 @@ the v2 protocol, and it has passed basic sanity checks: "max_accepted_htlcs": 483, "channel_flags": 1 "locktime": 2453, - "channel_max_msat": "16777215000msat" + "channel_max_msat": "16777215000msat", + "requested_lease_msat": "100000000msat", + "lease_blockheight_start": 683990, + "node_blockheight": 683990 } } ``` @@ -1108,6 +1111,10 @@ the v2 protocol, and it has passed basic sanity checks: There may be additional fields, such as `shutdown_scriptpubkey`. You can see the definitions of these fields in [BOLT 2's description of the open_channel message][bolt2-open-channel]. +`requested_lease_msat`, `lease_blockheight_start`, and `node_blockheight` are +only present if the opening peer has requested a funding lease, +per `option_will_fund`. + The returned result must contain a `result` member which is either the string `reject` or `continue`. If `reject` and there's a member `error_message`, that member is sent to the peer diff --git a/doc/index.rst b/doc/index.rst index d89b01aed7d7..d34db0943d3d 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -83,6 +83,7 @@ c-lightning Documentation lightning-openchannel_init lightning-openchannel_signed lightning-openchannel_update + lightning-parsefeerate lightning-pay lightning-ping lightning-plugin diff --git a/doc/lightning-close.7 b/doc/lightning-close.7 index c6313e41716a..4351ae8a69c1 100644 --- a/doc/lightning-close.7 +++ b/doc/lightning-close.7 @@ -3,7 +3,7 @@ lightning-close - Command for closing channels with direct peers .SH SYNOPSIS -\fBclose\fR \fIid\fR [\fIunilateraltimeout\fR] [\fIdestination\fR] [\fIfee_negotiation_step\fR] [\fIwrong_funding\\\fR] +\fBclose\fR \fIid\fR [\fIunilateraltimeout\fR] [\fIdestination\fR] [\fIfee_negotiation_step\fR] [\fIwrong_funding\fR] [\fIforce_lease_closed\fR] .SH DESCRIPTION @@ -64,6 +64,11 @@ allowed if this peer opened the channel and the channel is unused: it can rescue openings which have been manually miscreated\. +\fIforce_lease_closed\fR if the channel has funds leased to the peer +(option_will_fund), we prevent initiation of a mutual close +unless this flag is passed in\. Defaults to false\. + + The peer needs to be live and connected in order to negotiate a mutual close\. The default of unilaterally closing after 48 hours is usually a reasonable indication that you can no longer contact the peer\. @@ -120,4 +125,4 @@ ZmnSCPxj \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:507a9ca707e244eef65c5e16daa5a4d7ba8f59e93e988d252f7e854ae9f44781 +\" SHA256STAMP:c39f743723b34ad987d72c19a71d304c6bdd22db1fe78a137b38c9b20ddf5fad diff --git a/doc/lightning-close.7.md b/doc/lightning-close.7.md index 306598ca7a85..3e16d9449fe7 100644 --- a/doc/lightning-close.7.md +++ b/doc/lightning-close.7.md @@ -4,7 +4,7 @@ lightning-close -- Command for closing channels with direct peers SYNOPSIS -------- -**close** *id* \[*unilateraltimeout*\] \[*destination*\] \[*fee_negotiation_step*\] \[*wrong_funding\*] +**close** *id* \[*unilateraltimeout*\] \[*destination*\] \[*fee_negotiation_step*\] \[*wrong_funding*\] \[*force_lease_closed*\] DESCRIPTION ----------- @@ -53,6 +53,11 @@ shutdown transaction will spend this output instead. This is only allowed if this peer opened the channel and the channel is unused: it can rescue openings which have been manually miscreated. +*force_lease_closed* if the channel has funds leased to the peer +(option_will_fund), we prevent initiation of a mutual close +unless this flag is passed in. Defaults to false. + + The peer needs to be live and connected in order to negotiate a mutual close. The default of unilaterally closing after 48 hours is usually a reasonable indication that you can no longer contact the peer. diff --git a/doc/lightning-feerates.7 b/doc/lightning-feerates.7 index 294aeaf2fc46..0ce3f10cc2df 100644 --- a/doc/lightning-feerates.7 +++ b/doc/lightning-feerates.7 @@ -162,11 +162,11 @@ manpage\. .SH SEE ALSO -\fBlightning-fundchannel\fR(7), \fBlightning-withdraw\fR(7), \fBlightning-txprepare\fR(7), -\fBlightning-fundchannel_start\fR(7)\. +\fBlightning-parsefeerate\fR(7), \fBlightning-fundchannel\fR(7), \fBlightning-withdraw\fR(7), +\fBlightning-txprepare\fR(7), \fBlightning-fundchannel_start\fR(7)\. .SH RESOURCES Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:6e1ae2f26676a76231461ac1f696066e538edabfab00ec9480550a5c6bc6981e +\" SHA256STAMP:bfa51e0f06e7616497c9be3a9f5737bc4378d8118db9e57a3f4efebd112f375a diff --git a/doc/lightning-feerates.7.md b/doc/lightning-feerates.7.md index 79ae753e669e..6f54c387da2e 100644 --- a/doc/lightning-feerates.7.md +++ b/doc/lightning-feerates.7.md @@ -110,8 +110,8 @@ manpage. SEE ALSO -------- -lightning-fundchannel(7), lightning-withdraw(7), lightning-txprepare(7), -lightning-fundchannel_start(7). +lightning-parsefeerate(7), lightning-fundchannel(7), lightning-withdraw(7), +lightning-txprepare(7), lightning-fundchannel_start(7). RESOURCES --------- diff --git a/doc/lightning-fundchannel.7 b/doc/lightning-fundchannel.7 index 69944ba73735..11e8ca275062 100644 --- a/doc/lightning-fundchannel.7 +++ b/doc/lightning-fundchannel.7 @@ -4,7 +4,7 @@ lightning-fundchannel - Command for establishing a lightning channel .SH SYNOPSIS \fBfundchannel\fR \fIid\fR \fIamount\fR [\fIfeerate\fR] [\fIannounce\fR] [\fIminconf\fR] -[\fIutxos\fR] [\fIpush_msat\fR] [\fIclose_to\fR] +[\fIutxos\fR] [\fIpush_msat\fR] [\fIclose_to\fR] [\fIrequest_amt\fR] [\fIcompact_lease\fR] .SH DESCRIPTION @@ -72,6 +72,16 @@ on close\. Only valid if both peers have negotiated \fBoption_upfront_shutdown_s Returns \fBclose_to\fR set to closing script iff is negotiated\. +\fIrequest_amt\fR is an amount of liquidity you'd like to lease from the peer\. +If peer supports \fBoption_will_fund\fR, indicates to them to include this +much liquidity into the channel\. Must also pass in \fIcompact_lease\fR\. + + +\fIcompact_lease\fR is a compact represenation of the peer's expected +channel lease terms\. If the peer's terms don't match this set, we will +fail to open the channel\. + + This example shows how to use lightning-cli to open new channel with peer 03f\.\.\.fc1 from one whole utxo bcc1\.\.\.39c:0 (you can use \fBlistfunds\fR command to get txid and vout): @@ -80,7 +90,6 @@ This example shows how to use lightning-cli to open new channel with peer 03f\.\ lightning-cli -k fundchannel id=03f...fc1 amount=all feerate=normal utxos='["bcc1...39c:0"]' - .RE .fi @@ -130,4 +139,4 @@ channel parameters (funding limits, channel reserves, fees, etc\.)\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:ee8d7d247d9d4f263f8bbed936a2ba4b61d4afc5c48580f462a0d6142c13dbbd +\" SHA256STAMP:4ca7f4d2c07416d6039999a9e1b28a58125d25beb385df543b6ec14974920779 diff --git a/doc/lightning-fundchannel.7.md b/doc/lightning-fundchannel.7.md index deaa3904e8c8..a0c305d1f8e8 100644 --- a/doc/lightning-fundchannel.7.md +++ b/doc/lightning-fundchannel.7.md @@ -5,7 +5,7 @@ SYNOPSIS -------- **fundchannel** *id* *amount* \[*feerate*\] \[*announce*\] \[*minconf*\] -\[*utxos*\] \[*push_msat*\] \[*close_to*\] +\[*utxos*\] \[*push_msat*\] \[*close_to*\] \[*request_amt*\] \[*compact_lease*\] DESCRIPTION ----------- @@ -64,12 +64,21 @@ unrecoverable once pushed. on close. Only valid if both peers have negotiated `option_upfront_shutdown_script`. Returns `close_to` set to closing script iff is negotiated. +*request_amt* is an amount of liquidity you'd like to lease from the peer. +If peer supports `option_will_fund`, indicates to them to include this +much liquidity into the channel. Must also pass in *compact_lease*. + +*compact_lease* is a compact represenation of the peer's expected +channel lease terms. If the peer's terms don't match this set, we will +fail to open the channel. + + + This example shows how to use lightning-cli to open new channel with peer 03f...fc1 from one whole utxo bcc1...39c:0 (you can use **listfunds** command to get txid and vout): lightning-cli -k fundchannel id=03f...fc1 amount=all feerate=normal utxos='["bcc1...39c:0"]' - RETURN VALUE ------------ diff --git a/doc/lightning-multifundchannel.7 b/doc/lightning-multifundchannel.7 index a626ef63f926..717a75bd25b6 100644 --- a/doc/lightning-multifundchannel.7 +++ b/doc/lightning-multifundchannel.7 @@ -58,6 +58,14 @@ out of this\. on close\. Only valid if both peers have negotiated \fBoption_upfront_shutdown_script\fR\. Returns \fBclose_to\fR set to closing script iff is negotiated\. +.IP \[bu] +\fIrequest_amt\fR is the amount of liquidity you'd like to lease from peer\. +If peer supports \fBoption_will_fund\fR, indicates to them to include this +much liquidity into the channel\. Must also pass in \fIcompact_lease\fR\. +.IP \[bu] +\fIcompact_lease\fR is a compact represenation of the peer's expected +channel lease terms\. If the peer's terms don't match this set, we will +fail to open the channel to this destination\. .RE @@ -177,4 +185,4 @@ ZmnSCPxj \fI is mainly responsible\. Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:45015c53c73dc4d84850c84cb753d62739288365746b47f0403fb67e5f65d582 +\" SHA256STAMP:e8bb0dffab6f9d1b5345590c4d267a0778cde2a8fc0e84d1694e73a386da3de4 diff --git a/doc/lightning-multifundchannel.7.md b/doc/lightning-multifundchannel.7.md index 07b7307c8ade..7e1ebe93b1c6 100644 --- a/doc/lightning-multifundchannel.7.md +++ b/doc/lightning-multifundchannel.7.md @@ -51,6 +51,12 @@ Readiness is indicated by **listpeers** reporting a *state* of on close. Only valid if both peers have negotiated `option_upfront_shutdown_script`. Returns `close_to` set to closing script iff is negotiated. +* *request_amt* is the amount of liquidity you'd like to lease from peer. + If peer supports `option_will_fund`, indicates to them to include this + much liquidity into the channel. Must also pass in *compact_lease*. +* *compact_lease* is a compact represenation of the peer's expected + channel lease terms. If the peer's terms don't match this set, we will + fail to open the channel to this destination. There must be at least one entry in *destinations*; it cannot be an empty array. diff --git a/doc/lightning-openchannel_init.7 b/doc/lightning-openchannel_init.7 index a92edbb7b4e0..f6206bf82a50 100644 --- a/doc/lightning-openchannel_init.7 +++ b/doc/lightning-openchannel_init.7 @@ -3,7 +3,7 @@ lightning-openchannel_init - Command to initiate a channel to a peer .SH SYNOPSIS -\fBopenchannel_init\fR \fIid\fR \fIamount\fR \fIinitalpsbt\fR [\fIcommitment_feerate\fR] [\fIfunding_feerate\fR] [\fIannounce\fR] [\fIclose_to\fR] +\fBopenchannel_init\fR \fIid\fR \fIamount\fR \fIinitalpsbt\fR [\fIcommitment_feerate\fR] [\fIfunding_feerate\fR] [\fIannounce\fR] [\fIclose_to\fR] [\fIrequest_amt\fR] [\fIcompact_lease\fR] .SH DESCRIPTION @@ -43,6 +43,16 @@ funding transaction\. Defaults to 'opening' feerate\. sent on close\. Only valid if both peers have negotiated \fBoption_upfront_shutdown_script\fR\. + +\fIrequest_amt\fR is an amount of liquidity you'd like to lease from the peer\. +If peer supports \fBoption_will_fund\fR, indicates to them to include this +much liquidity into the channel\. Must also pass in \fIcompact_lease\fR\. + + +\fIcompact_lease\fR is a compact represenation of the peer's expected +channel lease terms\. If the peer's terms don't match this set, we will +fail to open the channel\. + .SH RETURN VALUE On success, returns the \fIchannel_id\fR for this channel; an updated @@ -57,6 +67,15 @@ If the peer does not support \fBoption_dual_fund\fR, this command will return an error\. +If you sent a \fIrequest_amt\fR and the peer supports \fBoption_will_fund\fR and is +interested in leasing you liquidity in this channel, returns their updated +channel fee max (\fIchannel_fee_proportional_basis\fR, \fIchannel_fee_base_msat\fR), +updated rate card for the lease fee (\fIlease_fee_proportional_basis\fR, +\fIlease_fee_base_sat\fR) and their on-chain weight \fIweight_charge\fR, which will +be added to the lease fee at a rate of \fIfunding_feerate\fR \fI \fRweight_charge* +/ 1000\. + + On error the returned object will contain \fBcode\fR and \fBmessage\fR properties, with \fBcode\fR being one of the following: @@ -99,4 +118,4 @@ lightning-fundchannel_\fBcomplete\fR(7), \fBlightning-fundchannel\fR(7), Main web site: \fIhttps://github.com/ElementsProject/lightning\fR -\" SHA256STAMP:8e37cb8813751e06f59b108e8a335b5b5269e5764c346838a6ddf79138ccea6e +\" SHA256STAMP:441d91dc7a69dd5b85a2b0f0a515aff984cd6882b03badf6318800439347e7c9 diff --git a/doc/lightning-openchannel_init.7.md b/doc/lightning-openchannel_init.7.md index 0385e1acb530..d2f172323cc1 100644 --- a/doc/lightning-openchannel_init.7.md +++ b/doc/lightning-openchannel_init.7.md @@ -4,7 +4,7 @@ lightning-openchannel\_init -- Command to initiate a channel to a peer SYNOPSIS -------- -**openchannel_init** *id* *amount* *initalpsbt* \[*commitment_feerate*\] \[*funding_feerate*\] \[*announce*\] \[*close_to*\] +**openchannel_init** *id* *amount* *initalpsbt* \[*commitment_feerate*\] \[*funding_feerate*\] \[*announce*\] \[*close_to*\] \[*request_amt*\] \[*compact_lease*\] DESCRIPTION ----------- @@ -38,6 +38,14 @@ funding transaction. Defaults to 'opening' feerate. sent on close. Only valid if both peers have negotiated `option_upfront_shutdown_script`. +*request_amt* is an amount of liquidity you'd like to lease from the peer. +If peer supports `option_will_fund`, indicates to them to include this +much liquidity into the channel. Must also pass in *compact_lease*. + +*compact_lease* is a compact represenation of the peer's expected +channel lease terms. If the peer's terms don't match this set, we will +fail to open the channel. + RETURN VALUE ------------ @@ -52,6 +60,14 @@ in the *psbt*. If the peer does not support `option_dual_fund`, this command will return an error. +If you sent a *request_amt* and the peer supports `option_will_fund` and is +interested in leasing you liquidity in this channel, returns their updated +channel fee max (*channel_fee_proportional_basis*, *channel_fee_base_msat*), +updated rate card for the lease fee (*lease_fee_proportional_basis*, +*lease_fee_base_sat*) and their on-chain weight *weight_charge*, which will +be added to the lease fee at a rate of *funding_feerate* * *weight_charge* +/ 1000. + On error the returned object will contain `code` and `message` properties, with `code` being one of the following: diff --git a/doc/lightning-parsefeerate.7 b/doc/lightning-parsefeerate.7 new file mode 100644 index 000000000000..7c4a63b02ac3 --- /dev/null +++ b/doc/lightning-parsefeerate.7 @@ -0,0 +1,44 @@ +.TH "LIGHTNING-PARSEFEERATE" "7" "" "" "lightning-parsefeerate" +.SH NAME +lightning-parsefeerate - Command for parsing a feerate string to a feerate +.SH SYNOPSIS + +\fBparsefeerate\fR \fIfeerate_str\fR + +.SH DESCRIPTION + +The \fBparsefeerate\fR command returns the current feerate for any valid +\fIfeerate_str\fR\. This is useful for finding the current feerate that a +\fBfundpsbt\fR or \fButxopsbt\fR command might use\. + +.SH RETURN VALUE + +On success, an object is returned, containing: + +.RS +.IP \[bu] +\fBperkw\fR (u32, optional): Value of \fIfeerate_str\fR in kilosipa + +.RE +.SH ERRORS + +The \fBparsefeerate\fR command will error if the \fIfeerate_str\fR format is +not recognized\. + +.RS +.IP \[bu] +-32602: If the given parameters are wrong\. + +.RE +.SH TRIVIA + +In C-lightning we like to call the weight unit "sipa" +in honor of Pieter Wuille, +who uses the name "sipa" on IRC and elsewhere\. +Internally we call the \fIperkw\fR style as "feerate per kilosipa"\. + +.SH RESOURCES + +Main web site: \fIhttps://github.com/ElementsProject/lightning\fR + +\" SHA256STAMP:3b79a80f8129d715b3e78778deb85a1839af53efbca8ffb2e67511a8556722c0 diff --git a/doc/lightning-parsefeerate.7.md b/doc/lightning-parsefeerate.7.md new file mode 100644 index 000000000000..31a9da97de6d --- /dev/null +++ b/doc/lightning-parsefeerate.7.md @@ -0,0 +1,45 @@ +lightning-parsefeerate -- Command for parsing a feerate string to a feerate +=========================================================================== + +SYNOPSIS +-------- + +**parsefeerate** *feerate_str* + +DESCRIPTION +----------- + +The **parsefeerate** command returns the current feerate for any valid +*feerate_str*. This is useful for finding the current feerate that a +**fundpsbt** or **utxopsbt** command might use. + +RETURN VALUE +------------ + +[comment]: # (GENERATE-FROM-SCHEMA-START) +On success, an object is returned, containing: +- **perkw** (u32, optional): Value of *feerate_str* in kilosipa +[comment]: # (GENERATE-FROM-SCHEMA-END) + +ERRORS +------ + +The **parsefeerate** command will error if the *feerate_str* format is +not recognized. + +- -32602: If the given parameters are wrong. + +TRIVIA +------ + +In C-lightning we like to call the weight unit "sipa" +in honor of Pieter Wuille, +who uses the name "sipa" on IRC and elsewhere. +Internally we call the *perkw* style as "feerate per kilosipa". + +RESOURCES +--------- + +Main web site: + +[comment]: # ( SHA256STAMP:836154538ca2e2dd986255adc3e71e6047d4fa1d3d9ab9244e7248d10fef0d26) diff --git a/doc/schemas/parsefeerate.schema.json b/doc/schemas/parsefeerate.schema.json new file mode 100644 index 000000000000..0af834cfe3fd --- /dev/null +++ b/doc/schemas/parsefeerate.schema.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "additionalProperties": false, + "required": [ ], + "properties": { + "perkw": { + "type": "u32", + "description": "Value of *feerate_str* in kilosipa", + "additionalProperties": false + } + } +} diff --git a/gossipd/Makefile b/gossipd/Makefile index 7d4f7d70a38b..510ebccf9486 100644 --- a/gossipd/Makefile +++ b/gossipd/Makefile @@ -48,6 +48,7 @@ GOSSIPD_COMMON_OBJS := \ common/status_wiregen.o \ common/gossip_rcvd_filter.o \ common/key_derive.o \ + common/lease_rates.o \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ diff --git a/gossipd/gossip_generation.c b/gossipd/gossip_generation.c index 1baddd1fca37..b3d9ce95006b 100644 --- a/gossipd/gossip_generation.c +++ b/gossipd/gossip_generation.c @@ -25,10 +25,12 @@ * between the dummy creation and the call with a signature. */ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, const secp256k1_ecdsa_signature *sig, - u32 timestamp) + u32 timestamp, + const struct lease_rates *rates) { u8 *addresses = tal_arr(tmpctx, u8, 0); u8 *announcement; + struct tlv_node_ann_tlvs *na_tlv; size_t i; if (!sig) @@ -37,13 +39,17 @@ static u8 *create_node_announcement(const tal_t *ctx, struct daemon *daemon, for (i = 0; i < tal_count(daemon->announcable); i++) towire_wireaddr(&addresses, &daemon->announcable[i]); + na_tlv = tlv_node_ann_tlvs_new(tmpctx); + na_tlv->option_will_fund = cast_const(struct lease_rates *, rates); + announcement = towire_node_announcement(ctx, sig, daemon->our_features->bits [NODE_ANNOUNCE_FEATURE], timestamp, &daemon->id, daemon->rgb, daemon->alias, - addresses); + addresses, + na_tlv); return announcement; } @@ -89,13 +95,14 @@ bool cupdate_different(struct gossip_store *gs, || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]); } -/* Get non-signature, non-timestamp parts of (valid!) node_announcement */ +/* Get non-signature, non-timestamp parts of (valid!) node_announcement, + * with TLV broken out separately */ static void get_nannounce_parts(const u8 *node_announcement, - const u8 *parts[2], - size_t sizes[2]) + const u8 *parts[3], + size_t sizes[3]) { - size_t len; - const u8 *flen; + size_t len, ad_len; + const u8 *flen, *ad_start; /* BOLT #7: * @@ -118,17 +125,43 @@ static void get_nannounce_parts(const u8 *node_announcement, sizes[0] = 2 + fromwire_u16(&flen, &len); assert(flen != NULL && len >= 4); + /* BOLT-0fe3485a5320efaa2be8cfa0e570ad4d0259cec3 #7: + * + * * [`u32`:`timestamp`] + * * [`point`:`node_id`] + * * [`3*byte`:`rgb_color`] + * * [`32*byte`:`alias`] + * * [`u16`:`addrlen`] + * * [`addrlen*byte`:`addresses`] + * * [`node_ann_tlvs`:`tlvs`] + */ parts[1] = node_announcement + 2 + 64 + sizes[0] + 4; - sizes[1] = tal_count(node_announcement) - (2 + 64 + sizes[0] + 4); + + /* Find the end of the addresses */ + ad_start = parts[1] + 33 + 3 + 32; + len = tal_count(node_announcement) + - (2 + 64 + sizes[0] + 4 + 33 + 3 + 32); + ad_len = fromwire_u16(&ad_start, &len); + assert(ad_start != NULL && len >= ad_len); + + sizes[1] = 33 + 3 + 32 + 2 + ad_len; + + /* Is there a TLV ? */ + sizes[2] = len - ad_len; + if (sizes[2] != 0) + parts[2] = parts[1] + sizes[1]; + else + parts[2] = NULL; } /* Is this node_announcement different from prev (not sigs and timestamps)? */ bool nannounce_different(struct gossip_store *gs, const struct node *node, - const u8 *nannounce) + const u8 *nannounce, + bool *only_missing_tlv) { - const u8 *oparts[2], *nparts[2]; - size_t osizes[2], nsizes[2]; + const u8 *oparts[3], *nparts[3]; + size_t osizes[3], nsizes[3]; const u8 *orig; /* Get last one we have. */ @@ -136,8 +169,44 @@ bool nannounce_different(struct gossip_store *gs, get_nannounce_parts(orig, oparts, osizes); get_nannounce_parts(nannounce, nparts, nsizes); + *only_missing_tlv = memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) + && memeq(oparts[1], osizes[1], nparts[1], nsizes[1]) + && !memeq(oparts[2], osizes[2], nparts[2], nsizes[2]); + return !memeq(oparts[0], osizes[0], nparts[0], nsizes[0]) - || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]); + || !memeq(oparts[1], osizes[1], nparts[1], nsizes[1]) + || !memeq(oparts[2], osizes[2], nparts[2], nsizes[2]); +} + +static void sign_and_send_nannounce(struct daemon *daemon, + u8 *nannounce, + u32 timestamp) +{ + secp256k1_ecdsa_signature sig; + u8 *msg, *err; + + /* Ask hsmd to sign it (synchronous) */ + if (!wire_sync_write(HSM_FD, take(towire_hsmd_node_announcement_sig_req(NULL, nannounce)))) + status_failed(STATUS_FAIL_MASTER_IO, "Could not write to HSM: %s", strerror(errno)); + + msg = wire_sync_read(tmpctx, HSM_FD); + if (!fromwire_hsmd_node_announcement_sig_reply(msg, &sig)) + status_failed(STATUS_FAIL_MASTER_IO, "HSM returned an invalid node_announcement sig"); + + /* We got the signature for our provisional node_announcement back + * from the HSM, create the real announcement and forward it to + * gossipd so it can take care of forwarding it. */ + nannounce = create_node_announcement(NULL, daemon, &sig, + timestamp, daemon->rates); + + /* This injects it into the routing code in routing.c; it should not + * reject it! */ + err = handle_node_announcement(daemon->rstate, take(nannounce), + NULL, NULL); + if (err) + status_failed(STATUS_FAIL_INTERNAL_ERROR, + "rejected own node announcement: %s", + tal_hex(tmpctx, err)); } /* This routine created a `node_announcement` for our node, and hands it to @@ -147,8 +216,7 @@ bool nannounce_different(struct gossip_store *gs, static void update_own_node_announcement(struct daemon *daemon) { u32 timestamp = gossip_time_now(daemon->rstate).ts.tv_sec; - secp256k1_ecdsa_signature sig; - u8 *msg, *nannounce, *err; + u8 *nannounce; struct node *self = get_node(daemon->rstate, &daemon->id); /* Discard existing timer. */ @@ -161,13 +229,17 @@ static void update_own_node_announcement(struct daemon *daemon) timestamp++; /* Make unsigned announcement. */ - nannounce = create_node_announcement(tmpctx, daemon, NULL, timestamp); + nannounce = create_node_announcement(tmpctx, daemon, NULL, + timestamp, + daemon->rates); /* If it's the same as the previous, nothing to do. */ if (self && self->bcast.index) { u32 next; + bool only_missing_tlv; - if (!nannounce_different(daemon->rstate->gs, self, nannounce)) + if (!nannounce_different(daemon->rstate->gs, self, nannounce, + &only_missing_tlv)) return; /* BOLT #7: @@ -183,6 +255,7 @@ static void update_own_node_announcement(struct daemon *daemon) if (timestamp < next) { status_debug("node_announcement: delaying %u secs", next - timestamp); + daemon->node_announce_timer = new_reltimer(&daemon->timers, daemon, @@ -193,31 +266,83 @@ static void update_own_node_announcement(struct daemon *daemon) } } - /* Ask hsmd to sign it (synchronous) */ - if (!wire_sync_write(HSM_FD, take(towire_hsmd_node_announcement_sig_req(NULL, nannounce)))) - status_failed(STATUS_FAIL_MASTER_IO, "Could not write to HSM: %s", strerror(errno)); + sign_and_send_nannounce(daemon, nannounce, timestamp); +} - msg = wire_sync_read(tmpctx, HSM_FD); - if (!fromwire_hsmd_node_announcement_sig_reply(msg, &sig)) - status_failed(STATUS_FAIL_MASTER_IO, "HSM returned an invalid node_announcement sig"); +static void update_own_node_announce_startup(struct daemon *daemon) +{ + u32 timestamp = gossip_time_now(daemon->rstate).ts.tv_sec; + u8 *nannounce; + struct node *self = get_node(daemon->rstate, &daemon->id); - /* We got the signature for our provisional node_announcement back - * from the HSM, create the real announcement and forward it to - * gossipd so it can take care of forwarding it. */ - nannounce = create_node_announcement(NULL, daemon, &sig, timestamp); + /* Discard existing timer. */ + daemon->node_announce_timer = tal_free(daemon->node_announce_timer); - /* This injects it into the routing code in routing.c; it should not - * reject it! */ - err = handle_node_announcement(daemon->rstate, take(nannounce), - NULL, NULL); - if (err) - status_failed(STATUS_FAIL_INTERNAL_ERROR, - "rejected own node announcement: %s", - tal_hex(tmpctx, err)); + /* If we ever use set-based propagation, ensuring the toggle the lower + * bit in consecutive timestamps makes it more robust. */ + if (self && self->bcast.index + && (timestamp & 1) == (self->bcast.timestamp & 1)) + timestamp++; + + /* Make unsigned announcement. */ + nannounce = create_node_announcement(tmpctx, daemon, NULL, + timestamp, + daemon->rates); + + + /* If it's the same as the previous, nothing to do. */ + if (self && self->bcast.index) { + u32 next; + bool only_missing_tlv; + + if (!nannounce_different(daemon->rstate->gs, self, nannounce, + &only_missing_tlv)) + return; + + /* Missing liquidity_ad, maybe we'll get plugin callback */ + if (only_missing_tlv) { + u32 delay = GOSSIP_NANN_STARTUP_DELAY(daemon->rstate->dev_fast_gossip); + status_debug("node_announcement: delaying" + " %u secs at start", delay); + + daemon->node_announce_timer + = new_reltimer(&daemon->timers, + daemon, + time_from_sec(delay), + update_own_node_announcement, + daemon); + return; + } + + /* BOLT #7: + * + * The origin node: + * - MUST set `timestamp` to be greater than that of any + * previous `node_announcement` it has previously created. + */ + /* We do better: never send them within more than 5 minutes. */ + next = self->bcast.timestamp + + GOSSIP_MIN_INTERVAL(daemon->rstate->dev_fast_gossip); + + if (timestamp < next) { + status_debug("node_announcement: delaying %u secs", + next - timestamp); + + daemon->node_announce_timer + = new_reltimer(&daemon->timers, + daemon, + time_from_sec(next - timestamp), + update_own_node_announcement, + daemon); + return; + } + } + + sign_and_send_nannounce(daemon, nannounce, timestamp); } /* Should we announce our own node? Called at strategic places. */ -void maybe_send_own_node_announce(struct daemon *daemon) +void maybe_send_own_node_announce(struct daemon *daemon, bool startup) { /* We keep an internal flag in the routing code to say we've announced * a local channel. The alternative would be to have it make a @@ -226,8 +351,10 @@ void maybe_send_own_node_announce(struct daemon *daemon) if (!daemon->rstate->local_channel_announced) return; - update_own_node_announcement(daemon); - daemon->rstate->local_channel_announced = false; + if (startup) + update_own_node_announce_startup(daemon); + else + update_own_node_announcement(daemon); } /* Our timer callbacks take a single argument, so we marshall everything diff --git a/gossipd/gossip_generation.h b/gossipd/gossip_generation.h index 5c4857661384..d58f5151e56b 100644 --- a/gossipd/gossip_generation.h +++ b/gossipd/gossip_generation.h @@ -29,10 +29,11 @@ bool cupdate_different(struct gossip_store *gs, * node->bcast.index must be non-zero! */ bool nannounce_different(struct gossip_store *gs, const struct node *node, - const u8 *nannounce); + const u8 *nannounce, + bool *only_missing_tlv); /* Should we announce our own node? Called at strategic places. */ -void maybe_send_own_node_announce(struct daemon *daemon); +void maybe_send_own_node_announce(struct daemon *daemon, bool startup); /* This is a refresh of a local channel: sends an update if one is needed. */ void refresh_local_channel(struct daemon *daemon, diff --git a/gossipd/gossip_store_wiregen.c b/gossipd/gossip_store_wiregen.c index 90cbd0b07a7b..bf8dc913ff5b 100644 --- a/gossipd/gossip_store_wiregen.c +++ b/gossipd/gossip_store_wiregen.c @@ -210,4 +210,4 @@ bool fromwire_gossipd_local_add_channel_obs(const tal_t *ctx, const void *p, str fromwire_u8_array(&cursor, &plen, *features, flen); return cursor != NULL; } -// SHA256STAMP:3e6e23b99855a3be9305cbc297d59d818cc193d6ebe5c2ca78dfb6ec5df31e94 +// SHA256STAMP:dd17fbe3d5e894679c6681818fb81b5407d19c8eb34d863aeda5cd46500bd956 diff --git a/gossipd/gossip_store_wiregen.h b/gossipd/gossip_store_wiregen.h index 0941d51e6151..4d1b612d6496 100644 --- a/gossipd/gossip_store_wiregen.h +++ b/gossipd/gossip_store_wiregen.h @@ -63,4 +63,4 @@ bool fromwire_gossipd_local_add_channel_obs(const tal_t *ctx, const void *p, str #endif /* LIGHTNING_GOSSIPD_GOSSIP_STORE_WIREGEN_H */ -// SHA256STAMP:3e6e23b99855a3be9305cbc297d59d818cc193d6ebe5c2ca78dfb6ec5df31e94 +// SHA256STAMP:dd17fbe3d5e894679c6681818fb81b5407d19c8eb34d863aeda5cd46500bd956 diff --git a/gossipd/gossipd.c b/gossipd/gossipd.c index 5619b5ef7b3d..36738ab315a0 100644 --- a/gossipd/gossipd.c +++ b/gossipd/gossipd.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -157,13 +158,15 @@ static bool get_node_announcement(const tal_t *ctx, u8 rgb_color[3], u8 alias[32], u8 **features, - struct wireaddr **wireaddrs) + struct wireaddr **wireaddrs, + struct lease_rates **rates) { const u8 *msg; struct node_id id; secp256k1_ecdsa_signature signature; u32 timestamp; u8 *addresses; + struct tlv_node_ann_tlvs *na_tlvs; if (!n->bcast.index) return false; @@ -171,11 +174,13 @@ static bool get_node_announcement(const tal_t *ctx, msg = gossip_store_get(tmpctx, daemon->rstate->gs, n->bcast.index); /* Note: validity of node_id is already checked. */ + na_tlvs = tlv_node_ann_tlvs_new(ctx); if (!fromwire_node_announcement(ctx, msg, &signature, features, ×tamp, &id, rgb_color, alias, - &addresses)) { + &addresses, + na_tlvs)) { status_broken("Bad local node_announcement @%u: %s", n->bcast.index, tal_hex(tmpctx, msg)); return false; @@ -194,6 +199,9 @@ static bool get_node_announcement(const tal_t *ctx, } *wireaddrs = read_addresses(ctx, addresses); + + *rates = tal_steal(ctx, na_tlvs->option_will_fund); + tal_free(addresses); return true; } @@ -205,14 +213,15 @@ static bool get_node_announcement_by_id(const tal_t *ctx, u8 rgb_color[3], u8 alias[32], u8 **features, - struct wireaddr **wireaddrs) + struct wireaddr **wireaddrs, + struct lease_rates **rates) { struct node *n = get_node(daemon->rstate, node_id); if (!n) return false; return get_node_announcement(ctx, daemon, n, rgb_color, alias, - features, wireaddrs); + features, wireaddrs, rates); } /*~Routines to handle gossip messages from peer, forwarded by subdaemons. @@ -281,7 +290,7 @@ static u8 *handle_channel_update_msg(struct peer *peer, const u8 *msg) * routing until you have both anyway. For this reason, we might have * just sent out our own channel_announce, so we check if it's time to * send a node_announcement too. */ - maybe_send_own_node_announce(peer->daemon); + maybe_send_own_node_announce(peer->daemon, false); return NULL; } @@ -728,6 +737,7 @@ static struct io_plan *peer_msg_in(struct io_conn *conn, case WIRE_COMMITMENT_SIGNED: case WIRE_REVOKE_AND_ACK: case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: @@ -918,6 +928,7 @@ static struct io_plan *connectd_get_address(struct io_conn *conn, u8 alias[32]; u8 *features; struct wireaddr *addrs; + struct lease_rates *rates; if (!fromwire_gossipd_get_addrs(msg, &id)) { status_broken("Bad gossipd_get_addrs msg from connectd: %s", @@ -926,7 +937,8 @@ static struct io_plan *connectd_get_address(struct io_conn *conn, } if (!get_node_announcement_by_id(tmpctx, daemon, &id, - rgb_color, alias, &features, &addrs)) + rgb_color, alias, &features, &addrs, + &rates)) addrs = NULL; daemon_conn_send(daemon->connectd, @@ -1121,7 +1133,7 @@ static struct io_plan *gossip_init(struct io_conn *conn, /* If that announced channels, we can announce ourselves (options * or addresses might have changed!) */ - maybe_send_own_node_announce(daemon); + maybe_send_own_node_announce(daemon, true); /* Start the twice- weekly refresh timer. */ notleak(new_reltimer(&daemon->timers, daemon, @@ -1358,7 +1370,8 @@ static void add_node_entry(const tal_t *ctx, if (get_node_announcement(ctx, daemon, n, e->color, e->alias, &e->features, - &e->addresses)) { + &e->addresses, + &e->rates)) { e->last_timestamp = n->bcast.timestamp; } else { /* Timestamp on wire is an unsigned 32 bit: we use a 64-bit @@ -1698,7 +1711,7 @@ static struct io_plan *handle_txout_reply(struct io_conn *conn, /* Anywhere we might have announced a channel, we check if it's time to * announce ourselves (ie. if we just announced our own first channel) */ - maybe_send_own_node_announce(daemon); + maybe_send_own_node_announce(daemon, false); return daemon_conn_read_next(conn, daemon->master); } @@ -1750,6 +1763,27 @@ static struct io_plan *inject_gossip(struct io_conn *conn, return daemon_conn_read_next(conn, daemon->master); } +static struct io_plan *handle_new_lease_rates(struct io_conn *conn, + struct daemon *daemon, + const u8 *msg) +{ + struct lease_rates *rates = tal(daemon, struct lease_rates); + + if (!fromwire_gossipd_new_lease_rates(msg, rates)) + master_badmsg(WIRE_GOSSIPD_NEW_LEASE_RATES, msg); + + daemon->rates = tal_free(daemon->rates); + if (!lease_rates_empty(rates)) + daemon->rates = rates; + else + tal_free(rates); + + /* Send the update over to the peer */ + maybe_send_own_node_announce(daemon, false); + + return daemon_conn_read_next(conn, daemon->master); +} + /*~ This is where lightningd tells us that a channel's funding transaction has * been spent. */ static struct io_plan *handle_outpoint_spent(struct io_conn *conn, @@ -1849,6 +1883,9 @@ static struct io_plan *recv_req(struct io_conn *conn, case WIRE_GOSSIPD_ADDGOSSIP: return inject_gossip(conn, daemon, msg); + case WIRE_GOSSIPD_NEW_LEASE_RATES: + return handle_new_lease_rates(conn, daemon, msg); + #if DEVELOPER case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: return dev_set_max_scids_encode_size(conn, daemon, msg); @@ -1914,6 +1951,7 @@ int main(int argc, char *argv[]) daemon->deferred_txouts = tal_arr(daemon, struct short_channel_id, 0); daemon->node_announce_timer = NULL; daemon->current_blockheight = 0; /* i.e. unknown */ + daemon->rates = NULL; /* Tell the ecdh() function how to talk to hsmd */ ecdh_hsmd_setup(HSM_FD, status_failed); diff --git a/gossipd/gossipd.h b/gossipd/gossipd.h index ab01b53aaccc..867c3ef802f9 100644 --- a/gossipd/gossipd.h +++ b/gossipd/gossipd.h @@ -17,6 +17,7 @@ struct chan; struct channel_update_timestamps; struct broadcastable; +struct lease_rates; struct seeker; /*~ The core daemon structure: */ @@ -60,6 +61,9 @@ struct daemon { /* Features lightningd told us to set. */ struct feature_set *our_features; + + /* The channel lease rates we're advertising */ + struct lease_rates *rates; }; struct range_query_reply { diff --git a/gossipd/gossipd_peerd_wiregen.c b/gossipd/gossipd_peerd_wiregen.c index a6ff50636ec1..1ca000e06071 100644 --- a/gossipd/gossipd_peerd_wiregen.c +++ b/gossipd/gossipd_peerd_wiregen.c @@ -161,4 +161,4 @@ bool fromwire_gossipd_local_channel_announcement(const tal_t *ctx, const void *p fromwire_u8_array(&cursor, &plen, *cannount, len); return cursor != NULL; } -// SHA256STAMP:e55284452718ed1baf12a38736b4bfeecc8bb18dac8ad4f0ee0b5dc8904fbdc2 +// SHA256STAMP:844e57181b9d9fae198f9eb0c4de8d9742a03194eea89058641c24670a5fb935 diff --git a/gossipd/gossipd_peerd_wiregen.h b/gossipd/gossipd_peerd_wiregen.h index 8240e6616c78..ebc9d57fbbac 100644 --- a/gossipd/gossipd_peerd_wiregen.h +++ b/gossipd/gossipd_peerd_wiregen.h @@ -57,4 +57,4 @@ bool fromwire_gossipd_local_channel_announcement(const tal_t *ctx, const void *p #endif /* LIGHTNING_GOSSIPD_GOSSIPD_PEERD_WIREGEN_H */ -// SHA256STAMP:e55284452718ed1baf12a38736b4bfeecc8bb18dac8ad4f0ee0b5dc8904fbdc2 +// SHA256STAMP:844e57181b9d9fae198f9eb0c4de8d9742a03194eea89058641c24670a5fb935 diff --git a/gossipd/gossipd_wire.csv b/gossipd/gossipd_wire.csv index f9b6e0f61939..83b4f2595a0d 100644 --- a/gossipd/gossipd_wire.csv +++ b/gossipd/gossipd_wire.csv @@ -1,6 +1,7 @@ #include #include #include +#include #include # Initialize the gossip daemon. @@ -166,3 +167,6 @@ msgdata,gossipd_addgossip,msg,u8,len msgtype,gossipd_addgossip_reply,3144 msgdata,gossipd_addgossip_reply,err,wirestring, +# Updated lease rates available +msgtype,gossipd_new_lease_rates,3046 +msgdata,gossipd_new_lease_rates,rates,lease_rates, diff --git a/gossipd/gossipd_wiregen.c b/gossipd/gossipd_wiregen.c index 6e86ca01e0e8..3237473361b9 100644 --- a/gossipd/gossipd_wiregen.c +++ b/gossipd/gossipd_wiregen.c @@ -50,6 +50,7 @@ const char *gossipd_wire_name(int e) case WIRE_GOSSIPD_SEND_ONIONMSG: return "WIRE_GOSSIPD_SEND_ONIONMSG"; case WIRE_GOSSIPD_ADDGOSSIP: return "WIRE_GOSSIPD_ADDGOSSIP"; case WIRE_GOSSIPD_ADDGOSSIP_REPLY: return "WIRE_GOSSIPD_ADDGOSSIP_REPLY"; + case WIRE_GOSSIPD_NEW_LEASE_RATES: return "WIRE_GOSSIPD_NEW_LEASE_RATES"; } snprintf(invalidbuf, sizeof(invalidbuf), "INVALID %i", e); @@ -89,6 +90,7 @@ bool gossipd_wire_is_defined(u16 type) case WIRE_GOSSIPD_SEND_ONIONMSG:; case WIRE_GOSSIPD_ADDGOSSIP:; case WIRE_GOSSIPD_ADDGOSSIP_REPLY:; + case WIRE_GOSSIPD_NEW_LEASE_RATES:; return true; } return false; @@ -1057,4 +1059,26 @@ bool fromwire_gossipd_addgossip_reply(const tal_t *ctx, const void *p, wirestrin *err = fromwire_wirestring(ctx, &cursor, &plen); return cursor != NULL; } -// SHA256STAMP:a0d7494995d7f95fb7df295bab9d865e18670f15243116a0aaa9b9548534b922 + +/* WIRE: GOSSIPD_NEW_LEASE_RATES */ +/* Updated lease rates available */ +u8 *towire_gossipd_new_lease_rates(const tal_t *ctx, const struct lease_rates *rates) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_GOSSIPD_NEW_LEASE_RATES); + towire_lease_rates(&p, rates); + + return memcheck(p, tal_count(p)); +} +bool fromwire_gossipd_new_lease_rates(const void *p, struct lease_rates *rates) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_GOSSIPD_NEW_LEASE_RATES) + return false; + fromwire_lease_rates(&cursor, &plen, rates); + return cursor != NULL; +} +// SHA256STAMP:85cc676dac833ef56e79962024738ff836a924e8d2dcbdd9557eb21c86ffd3dd diff --git a/gossipd/gossipd_wiregen.h b/gossipd/gossipd_wiregen.h index 0e989c517672..2ab53ad7162a 100644 --- a/gossipd/gossipd_wiregen.h +++ b/gossipd/gossipd_wiregen.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -67,6 +68,8 @@ enum gossipd_wire { WIRE_GOSSIPD_ADDGOSSIP = 3044, /* Empty string means no problem. */ WIRE_GOSSIPD_ADDGOSSIP_REPLY = 3144, + /* Updated lease rates available */ + WIRE_GOSSIPD_NEW_LEASE_RATES = 3046, }; const char *gossipd_wire_name(int e); @@ -223,6 +226,11 @@ bool fromwire_gossipd_addgossip(const tal_t *ctx, const void *p, u8 **msg); u8 *towire_gossipd_addgossip_reply(const tal_t *ctx, const wirestring *err); bool fromwire_gossipd_addgossip_reply(const tal_t *ctx, const void *p, wirestring **err); +/* WIRE: GOSSIPD_NEW_LEASE_RATES */ +/* Updated lease rates available */ +u8 *towire_gossipd_new_lease_rates(const tal_t *ctx, const struct lease_rates *rates); +bool fromwire_gossipd_new_lease_rates(const void *p, struct lease_rates *rates); + #endif /* LIGHTNING_GOSSIPD_GOSSIPD_WIREGEN_H */ -// SHA256STAMP:a0d7494995d7f95fb7df295bab9d865e18670f15243116a0aaa9b9548534b922 +// SHA256STAMP:85cc676dac833ef56e79962024738ff836a924e8d2dcbdd9557eb21c86ffd3dd diff --git a/gossipd/routing.c b/gossipd/routing.c index 321ce14ba1cf..a64071d188f1 100644 --- a/gossipd/routing.c +++ b/gossipd/routing.c @@ -2427,6 +2427,7 @@ bool routing_add_node_announcement(struct routing_state *rstate, u8 rgb_color[3]; u8 alias[32]; u8 *features, *addresses; + struct tlv_node_ann_tlvs *na_tlv; if (was_unknown) *was_unknown = false; @@ -2436,10 +2437,12 @@ bool routing_add_node_announcement(struct routing_state *rstate, tal_steal(tmpctx, msg); /* Note: validity of node_id is already checked. */ + na_tlv = tlv_node_ann_tlvs_new(tmpctx); if (!fromwire_node_announcement(tmpctx, msg, &signature, &features, ×tamp, &node_id, rgb_color, alias, - &addresses)) { + &addresses, + na_tlv)) { return false; } @@ -2482,6 +2485,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, } if (node->bcast.index) { + bool only_tlv_diff; + if (index != 0) { status_broken("gossip_store node_announcement %u replaces %u!", index, node->bcast.index); @@ -2496,7 +2501,8 @@ bool routing_add_node_announcement(struct routing_state *rstate, /* Allow redundant updates once every 7 days */ if (timestamp < node->bcast.timestamp + GOSSIP_PRUNE_INTERVAL(rstate->dev_fast_gossip_prune) / 2 - && !nannounce_different(rstate->gs, node, msg)) { + && !nannounce_different(rstate->gs, node, msg, + &only_tlv_diff)) { SUPERVERBOSE( "Ignoring redundant nannounce for %s" " (last %u, now %u)", @@ -2572,15 +2578,18 @@ u8 *handle_node_announcement(struct routing_state *rstate, const u8 *node_ann, u8 *features, *addresses; struct wireaddr *wireaddrs; size_t len = tal_count(node_ann); + struct tlv_node_ann_tlvs *na_tlv; if (was_unknown) *was_unknown = false; serialized = tal_dup_arr(tmpctx, u8, node_ann, len, 0); + na_tlv = tlv_node_ann_tlvs_new(tmpctx); if (!fromwire_node_announcement(tmpctx, serialized, &signature, &features, ×tamp, &node_id, rgb_color, alias, - &addresses)) { + &addresses, + na_tlv)) { /* BOLT #7: * * - if `node_id` is NOT a valid compressed public key: diff --git a/gossipd/test/Makefile b/gossipd/test/Makefile index b9f0949115d8..28aaed2ce3d3 100644 --- a/gossipd/test/Makefile +++ b/gossipd/test/Makefile @@ -14,6 +14,7 @@ GOSSIPD_TEST_COMMON_OBJS := \ common/node_id.o \ common/json.o \ common/json_helpers.o \ + common/lease_rates.o \ common/pseudorand.o \ common/setup.o \ common/type_to_string.o \ diff --git a/gossipd/test/run-bench-find_route.c b/gossipd/test/run-bench-find_route.c index 0772b7d2348f..661f08bc51d0 100644 --- a/gossipd/test/run-bench-find_route.c +++ b/gossipd/test/run-bench-find_route.c @@ -64,7 +64,8 @@ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memt /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) + const u8 *nannounce UNNEEDED, + bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) diff --git a/gossipd/test/run-check_channel_announcement.c b/gossipd/test/run-check_channel_announcement.c index cc77a3414819..76de7a36a164 100644 --- a/gossipd/test/run-check_channel_announcement.c +++ b/gossipd/test/run-check_channel_announcement.c @@ -103,7 +103,8 @@ void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intma /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) + const u8 *nannounce UNNEEDED, + bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, diff --git a/gossipd/test/run-check_node_announcement.c b/gossipd/test/run-check_node_announcement.c new file mode 100644 index 000000000000..5017ce3d14c2 --- /dev/null +++ b/gossipd/test/run-check_node_announcement.c @@ -0,0 +1,147 @@ +#include "../gossip_generation.c" +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for find_peer */ +struct peer *find_peer(struct daemon *daemon UNNEEDED, const struct node_id *id UNNEEDED) +{ fprintf(stderr, "find_peer called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire_gossipd_local_channel_update */ +bool fromwire_gossipd_local_channel_update(const void *p UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED, bool *disable UNNEEDED, u16 *cltv_expiry_delta UNNEEDED, struct amount_msat *htlc_minimum_msat UNNEEDED, u32 *fee_base_msat UNNEEDED, u32 *fee_proportional_millionths UNNEEDED, struct amount_msat *htlc_maximum_msat UNNEEDED) +{ fprintf(stderr, "fromwire_gossipd_local_channel_update called!\n"); abort(); } +/* Generated stub for fromwire_hsmd_cupdate_sig_reply */ +bool fromwire_hsmd_cupdate_sig_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u8 **cu UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_cupdate_sig_reply called!\n"); abort(); } +/* Generated stub for fromwire_hsmd_node_announcement_sig_reply */ +bool fromwire_hsmd_node_announcement_sig_reply(const void *p UNNEEDED, secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_hsmd_node_announcement_sig_reply called!\n"); abort(); } +/* Generated stub for get_node */ +struct node *get_node(struct routing_state *rstate UNNEEDED, + const struct node_id *id UNNEEDED) +{ fprintf(stderr, "get_node called!\n"); abort(); } +/* Generated stub for gossip_time_now */ +struct timeabs gossip_time_now(const struct routing_state *rstate UNNEEDED) +{ fprintf(stderr, "gossip_time_now called!\n"); abort(); } +/* Generated stub for handle_channel_update */ +u8 *handle_channel_update(struct routing_state *rstate UNNEEDED, const u8 *update TAKES UNNEEDED, + struct peer *peer UNNEEDED, + struct short_channel_id *unknown_scid UNNEEDED, + bool force UNNEEDED) +{ fprintf(stderr, "handle_channel_update called!\n"); abort(); } +/* Generated stub for handle_node_announcement */ +u8 *handle_node_announcement(struct routing_state *rstate UNNEEDED, const u8 *node UNNEEDED, + struct peer *peer UNNEEDED, bool *was_unknown UNNEEDED) +{ fprintf(stderr, "handle_node_announcement called!\n"); abort(); } +/* Generated stub for json_add_member */ +void json_add_member(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, + bool quote UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "json_add_member called!\n"); abort(); } +/* Generated stub for json_member_direct */ +char *json_member_direct(struct json_stream *js UNNEEDED, + const char *fieldname UNNEEDED, size_t extra UNNEEDED) +{ fprintf(stderr, "json_member_direct called!\n"); abort(); } +/* Generated stub for json_object_end */ +void json_object_end(struct json_stream *js UNNEEDED) +{ fprintf(stderr, "json_object_end called!\n"); abort(); } +/* Generated stub for json_object_start */ +void json_object_start(struct json_stream *ks UNNEEDED, const char *fieldname UNNEEDED) +{ fprintf(stderr, "json_object_start called!\n"); abort(); } +/* Generated stub for new_reltimer_ */ +struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, + const tal_t *ctx UNNEEDED, + struct timerel expire UNNEEDED, + void (*cb)(void *) UNNEEDED, void *arg UNNEEDED) +{ fprintf(stderr, "new_reltimer_ called!\n"); abort(); } +/* Generated stub for notleak_ */ +void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) +{ fprintf(stderr, "notleak_ called!\n"); abort(); } +/* Generated stub for queue_peer_msg */ +void queue_peer_msg(struct peer *peer UNNEEDED, const u8 *msg TAKES UNNEEDED) +{ fprintf(stderr, "queue_peer_msg called!\n"); abort(); } +/* Generated stub for status_failed */ +void status_failed(enum status_failreason code UNNEEDED, + const char *fmt UNNEEDED, ...) +{ fprintf(stderr, "status_failed called!\n"); abort(); } +/* Generated stub for status_fmt */ +void status_fmt(enum log_level level UNNEEDED, + const struct node_id *peer UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "status_fmt called!\n"); abort(); } +/* Generated stub for towire_hsmd_cupdate_sig_req */ +u8 *towire_hsmd_cupdate_sig_req(const tal_t *ctx UNNEEDED, const u8 *cu UNNEEDED) +{ fprintf(stderr, "towire_hsmd_cupdate_sig_req called!\n"); abort(); } +/* Generated stub for towire_hsmd_node_announcement_sig_req */ +u8 *towire_hsmd_node_announcement_sig_req(const tal_t *ctx UNNEEDED, const u8 *announcement UNNEEDED) +{ fprintf(stderr, "towire_hsmd_node_announcement_sig_req called!\n"); abort(); } +/* Generated stub for towire_wireaddr */ +void towire_wireaddr(u8 **pptr UNNEEDED, const struct wireaddr *addr UNNEEDED) +{ fprintf(stderr, "towire_wireaddr called!\n"); abort(); } +/* Generated stub for wire_sync_read */ +u8 *wire_sync_read(const tal_t *ctx UNNEEDED, int fd UNNEEDED) +{ fprintf(stderr, "wire_sync_read called!\n"); abort(); } +/* Generated stub for wire_sync_write */ +bool wire_sync_write(int fd UNNEEDED, const void *msg TAKES UNNEEDED) +{ fprintf(stderr, "wire_sync_write called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +/* Overwriting this to return test data */ +const u8 *gossip_store_get(const tal_t *ctx, + struct gossip_store *gs, + u64 offset) +{ + /* No TLV, different */ + if (offset == 0) + return tal_hexdata(ctx, "01010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078000000802aaa260b145790266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000", strlen("01010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000078000000802aaa260b145790266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000")); + + /* No TLV, same */ + if (offset == 1) + return tal_hexdata(ctx, "01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000", strlen("01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000")); + + /* Same, with TLV */ + if (offset == 2) + return tal_hexdata(ctx, "0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0", strlen("0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0")); + + return NULL; +} + +int main(int argc, char *argv[]) +{ + struct node node; + bool only_missing_tlv; + u8 *ann; + + common_setup(argv[0]); + + /* No TLV checks */ + node.bcast.index = 1; + ann = tal_hexdata(tmpctx, "01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000", strlen("01017d49b51b7d3772636c09901df78c81faa1a7f045e329366ad779ecbaf0cc07f764d8ffd3596ef6802fd0e4c5c180c74fb3dfb14aae493ed48d35a3df75c20eca00078000000802aaa260b14569022d223620a359a47ff7f7ac447c85c46c923da53389221a0054c11c1e3ca31d59022d2253494c454e544152544953542d3236352d67373833393533312d6d6f646465640000")); + assert(!nannounce_different(NULL, &node, ann, &only_missing_tlv)); + assert(!only_missing_tlv); + + node.bcast.index = 0; + assert(nannounce_different(NULL, &node, ann, &only_missing_tlv)); + assert(!only_missing_tlv); + + /* TLV checks */ + ann = tal_hexdata(tmpctx, "0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0", strlen("0101069628d227649cc2823d94647ad08ea34ad24e7eea95b7a0249bc83e73efefa6072cab1841f0ef3e6d7f4c4140b7b1b13049eb0d85941d7d7bd30c921bfd550300078000000802aaa260b1496f0266e4598d1d3c415f572a8488830b60f7e744ed9235eb0b1ba93283b315c035180266e453454e494f524245414d000000000000000000000000000000000000000000000000010c0014000003e80096001607d0")); + + node.bcast.index = 2; + assert(!nannounce_different(NULL, &node, ann, &only_missing_tlv)); + assert(!only_missing_tlv); + + /* Tweak the last, check that it fails */ + node.bcast.index = 2; + ann[tal_count(ann) - 1]++; + assert(nannounce_different(NULL, &node, ann, &only_missing_tlv)); + assert(only_missing_tlv); + + common_shutdown(); + return 0; +} diff --git a/gossipd/test/run-find_route-specific.c b/gossipd/test/run-find_route-specific.c index 64060092c2cb..3835136f2e4c 100644 --- a/gossipd/test/run-find_route-specific.c +++ b/gossipd/test/run-find_route-specific.c @@ -50,7 +50,8 @@ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memt /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) + const u8 *nannounce UNNEEDED, + bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) diff --git a/gossipd/test/run-find_route.c b/gossipd/test/run-find_route.c index 41400bf2cd05..0ed7524d19f1 100644 --- a/gossipd/test/run-find_route.c +++ b/gossipd/test/run-find_route.c @@ -50,7 +50,8 @@ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memt /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) + const u8 *nannounce UNNEEDED, + bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) diff --git a/gossipd/test/run-overlong.c b/gossipd/test/run-overlong.c index 2a16642f7026..1d8e6dd0864c 100644 --- a/gossipd/test/run-overlong.c +++ b/gossipd/test/run-overlong.c @@ -50,7 +50,8 @@ void memleak_add_helper_(const tal_t *p UNNEEDED, void (*cb)(struct htable *memt /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) + const u8 *nannounce UNNEEDED, + bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) diff --git a/gossipd/test/run-txout_failure.c b/gossipd/test/run-txout_failure.c index 044e8fed16ff..bf1df6a3e7b1 100644 --- a/gossipd/test/run-txout_failure.c +++ b/gossipd/test/run-txout_failure.c @@ -70,7 +70,8 @@ void memleak_remove_intmap_(struct htable *memtable UNNEEDED, const struct intma /* Generated stub for nannounce_different */ bool nannounce_different(struct gossip_store *gs UNNEEDED, const struct node *node UNNEEDED, - const u8 *nannounce UNNEEDED) + const u8 *nannounce UNNEEDED, + bool *only_missing_tlv UNNEEDED) { fprintf(stderr, "nannounce_different called!\n"); abort(); } /* Generated stub for notleak_ */ void *notleak_(const void *ptr UNNEEDED, bool plus_children UNNEEDED) diff --git a/hsmd/Makefile b/hsmd/Makefile index 6e18705243ea..afd120d649e6 100644 --- a/hsmd/Makefile +++ b/hsmd/Makefile @@ -31,6 +31,7 @@ HSMD_COMMON_OBJS := \ common/hash_u5.o \ common/hsm_encryption.o \ common/key_derive.o \ + common/lease_rates.o \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ diff --git a/hsmd/capabilities.h b/hsmd/capabilities.h index 3a306e778a2a..a95177901a61 100644 --- a/hsmd/capabilities.h +++ b/hsmd/capabilities.h @@ -7,6 +7,7 @@ #define HSM_CAP_COMMITMENT_POINT 8 #define HSM_CAP_SIGN_REMOTE_TX 16 #define HSM_CAP_SIGN_CLOSING_TX 32 +#define HSM_CAP_SIGN_WILL_FUND_OFFER 64 #define HSM_CAP_MASTER 1024 #endif /* LIGHTNING_HSMD_CAPABILITIES_H */ diff --git a/hsmd/hsmd.c b/hsmd/hsmd.c index 28eafd153351..39642fc9c473 100644 --- a/hsmd/hsmd.c +++ b/hsmd/hsmd.c @@ -663,6 +663,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMD_GET_CHANNEL_BASEPOINTS: case WIRE_HSMD_SIGN_INVOICE: case WIRE_HSMD_SIGN_MESSAGE: + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER: case WIRE_HSMD_SIGN_BOLT12: case WIRE_HSMD_ECDH_REQ: case WIRE_HSMD_CHECK_FUTURE_SECRET: @@ -689,6 +690,7 @@ static struct io_plan *handle_client(struct io_conn *conn, struct client *c) case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: case WIRE_HSMD_GET_PER_COMMITMENT_POINT_REPLY: case WIRE_HSMD_CHECK_FUTURE_SECRET_REPLY: case WIRE_HSMD_GET_CHANNEL_BASEPOINTS_REPLY: diff --git a/hsmd/hsmd_wire.csv b/hsmd/hsmd_wire.csv index 86be49bb13d9..e6126bf9b84c 100644 --- a/hsmd/hsmd_wire.csv +++ b/hsmd/hsmd_wire.csv @@ -212,3 +212,12 @@ msgdata,hsmd_sign_bolt12,publictweak,u8,publictweaklen msgtype,hsmd_sign_bolt12_reply,125 msgdata,hsmd_sign_bolt12_reply,sig,bip340sig, +# Sign an option_will_fund offer hash +msgtype,hsmd_sign_option_will_fund_offer,26 +msgdata,hsmd_sign_option_will_fund_offer,funding_pubkey,pubkey, +msgdata,hsmd_sign_option_will_fund_offer,blockheight,u32, +msgdata,hsmd_sign_option_will_fund_offer,channel_fee_base_max_msat,u32, +msgdata,hsmd_sign_option_will_fund_offer,channel_fee_proportional_basis_max,u16, + +msgtype,hsmd_sign_option_will_fund_offer_reply,126 +msgdata,hsmd_sign_option_will_fund_offer_reply,rsig,secp256k1_ecdsa_signature, diff --git a/hsmd/hsmd_wiregen.c b/hsmd/hsmd_wiregen.c index 048a49a282d6..2263247658ed 100644 --- a/hsmd/hsmd_wiregen.c +++ b/hsmd/hsmd_wiregen.c @@ -62,6 +62,8 @@ const char *hsmd_wire_name(int e) case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY: return "WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY"; case WIRE_HSMD_SIGN_BOLT12: return "WIRE_HSMD_SIGN_BOLT12"; case WIRE_HSMD_SIGN_BOLT12_REPLY: return "WIRE_HSMD_SIGN_BOLT12_REPLY"; + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER: return "WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER"; + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: return "WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY"; } snprintf(invalidbuf, sizeof(invalidbuf), "INVALID %i", e); @@ -112,6 +114,8 @@ bool hsmd_wire_is_defined(u16 type) case WIRE_HSMD_GET_OUTPUT_SCRIPTPUBKEY_REPLY:; case WIRE_HSMD_SIGN_BOLT12:; case WIRE_HSMD_SIGN_BOLT12_REPLY:; + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER:; + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY:; return true; } return false; @@ -1278,4 +1282,53 @@ bool fromwire_hsmd_sign_bolt12_reply(const void *p, struct bip340sig *sig) fromwire_bip340sig(&cursor, &plen, sig); return cursor != NULL; } -// SHA256STAMP:535c69a065c06a2e2ea151154ae83b53283d1c5b34e18b43a2c12c9444472548 + +/* WIRE: HSMD_SIGN_OPTION_WILL_FUND_OFFER */ +/* Sign an option_will_fund offer hash */ +u8 *towire_hsmd_sign_option_will_fund_offer(const tal_t *ctx, const struct pubkey *funding_pubkey, u32 blockheight, u32 channel_fee_base_max_msat, u16 channel_fee_proportional_basis_max) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER); + towire_pubkey(&p, funding_pubkey); + towire_u32(&p, blockheight); + towire_u32(&p, channel_fee_base_max_msat); + towire_u16(&p, channel_fee_proportional_basis_max); + + return memcheck(p, tal_count(p)); +} +bool fromwire_hsmd_sign_option_will_fund_offer(const void *p, struct pubkey *funding_pubkey, u32 *blockheight, u32 *channel_fee_base_max_msat, u16 *channel_fee_proportional_basis_max) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER) + return false; + fromwire_pubkey(&cursor, &plen, funding_pubkey); + *blockheight = fromwire_u32(&cursor, &plen); + *channel_fee_base_max_msat = fromwire_u32(&cursor, &plen); + *channel_fee_proportional_basis_max = fromwire_u16(&cursor, &plen); + return cursor != NULL; +} + +/* WIRE: HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY */ +u8 *towire_hsmd_sign_option_will_fund_offer_reply(const tal_t *ctx, const secp256k1_ecdsa_signature *rsig) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY); + towire_secp256k1_ecdsa_signature(&p, rsig); + + return memcheck(p, tal_count(p)); +} +bool fromwire_hsmd_sign_option_will_fund_offer_reply(const void *p, secp256k1_ecdsa_signature *rsig) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY) + return false; + fromwire_secp256k1_ecdsa_signature(&cursor, &plen, rsig); + return cursor != NULL; +} +// SHA256STAMP:db96cc713b36122efe50406d2c99a1f9e4a9b3663107196f1776e9a299d224a2 diff --git a/hsmd/hsmd_wiregen.h b/hsmd/hsmd_wiregen.h index 1da52edbcb21..7ce15d540619 100644 --- a/hsmd/hsmd_wiregen.h +++ b/hsmd/hsmd_wiregen.h @@ -79,6 +79,9 @@ enum hsmd_wire { /* Sign a bolt12-style merkle hash */ WIRE_HSMD_SIGN_BOLT12 = 25, WIRE_HSMD_SIGN_BOLT12_REPLY = 125, + /* Sign an option_will_fund offer hash */ + WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER = 26, + WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY = 126, }; const char *hsmd_wire_name(int e); @@ -281,6 +284,15 @@ bool fromwire_hsmd_sign_bolt12(const tal_t *ctx, const void *p, wirestring **mes u8 *towire_hsmd_sign_bolt12_reply(const tal_t *ctx, const struct bip340sig *sig); bool fromwire_hsmd_sign_bolt12_reply(const void *p, struct bip340sig *sig); +/* WIRE: HSMD_SIGN_OPTION_WILL_FUND_OFFER */ +/* Sign an option_will_fund offer hash */ +u8 *towire_hsmd_sign_option_will_fund_offer(const tal_t *ctx, const struct pubkey *funding_pubkey, u32 blockheight, u32 channel_fee_base_max_msat, u16 channel_fee_proportional_basis_max); +bool fromwire_hsmd_sign_option_will_fund_offer(const void *p, struct pubkey *funding_pubkey, u32 *blockheight, u32 *channel_fee_base_max_msat, u16 *channel_fee_proportional_basis_max); + +/* WIRE: HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY */ +u8 *towire_hsmd_sign_option_will_fund_offer_reply(const tal_t *ctx, const secp256k1_ecdsa_signature *rsig); +bool fromwire_hsmd_sign_option_will_fund_offer_reply(const void *p, secp256k1_ecdsa_signature *rsig); + #endif /* LIGHTNING_HSMD_HSMD_WIREGEN_H */ -// SHA256STAMP:535c69a065c06a2e2ea151154ae83b53283d1c5b34e18b43a2c12c9444472548 +// SHA256STAMP:db96cc713b36122efe50406d2c99a1f9e4a9b3663107196f1776e9a299d224a2 diff --git a/hsmd/libhsmd.c b/hsmd/libhsmd.c index cef38c5b3527..b1db7aac5474 100644 --- a/hsmd/libhsmd.c +++ b/hsmd/libhsmd.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,9 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMD_SIGN_MUTUAL_CLOSE_TX: return (client->capabilities & HSM_CAP_SIGN_CLOSING_TX) != 0; + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER: + return (client->capabilities & HSM_CAP_SIGN_WILL_FUND_OFFER) != 0; + case WIRE_HSMD_INIT: case WIRE_HSMD_CLIENT_HSMFD: case WIRE_HSMD_SIGN_WITHDRAWAL: @@ -119,6 +123,7 @@ bool hsmd_check_client_capabilities(struct hsmd_client *client, case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: case WIRE_HSMD_GET_PER_COMMITMENT_POINT_REPLY: case WIRE_HSMD_CHECK_FUTURE_SECRET_REPLY: case WIRE_HSMD_GET_CHANNEL_BASEPOINTS_REPLY: @@ -383,7 +388,10 @@ static void sign_our_inputs(struct utxo **utxos, struct wally_psbt *psbt) /* It's actually a P2WSH in this case. */ if (utxo->close_info && utxo->close_info->option_anchor_outputs) { - const u8 *wscript = anchor_to_remote_redeem(tmpctx, &pubkey); + const u8 *wscript + = anchor_to_remote_redeem(tmpctx, + &pubkey, + utxo->close_info->csv); psbt_input_set_witscript(psbt, j, wscript); psbt_input_set_wit_utxo(psbt, j, scriptpubkey_p2wsh(psbt, wscript), @@ -468,6 +476,41 @@ static u8 *handle_sign_message(struct hsmd_client *c, const u8 *msg_in) return towire_hsmd_sign_message_reply(NULL, &rsig); } +/*~ lightningd asks us to sign a liquidity ad offer */ +static u8 *handle_sign_option_will_fund_offer(struct hsmd_client *c, + const u8 *msg_in) +{ + struct pubkey funding_pubkey; + u32 lease_expiry, channel_fee_base_max_msat; + u16 channel_fee_max_ppt; + struct sha256 sha; + secp256k1_ecdsa_signature sig; + struct privkey node_pkey; + + if (!fromwire_hsmd_sign_option_will_fund_offer(msg_in, + &funding_pubkey, + &lease_expiry, + &channel_fee_base_max_msat, + &channel_fee_max_ppt)) + return hsmd_status_malformed_request(c, msg_in); + + lease_rates_get_commitment(&funding_pubkey, lease_expiry, + channel_fee_base_max_msat, + channel_fee_max_ppt, + &sha); + + node_key(&node_pkey, NULL); + + if (!secp256k1_ecdsa_sign(secp256k1_ctx, &sig, + sha.u.u8, + node_pkey.secret.data, + NULL, NULL)) + return hsmd_status_bad_request(c, msg_in, + "Failed to sign message"); + + return towire_hsmd_sign_option_will_fund_offer_reply(NULL, &sig); +} + /*~ lightningd asks us to sign a bolt12 (e.g. offer). */ static u8 *handle_sign_bolt12(struct hsmd_client *c, const u8 *msg_in) { @@ -1352,6 +1395,8 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, return handle_ecdh(client, msg); case WIRE_HSMD_SIGN_INVOICE: return handle_sign_invoice(client, msg); + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER: + return handle_sign_option_will_fund_offer(client, msg); case WIRE_HSMD_SIGN_BOLT12: return handle_sign_bolt12(client, msg); case WIRE_HSMD_SIGN_MESSAGE: @@ -1397,6 +1442,7 @@ u8 *hsmd_handle_client_message(const tal_t *ctx, struct hsmd_client *client, case WIRE_HSMSTATUS_CLIENT_BAD_REQUEST: case WIRE_HSMD_SIGN_COMMITMENT_TX_REPLY: case WIRE_HSMD_SIGN_TX_REPLY: + case WIRE_HSMD_SIGN_OPTION_WILL_FUND_OFFER_REPLY: case WIRE_HSMD_GET_PER_COMMITMENT_POINT_REPLY: case WIRE_HSMD_CHECK_FUTURE_SECRET_REPLY: case WIRE_HSMD_GET_CHANNEL_BASEPOINTS_REPLY: diff --git a/lightningd/Makefile b/lightningd/Makefile index 1a4a1c4dbf0c..c4dfafdd315f 100644 --- a/lightningd/Makefile +++ b/lightningd/Makefile @@ -69,6 +69,7 @@ LIGHTNINGD_COMMON_OBJS := \ common/bigsize.o \ common/bip32.o \ common/blinding.o \ + common/blockheight_states.o \ common/bolt11.o \ common/bolt11_json.o \ common/bolt12.o \ @@ -100,6 +101,7 @@ LIGHTNINGD_COMMON_OBJS := \ common/json_helpers.o \ common/json_stream.o \ common/json_tok.o \ + common/lease_rates.o \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ diff --git a/lightningd/chaintopology.c b/lightningd/chaintopology.c index 949e0f0f615f..6f624c839dd3 100644 --- a/lightningd/chaintopology.c +++ b/lightningd/chaintopology.c @@ -553,6 +553,33 @@ static const struct json_command feerates_command = { }; AUTODATA(json_command, &feerates_command); +static struct command_result *json_parse_feerate(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *response; + u32 *feerate; + + if (!param(cmd, buffer, params, + p_req("feerate", param_feerate, &feerate), + NULL)) + return command_param_failed(); + + response = json_stream_success(cmd); + json_add_num(response, feerate_style_name(FEERATE_PER_KSIPA), + feerate_to_style(*feerate, FEERATE_PER_KSIPA)); + return command_success(cmd, response); +} + +static const struct json_command parse_feerate_command = { + "parsefeerate", + "bitcoin", + json_parse_feerate, + "Return current feerate in perkw + perkb for given feerate string." +}; +AUTODATA(json_command, &parse_feerate_command); + static void next_updatefee_timer(struct chain_topology *topo) { /* This takes care of its own lifetime. */ diff --git a/lightningd/channel.c b/lightningd/channel.c index 074a058efb18..a4ead163d169 100644 --- a/lightningd/channel.c +++ b/lightningd/channel.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -160,7 +161,11 @@ new_inflight(struct channel *channel, struct amount_sat our_funds, struct wally_psbt *psbt STEALS, struct bitcoin_tx *last_tx, - const struct bitcoin_signature last_sig) + const struct bitcoin_signature last_sig, + const u32 lease_expiry, + const secp256k1_ecdsa_signature *lease_commit_sig, + const u32 lease_chan_max_msat, const u16 lease_chan_max_ppt, + const u32 lease_blockheight_start) { struct wally_psbt *last_tx_psbt_clone; struct channel_inflight *inflight @@ -185,6 +190,19 @@ new_inflight(struct channel *channel, inflight->last_sig = last_sig; inflight->tx_broadcast = false; + /* Channel lease infos */ + inflight->lease_blockheight_start = lease_blockheight_start; + inflight->lease_expiry = lease_expiry; + if (lease_commit_sig) + inflight->lease_commit_sig + = tal_dup(inflight, secp256k1_ecdsa_signature, + lease_commit_sig); + else + inflight->lease_commit_sig = NULL; + + inflight->lease_chan_max_msat = lease_chan_max_msat; + inflight->lease_chan_max_ppt = lease_chan_max_ppt; + list_add_tail(&channel->inflights, &inflight->list); tal_add_destructor(inflight, destroy_inflight); @@ -268,6 +286,8 @@ struct channel *new_unsaved_channel(struct peer *peer, channel->option_anchor_outputs = true; channel->future_per_commitment_point = NULL; + channel->lease_commit_sig = NULL; + /* No shachain yet */ channel->their_shachain.id = 0; shachain_init(&channel->their_shachain.chain); @@ -341,7 +361,12 @@ struct channel *new_channel(struct peer *peer, u64 dbid, enum side closer, enum state_change reason, /* NULL or stolen */ - const struct bitcoin_outpoint *shutdown_wrong_funding) + const struct bitcoin_outpoint *shutdown_wrong_funding, + const struct height_states *height_states TAKES, + u32 lease_expiry, + secp256k1_ecdsa_signature *lease_commit_sig STEALS, + u32 lease_chan_max_msat, + u16 lease_chan_max_ppt) { struct channel *channel = tal(peer->ld, struct channel); @@ -430,6 +455,12 @@ struct channel *new_channel(struct peer *peer, u64 dbid, channel->option_anchor_outputs = option_anchor_outputs; channel->forgets = tal_arr(channel, struct command *, 0); + channel->lease_expiry = lease_expiry; + channel->lease_commit_sig = tal_steal(channel, lease_commit_sig); + channel->lease_chan_max_msat = lease_chan_max_msat; + channel->lease_chan_max_ppt = lease_chan_max_ppt; + channel->blockheight_states = dup_height_states(channel, height_states); + list_add_tail(&peer->channels, &channel->list); channel->rr_number = peer->ld->rr_counter++; tal_add_destructor(channel, destroy_channel); diff --git a/lightningd/channel.h b/lightningd/channel.h index 0ea0ca451a12..1c25702ed800 100644 --- a/lightningd/channel.h +++ b/lightningd/channel.h @@ -45,6 +45,13 @@ struct channel_inflight { /* Commitment tx and sigs */ struct bitcoin_tx *last_tx; struct bitcoin_signature last_sig; + + /* Channel lease infos */ + u32 lease_expiry; + secp256k1_ecdsa_signature *lease_commit_sig; + u32 lease_chan_max_msat; + u16 lease_chan_max_ppt; + u32 lease_blockheight_start; }; struct open_attempt { @@ -144,6 +151,9 @@ struct channel { /* Fee status */ const struct fee_states *fee_states; + /* Height states (option_will_fund, update_blockheight) */ + const struct height_states *blockheight_states; + /* Our local basepoints */ struct basepoints local_basepoints; @@ -208,6 +218,18 @@ struct channel { /* Outstanding command for this channel, v2 only */ struct command *openchannel_signed_cmd; + + /* Block lease expires at, zero is no lease */ + u32 lease_expiry; + + /* Lease commitment, useful someone breaks their promise + * wrt channel fees */ + secp256k1_ecdsa_signature *lease_commit_sig; + + /* Lease commited maximum channel fee base msat */ + u32 lease_chan_max_msat; + /* Lease commited max part per thousandth channel fee (ppm * 1000) */ + u16 lease_chan_max_ppt; }; /* For v2 opens, a channel that has not yet been committed/saved to disk */ @@ -273,7 +295,12 @@ struct channel *new_channel(struct peer *peer, u64 dbid, enum side closer, enum state_change reason, /* NULL or stolen */ - const struct bitcoin_outpoint *shutdown_wrong_funding STEALS); + const struct bitcoin_outpoint *shutdown_wrong_funding STEALS, + const struct height_states *height_states TAKES, + u32 lease_expiry, + secp256k1_ecdsa_signature *lease_commit_sig STEALS, + u32 lease_chan_max_msat, + u16 lease_chan_max_ppt); /* new_inflight - Create a new channel_inflight for a channel */ struct channel_inflight * @@ -285,7 +312,12 @@ new_inflight(struct channel *channel, struct amount_sat our_funds, struct wally_psbt *funding_psbt STEALS, struct bitcoin_tx *last_tx STEALS, - const struct bitcoin_signature last_sig); + const struct bitcoin_signature last_sig, + const u32 lease_expiry, + const secp256k1_ecdsa_signature *lease_commit_sig, + const u32 lease_chan_max_msat, + const u16 lease_chan_max_ppt, + const u32 lease_blockheight_start); /* Given a txid, find an inflight channel stub. Returns NULL if none found */ struct channel_inflight *channel_inflight_find(struct channel *channel, diff --git a/lightningd/channel_control.c b/lightningd/channel_control.c index e8c87401af1d..3940dab5a32b 100644 --- a/lightningd/channel_control.c +++ b/lightningd/channel_control.c @@ -69,6 +69,56 @@ static void try_update_feerates(struct lightningd *ld, struct channel *channel) update_feerates(ld, channel); } +static void try_update_blockheight(struct lightningd *ld, + struct channel *channel, + u32 blockheight) +{ + u8 *msg; + + log_debug(channel->log, "attempting update blockheight %s", + type_to_string(tmpctx, struct channel_id, &channel->cid)); + + /* If they're offline, check that we're not too far behind anyway */ + if (!channel->owner) { + if (channel->opener == REMOTE + && channel->lease_expiry > 0) { + u32 peer_height + = get_blockheight(channel->blockheight_states, + channel->opener, REMOTE); + + /* Lease no longer active, we don't (really) care */ + if (peer_height >= channel->lease_expiry) + return; + + assert(peer_height + 1008 > peer_height); + if (peer_height + 1008 < blockheight) + channel_fail_permanent(channel, + REASON_PROTOCOL, + "Offline peer is too" + " far behind," + " terminating leased" + " channel. Our current" + " %u, theirs %u", + blockheight, + peer_height); + } + return; + } + + /* If we're not opened/locked in yet, don't send update */ + if (!channel_fees_can_change(channel)) + return; + + /* We don't update the blockheight for non-leased chans */ + if (channel->lease_expiry == 0) + return; + + log_debug(ld->log, "update_blockheight: height = %u", blockheight); + + msg = towire_channeld_blockheight(NULL, blockheight); + subd_send_msg(channel->owner, take(msg)); +} + void notify_feerate_change(struct lightningd *ld) { struct peer *peer; @@ -155,6 +205,9 @@ static void lockin_complete(struct channel *channel) /* Fees might have changed (and we use IMMEDIATE once we're funded), * so update now. */ try_update_feerates(channel->peer->ld, channel); + + try_update_blockheight(channel->peer->ld, channel, + get_block_height(channel->peer->ld->topology)); channel_record_open(channel); } @@ -447,6 +500,7 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds) case WIRE_CHANNELD_SEND_SHUTDOWN: case WIRE_CHANNELD_DEV_REENABLE_COMMIT: case WIRE_CHANNELD_FEERATES: + case WIRE_CHANNELD_BLOCKHEIGHT: case WIRE_CHANNELD_SPECIFIC_FEERATES: case WIRE_CHANNELD_DEV_MEMLEAK: case WIRE_CHANNELD_DEV_QUIESCE: @@ -586,6 +640,9 @@ void peer_start_channeld(struct channel *channel, channel->funding_outnum, channel->funding, channel->minimum_depth, + get_block_height(ld->topology), + channel->blockheight_states, + channel->lease_expiry, &channel->our_config, &channel->channel_info.their_config, channel->fee_states, @@ -641,9 +698,13 @@ void peer_start_channeld(struct channel *channel, /* We don't expect a response: we are triggered by funding_depth_cb. */ subd_send_msg(channel->owner, take(initmsg)); - /* On restart, feerate might not be what we expect: adjust now. */ - if (channel->opener == LOCAL) + /* On restart, feerate and blockheight + * might not be what we expect: adjust now. */ + if (channel->opener == LOCAL) { try_update_feerates(ld, channel); + try_update_blockheight(ld, channel, + get_block_height(ld->topology)); + } } bool channel_tell_depth(struct lightningd *ld, @@ -732,6 +793,10 @@ is_fundee_should_forget(struct lightningd *ld, if (block_height - channel->first_blocknum < max_funding_unconfirmed) return false; + /* If we've got funds in the channel, don't forget it */ + if (!amount_sat_zero(channel->our_funds)) + return false; + /* Ah forget it! */ return true; } @@ -751,7 +816,11 @@ void channel_notify_new_block(struct lightningd *ld, continue; if (is_fundee_should_forget(ld, channel, block_height)) { tal_arr_expand(&to_forget, channel); - } + } else + /* Let channels know about new blocks, + * required for lease updates */ + try_update_blockheight(ld, channel, + block_height); } } diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index ac9e1f6effea..14da1a34c854 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -244,10 +246,15 @@ struct openchannel2_payload { /* What's the maximum amount of funding * this channel can hold */ struct amount_sat channel_max; + /* If they've requested funds, this is their request */ + struct amount_sat requested_lease_amt; + u32 lease_blockheight_start; + u32 node_blockheight; struct amount_sat accepter_funding; struct wally_psbt *psbt; const u8 *our_shutdown_scriptpubkey; + struct lease_rates *rates; char *err_msg; }; @@ -283,6 +290,14 @@ static void openchannel2_hook_serialize(struct openchannel2_payload *payload, payload->shutdown_scriptpubkey); json_add_amount_sat_only(stream, "channel_max_msat", payload->channel_max); + if (!amount_sat_zero(payload->requested_lease_amt)) { + json_add_amount_sat_only(stream, "requested_lease_msat", + payload->requested_lease_amt); + json_add_num(stream, "lease_blockheight_start", + payload->lease_blockheight_start); + json_add_num(stream, "node_blockheight", + payload->node_blockheight); + } json_object_end(stream); } @@ -619,11 +634,11 @@ rbf_channel_hook_deserialize(struct rbf_channel_payload *payload, fatal("Plugin failed to supply our_funding_msat field"); if (payload->psbt - && amount_sat_eq(payload->our_funding, AMOUNT_SAT(0))) + && amount_sat_zero(payload->our_funding)) fatal("Plugin failed to supply our_funding_msat field"); if (!payload->psbt && - !amount_sat_eq(payload->our_funding, AMOUNT_SAT(0))) { + !amount_sat_zero(payload->our_funding)) { log_broken(channel->log, "`our_funding_msat` returned" " but no `psbt` present. %.*s", @@ -687,7 +702,8 @@ openchannel2_hook_cb(struct openchannel2_payload *payload STEALS) msg = towire_dualopend_got_offer_reply(NULL, payload->accepter_funding, payload->psbt, - payload->our_shutdown_scriptpubkey); + payload->our_shutdown_scriptpubkey, + payload->rates); subd_send_msg(dualopend, take(msg)); } @@ -699,6 +715,7 @@ openchannel2_hook_deserialize(struct openchannel2_payload *payload, const jsmntok_t *toks) { const u8 *shutdown_script; + const char *err; struct subd *dualopend = payload->dualopend; /* If our daemon died, we're done */ @@ -756,6 +773,38 @@ openchannel2_hook_deserialize(struct openchannel2_payload *payload, else payload->our_shutdown_scriptpubkey = shutdown_script; + + struct amount_sat sats; + struct amount_msat msats; + payload->rates = tal(payload, struct lease_rates); + err = json_scan(payload, buffer, toks, + "{lease_fee_base_msat:%" + ",lease_fee_basis:%" + ",channel_fee_max_base_msat:%" + ",channel_fee_max_proportional_thousandths:%" + ",funding_weight:%}", + JSON_SCAN(json_to_sat, &sats), + JSON_SCAN(json_to_u16, + &payload->rates->lease_fee_basis), + JSON_SCAN(json_to_msat, &msats), + JSON_SCAN(json_to_u16, + &payload->rates->channel_fee_max_proportional_thousandths), + JSON_SCAN(json_to_u16, + &payload->rates->funding_weight)); + + /* It's possible they didn't send these back! */ + if (err) + payload->rates = tal_free(payload->rates); + + /* Convert to u32s */ + if (payload->rates && + !lease_rates_set_lease_fee_sat(payload->rates, sats)) + fatal("Plugin sent overflowing `lease_fee_base_msat`"); + + if (payload->rates && + !lease_rates_set_chan_fee_base_msat(payload->rates, msats)) + fatal("Plugin sent overflowing `channel_fee_max_base_msat`"); + /* Add a serial_id to everything that doesn't have one yet */ if (payload->psbt) psbt_add_serials(payload->psbt, TX_ACCEPTER); @@ -777,11 +826,11 @@ openchannel2_hook_deserialize(struct openchannel2_payload *payload, fatal("Plugin failed to supply our_funding_msat field"); if (payload->psbt - && amount_sat_eq(payload->accepter_funding, AMOUNT_SAT(0))) + && amount_sat_zero(payload->accepter_funding)) fatal("Plugin failed to supply our_funding_msat field"); if (!payload->psbt - && !amount_sat_eq(payload->accepter_funding, AMOUNT_SAT(0))) { + && !amount_sat_zero(payload->accepter_funding)) { /* Gotta give a PSBT if you set the accepter_funding amount */ /* Let dualopend know we've failed */ payload->err_msg = "Client error. Unable to continue"; @@ -1064,7 +1113,12 @@ wallet_update_channel(struct lightningd *ld, struct amount_sat total_funding, struct amount_sat our_funding, u32 funding_feerate, - struct wally_psbt *psbt STEALS) + struct wally_psbt *psbt STEALS, + const u32 lease_expiry, + secp256k1_ecdsa_signature *lease_commit_sig STEALS, + const u32 lease_chan_max_msat, + const u16 lease_chan_max_ppt, + const u32 lease_blockheight_start) { struct amount_msat our_msat; struct channel_inflight *inflight; @@ -1084,6 +1138,17 @@ wallet_update_channel(struct lightningd *ld, channel->our_msat = our_msat; channel->msat_to_us_min = our_msat; channel->msat_to_us_max = our_msat; + channel->lease_expiry = lease_expiry; + + tal_free(channel->lease_commit_sig); + channel->lease_commit_sig = tal_steal(channel, lease_commit_sig); + channel->lease_chan_max_msat = lease_chan_max_msat; + channel->lease_chan_max_ppt = lease_chan_max_ppt; + + tal_free(channel->blockheight_states); + channel->blockheight_states = new_height_states(channel, + channel->opener, + &lease_blockheight_start); channel_set_last_tx(channel, tal_steal(channel, remote_commit), @@ -1102,7 +1167,12 @@ wallet_update_channel(struct lightningd *ld, channel->our_funds, psbt, channel->last_tx, - channel->last_sig); + channel->last_sig, + channel->lease_expiry, + channel->lease_commit_sig, + channel->lease_chan_max_msat, + channel->lease_chan_max_ppt, + lease_blockheight_start); wallet_inflight_add(ld->wallet, inflight); return inflight; @@ -1123,7 +1193,12 @@ wallet_commit_channel(struct lightningd *ld, u32 commitment_feerate, const u8 *our_upfront_shutdown_script, const u8 *remote_upfront_shutdown_script, - struct wally_psbt *psbt STEALS) + struct wally_psbt *psbt STEALS, + const u32 lease_blockheight_start, + const u32 lease_expiry, + secp256k1_ecdsa_signature *lease_commit_sig STEALS, + const u32 lease_chan_max_msat, + const u16 lease_chan_max_ppt) { struct amount_msat our_msat; struct channel_inflight *inflight; @@ -1190,6 +1265,18 @@ wallet_commit_channel(struct lightningd *ld, * in theory, but it's only used for timing out. */ channel->first_blocknum = get_block_height(ld->topology); + /* Update lease info for channel */ + channel->blockheight_states = new_height_states(channel, + channel->opener, + &lease_blockheight_start); + channel->lease_expiry = lease_expiry; + + tal_free(channel->lease_commit_sig); + channel->lease_commit_sig = tal_steal(channel, lease_commit_sig); + + channel->lease_chan_max_msat = lease_chan_max_msat; + channel->lease_chan_max_ppt = lease_chan_max_ppt; + /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); @@ -1202,7 +1289,12 @@ wallet_commit_channel(struct lightningd *ld, channel->our_funds, psbt, channel->last_tx, - channel->last_sig); + channel->last_sig, + channel->lease_expiry, + channel->lease_commit_sig, + channel->lease_chan_max_msat, + channel->lease_chan_max_ppt, + lease_blockheight_start); wallet_inflight_add(ld->wallet, inflight); return inflight; @@ -1494,6 +1586,47 @@ static void handle_peer_tx_sigs_sent(struct subd *dualopend, } } +static void handle_dry_run_finished(struct subd *dualopend, const u8 *msg) +{ + struct json_stream *response; + struct channel_id c_id; + struct channel *channel = dualopend->channel; + struct command *cmd; + struct lease_rates *rates; + struct amount_sat their_funding, our_funding; + + assert(channel->open_attempt); + cmd = channel->open_attempt->cmd; + channel->open_attempt->cmd = NULL; + + if (!fromwire_dualopend_dry_run(msg, msg, &c_id, + &our_funding, + &their_funding, + &rates)) { + channel_internal_error(channel, + "Bad WIRE_DUALOPEND_DRY_RUN_FINISHED: %s", + tal_hex(msg, msg)); + + return; + } + + /* Free up this open attempt */ + channel->open_attempt = tal_free(channel->open_attempt); + + response = json_stream_success(cmd); + json_add_amount_sat_only(response, "our_funding_msat", our_funding); + json_add_amount_sat_only(response, "their_funding_msat", their_funding); + + if (rates) { + json_add_lease_rates(response, rates); + /* As a convenience, add a hexstring version of this data */ + json_add_string(response, "compact_lease", + lease_rates_tohex(tmpctx, rates)); + } + + was_pending(command_success(cmd, response)); +} + static void handle_peer_locked(struct subd *dualopend, const u8 *msg) { struct pubkey remote_per_commit; @@ -1686,7 +1819,9 @@ static void accepter_got_offer(struct subd *dualopend, &payload->max_accepted_htlcs, &payload->channel_flags, &payload->locktime, - &payload->shutdown_scriptpubkey)) { + &payload->shutdown_scriptpubkey, + &payload->requested_lease_amt, + &payload->lease_blockheight_start)) { channel_internal_error(channel, "Bad DUALOPEND_GOT_OFFER: %s", tal_hex(tmpctx, msg)); return; @@ -1699,6 +1834,7 @@ static void accepter_got_offer(struct subd *dualopend, * the plugin */ payload->feerate_our_min = feerate_min(dualopend->ld, NULL); payload->feerate_our_max = feerate_max(dualopend->ld, NULL); + payload->node_blockheight = get_block_height(dualopend->ld->topology); payload->channel_max = chainparams->max_funding; if (feature_negotiated(dualopend->ld->our_features, @@ -1825,6 +1961,72 @@ static void handle_peer_tx_sigs_msg(struct subd *dualopend, inflight->funding_psbt); } +static bool verify_option_will_fund_signature(struct peer *peer, + struct pubkey *funding_pubkey, + u32 lease_expiry, + u32 chan_fee_msat, + u16 chan_fee_ppt, + const secp256k1_ecdsa_signature *sig) + +{ + struct pubkey their_pubkey; + struct sha256 sha; + int ret; + + lease_rates_get_commitment(funding_pubkey, lease_expiry, + chan_fee_msat, chan_fee_ppt, + &sha); + + if (!pubkey_from_node_id(&their_pubkey, &peer->id)) { + log_broken(peer->ld->log, + "Unable to extract pubkey from peer's node id %s", + type_to_string(tmpctx, struct node_id, &peer->id)); + return false; + } + + ret = secp256k1_ecdsa_verify(secp256k1_ctx, sig, sha.u.u8, + &their_pubkey.pubkey); + return ret == 1; +} + +static void handle_validate_lease(struct subd *dualopend, + const u8 *msg) +{ + const secp256k1_ecdsa_signature sig; + u16 chan_fee_max_ppt; + u32 chan_fee_max_base_msat, lease_expiry; + struct pubkey their_pubkey; + struct channel *chan; + char *err_msg; + + if (!fromwire_dualopend_validate_lease(msg, + cast_const(secp256k1_ecdsa_signature *, &sig), + &lease_expiry, + &chan_fee_max_base_msat, + &chan_fee_max_ppt, + &their_pubkey)) { + channel_internal_error(dualopend->channel, + "Bad DUALOPEND_VALIDATE_LEASE: %s", + tal_hex(tmpctx, msg)); + return; + } + + assert(dualopend->channel); + chan = dualopend->channel; + + if (!verify_option_will_fund_signature(chan->peer, &their_pubkey, + lease_expiry, + chan_fee_max_base_msat, + chan_fee_max_ppt, + &sig)) + err_msg = "Unable to verify sig"; + else + err_msg = NULL; + + subd_send_msg(dualopend, + take(towire_dualopend_validate_lease_reply(NULL, err_msg))); +} + static void handle_validate_rbf(struct subd *dualopend, const u8 *msg) { @@ -2265,10 +2467,33 @@ static struct command_result *json_openchannel_update(struct command *cmd, return command_still_pending(cmd); } +static struct command_result *init_set_feerate(struct command *cmd, + u32 **feerate_per_kw, + u32 **feerate_per_kw_funding) + +{ + if (!*feerate_per_kw_funding) { + *feerate_per_kw_funding = tal(cmd, u32); + **feerate_per_kw_funding = opening_feerate(cmd->ld->topology); + if (!**feerate_per_kw_funding) + return command_fail(cmd, LIGHTNINGD, + "`funding_feerate` not specified and fee " + "estimation failed"); + } + if (!*feerate_per_kw) { + *feerate_per_kw = tal(cmd, u32); + /* FIXME: Anchors are on by default, we should use the lowest + * possible feerate */ + **feerate_per_kw = **feerate_per_kw_funding; + } + + return NULL; +} + static struct command_result *json_openchannel_init(struct command *cmd, - const char *buffer, - const jsmntok_t *obj UNNEEDED, - const jsmntok_t *params) + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) { struct node_id *id; struct peer *peer; @@ -2276,10 +2501,12 @@ static struct command_result *json_openchannel_init(struct command *cmd, bool *announce_channel; u32 *feerate_per_kw_funding; u32 *feerate_per_kw; - struct amount_sat *amount, psbt_val; + struct amount_sat *amount, psbt_val, *request_amt; struct wally_psbt *psbt; const u8 *our_upfront_shutdown_script; struct open_attempt *oa; + struct lease_rates *rates; + struct command_result *res; u8 *msg; if (!param(cmd, buffer, params, @@ -2290,9 +2517,16 @@ static struct command_result *json_openchannel_init(struct command *cmd, p_opt("funding_feerate", param_feerate, &feerate_per_kw_funding), p_opt_def("announce", param_bool, &announce_channel, true), p_opt("close_to", param_bitcoin_address, &our_upfront_shutdown_script), + p_opt_def("request_amt", param_sat, &request_amt, AMOUNT_SAT(0)), + p_opt("compact_lease", param_lease_hex, &rates), NULL)) return command_param_failed(); + /* Gotta expect some rates ! */ + if (!amount_sat_zero(*request_amt) && !rates) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Must pass in 'compact_lease' if requesting" + " funds from peer"); psbt_val = AMOUNT_SAT(0); for (size_t i = 0; i < psbt->num_inputs; i++) { struct amount_sat in_amt = psbt_input_get_amount(psbt, i); @@ -2318,20 +2552,9 @@ static struct command_result *json_openchannel_init(struct command *cmd, struct wally_psbt, psbt)); - if (!feerate_per_kw_funding) { - feerate_per_kw_funding = tal(cmd, u32); - *feerate_per_kw_funding = opening_feerate(cmd->ld->topology); - if (!*feerate_per_kw_funding) - return command_fail(cmd, LIGHTNINGD, - "`funding_feerate` not specified and fee " - "estimation failed"); - } - if (!feerate_per_kw) { - feerate_per_kw = tal(cmd, u32); - /* FIXME: Anchors are on by default, we should use the lowest - * possible feerate */ - *feerate_per_kw = *feerate_per_kw_funding; - } + res = init_set_feerate(cmd, &feerate_per_kw, &feerate_per_kw_funding); + if (res) + return res; if (!topology_synced(cmd->ld->topology)) { return command_fail(cmd, FUNDING_STILL_SYNCING_BITCOIN, @@ -2421,7 +2644,11 @@ static struct command_result *json_openchannel_init(struct command *cmd, oa->our_upfront_shutdown_script, *feerate_per_kw, *feerate_per_kw_funding, - channel->channel_flags); + channel->channel_flags, + *request_amt, + get_block_height(cmd->ld->topology), + false, + rates); subd_send_msg(channel->owner, take(msg)); return command_still_pending(cmd); @@ -2515,8 +2742,9 @@ static void handle_commit_received(struct subd *dualopend, struct bitcoin_tx *remote_commit; struct bitcoin_signature remote_commit_sig; struct bitcoin_txid funding_txid; - u16 funding_outnum; - u32 feerate_funding, feerate_commitment; + u16 funding_outnum, lease_chan_max_ppt; + u32 feerate_funding, feerate_commitment, lease_expiry, + lease_chan_max_msat, lease_blockheight_start; struct amount_sat total_funding, funding_ours; u8 *remote_upfront_shutdown_script, *local_upfront_shutdown_script; @@ -2526,6 +2754,7 @@ static void handle_commit_received(struct subd *dualopend, struct openchannel2_psbt_payload *payload; struct channel_inflight *inflight; struct command *cmd = oa->cmd; + secp256k1_ecdsa_signature *lease_commit_sig; if (!fromwire_dualopend_commit_rcvd(tmpctx, msg, &channel_info.their_config, @@ -2547,7 +2776,12 @@ static void handle_commit_received(struct subd *dualopend, &feerate_funding, &feerate_commitment, &local_upfront_shutdown_script, - &remote_upfront_shutdown_script)) { + &remote_upfront_shutdown_script, + &lease_blockheight_start, + &lease_expiry, + &lease_commit_sig, + &lease_chan_max_msat, + &lease_chan_max_ppt)) { channel_internal_error(channel, "Bad WIRE_DUALOPEND_COMMIT_RCVD: %s", tal_hex(msg, msg)); @@ -2588,7 +2822,12 @@ static void handle_commit_received(struct subd *dualopend, oa->our_upfront_shutdown_script : local_upfront_shutdown_script, remote_upfront_shutdown_script, - psbt))) { + psbt, + lease_blockheight_start, + lease_expiry, + lease_commit_sig, + lease_chan_max_msat, + lease_chan_max_ppt))) { channel_internal_error(channel, "wallet_commit_channel failed" " (chan %s)", @@ -2617,7 +2856,12 @@ static void handle_commit_received(struct subd *dualopend, total_funding, funding_ours, feerate_funding, - psbt))) { + psbt, + lease_expiry, + lease_commit_sig, + lease_chan_max_msat, + lease_chan_max_ppt, + lease_blockheight_start))) { channel_internal_error(channel, "wallet_update_channel failed" " (chan %s)", @@ -2706,6 +2950,9 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_RBF_VALIDATE: handle_validate_rbf(dualopend, msg); return 0; + case WIRE_DUALOPEND_VALIDATE_LEASE: + handle_validate_lease(dualopend, msg); + return 0; case WIRE_DUALOPEND_FUNDING_SIGS: handle_peer_tx_sigs_msg(dualopend, msg); return 0; @@ -2715,6 +2962,9 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_PEER_LOCKED: handle_peer_locked(dualopend, msg); return 0; + case WIRE_DUALOPEND_DRY_RUN: + handle_dry_run_finished(dualopend, msg); + return 0; case WIRE_DUALOPEND_CHANNEL_LOCKED: if (tal_count(fds) != 3) return 3; @@ -2741,6 +2991,7 @@ static unsigned int dual_opend_msg(struct subd *dualopend, case WIRE_DUALOPEND_GOT_OFFER_REPLY: case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY: case WIRE_DUALOPEND_RBF_VALID: + case WIRE_DUALOPEND_VALIDATE_LEASE_REPLY: case WIRE_DUALOPEND_FAIL: case WIRE_DUALOPEND_PSBT_UPDATED: case WIRE_DUALOPEND_SEND_TX_SIGS: @@ -2769,6 +3020,118 @@ static unsigned int dual_opend_msg(struct subd *dualopend, return 0; } +#if DEVELOPER +static struct command_result *json_queryrates(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct node_id *id; + struct peer *peer; + struct channel *channel; + u32 *feerate_per_kw_funding; + u32 *feerate_per_kw; + struct amount_sat *amount, *request_amt; + struct wally_psbt *psbt; + struct open_attempt *oa; + u8 *msg; + struct command_result *res; + + if (!param(cmd, buffer, params, + p_req("id", param_node_id, &id), + p_req("amount", param_sat, &amount), + p_req("request_amt", param_sat, &request_amt), + p_opt("commitment_feerate", param_feerate, &feerate_per_kw), + p_opt("funding_feerate", param_feerate, &feerate_per_kw_funding), + NULL)) + return command_param_failed(); + + res = init_set_feerate(cmd, &feerate_per_kw, &feerate_per_kw_funding); + if (res) + return res; + + peer = peer_by_id(cmd->ld, id); + if (!peer) { + return command_fail(cmd, FUNDING_UNKNOWN_PEER, "Unknown peer"); + } + + /* We can't query rates for a peer we have a channel with */ + channel = peer_active_channel(peer); + if (channel) + return command_fail(cmd, LIGHTNINGD, "Peer in state %s," + " can't query peer's rates if already" + " have a channel", + channel_state_name(channel)); + + channel = peer_unsaved_channel(peer); + if (!channel || !channel->owner) + return command_fail(cmd, FUNDING_PEER_NOT_CONNECTED, + "Peer not connected"); + if (channel->open_attempt + || !list_empty(&channel->inflights)) + return command_fail(cmd, FUNDING_STATE_INVALID, + "Channel funding in-progress. %s", + channel_state_name(channel)); + + if (!feature_negotiated(cmd->ld->our_features, + peer->their_features, + OPT_DUAL_FUND)) { + return command_fail(cmd, FUNDING_V2_NOT_SUPPORTED, + "v2 openchannel not supported " + "by peer, can't query rates"); + } + + /* BOLT #2: + * - if both nodes advertised `option_support_large_channel`: + * - MAY set `funding_satoshis` greater than or equal to 2^24 satoshi. + * - otherwise: + * - MUST set `funding_satoshis` to less than 2^24 satoshi. + */ + if (!feature_negotiated(cmd->ld->our_features, + peer->their_features, OPT_LARGE_CHANNELS) + && amount_sat_greater(*amount, chainparams->max_funding)) + return command_fail(cmd, FUND_MAX_EXCEEDED, + "Amount exceeded %s", + type_to_string(tmpctx, struct amount_sat, + &chainparams->max_funding)); + + /* Get a new open_attempt going, keeps us from re-initing + * while looking */ + channel->opener = LOCAL; + channel->open_attempt = oa = new_channel_open_attempt(channel); + channel->channel_flags = OUR_CHANNEL_FLAGS; + oa->funding = *amount; + oa->cmd = cmd; + /* empty psbt to start */ + psbt = create_psbt(tmpctx, 0, 0, 0); + + msg = towire_dualopend_opener_init(NULL, + psbt, *amount, + oa->our_upfront_shutdown_script, + *feerate_per_kw, + *feerate_per_kw_funding, + channel->channel_flags, + *request_amt, + get_block_height(cmd->ld->topology), + true, + NULL); + + subd_send_msg(channel->owner, take(msg)); + return command_still_pending(cmd); + +} + +static const struct json_command queryrates_command = { + "dev-queryrates", + "channels", + json_queryrates, + "Ask a peer what their contribution and liquidity rates are" + " for the given {amount} and {requested_amt}" +}; + +AUTODATA(json_command, &queryrates_command); +#endif /* DEVELOPER */ + static const struct json_command openchannel_init_command = { "openchannel_init", "channels", @@ -2824,7 +3187,8 @@ static void start_fresh_dualopend(struct peer *peer, hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->unsaved_dbid, HSM_CAP_COMMITMENT_POINT - | HSM_CAP_SIGN_REMOTE_TX); + | HSM_CAP_SIGN_REMOTE_TX + | HSM_CAP_SIGN_WILL_FUND_OFFER); channel->owner = new_channel_subd(peer->ld, "lightning_dualopend", @@ -2877,7 +3241,7 @@ void peer_restart_dualopend(struct peer *peer, struct per_peer_state *pps, struct channel *channel) { - u32 max_to_self_delay; + u32 max_to_self_delay, blockheight; struct amount_msat min_effective_htlc_capacity; struct channel_config unused_config; struct channel_inflight *inflight, *first_inflight; @@ -2890,7 +3254,8 @@ void peer_restart_dualopend(struct peer *peer, } hsmfd = hsm_get_client_fd(peer->ld, &peer->id, channel->dbid, HSM_CAP_COMMITMENT_POINT - | HSM_CAP_SIGN_REMOTE_TX); + | HSM_CAP_SIGN_REMOTE_TX + | HSM_CAP_SIGN_WILL_FUND_OFFER); channel_set_owner(channel, new_channel_subd(peer->ld, "lightning_dualopend", @@ -2920,6 +3285,8 @@ void peer_restart_dualopend(struct peer *peer, inflight = channel_current_inflight(channel); assert(inflight); + blockheight = get_blockheight(channel->blockheight_states, + channel->opener, LOCAL); /* Get the first inflight to figure out the original feerate * for this channel. It's fine if it's the same as the current */ @@ -2959,7 +3326,12 @@ void peer_restart_dualopend(struct peer *peer, channel->remote_upfront_shutdown_script, inflight->remote_tx_sigs, channel->fee_states, - channel->channel_flags); + channel->channel_flags, + blockheight, + inflight->lease_expiry, + inflight->lease_commit_sig, + inflight->lease_chan_max_msat, + inflight->lease_chan_max_ppt); subd_send_msg(channel->owner, take(msg)); diff --git a/lightningd/gossip_control.c b/lightningd/gossip_control.c index 38ceac648123..7fa2b62a51a1 100644 --- a/lightningd/gossip_control.c +++ b/lightningd/gossip_control.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -141,6 +142,7 @@ static unsigned gossip_msg(struct subd *gossip, const u8 *msg, const int *fds) case WIRE_GOSSIPD_GET_STRIPPED_CUPDATE: case WIRE_GOSSIPD_GET_TXOUT_REPLY: case WIRE_GOSSIPD_OUTPOINT_SPENT: + case WIRE_GOSSIPD_NEW_LEASE_RATES: case WIRE_GOSSIPD_GET_INCOMING_CHANNELS: case WIRE_GOSSIPD_DEV_SET_MAX_SCIDS_ENCODE_SIZE: case WIRE_GOSSIPD_DEV_SUPPRESS: @@ -276,6 +278,16 @@ static void json_getnodes_reply(struct subd *gossip UNUSED, const u8 *reply, json_add_address(response, NULL, &nodes[i]->addresses[j]); } json_array_end(response); + if (nodes[i]->rates) { + json_object_start(response, "option_will_fund"); + json_add_lease_rates(response, nodes[i]->rates); + /* As a convenience, add a hexstring version + * of this info */ + json_add_string(response, "compact_lease", + lease_rates_tohex(tmpctx, + nodes[i]->rates)); + json_object_end(response); + } json_object_end(response); } json_array_end(response); @@ -460,6 +472,74 @@ static const struct json_command getroute_command = { }; AUTODATA(json_command, &getroute_command); +static struct command_result *json_setleaserates(struct command *cmd, + const char *buffer, + const jsmntok_t *obj UNNEEDED, + const jsmntok_t *params) +{ + struct json_stream *res; + struct lease_rates *rates; + struct amount_sat *lease_base_sat; + struct amount_msat *channel_fee_base_msat; + u32 *lease_basis, *channel_fee_max_ppt, *funding_weight; + + if (!param(cmd, buffer, params, + p_req("lease_fee_base_msat", param_sat, &lease_base_sat), + p_req("lease_fee_basis", param_number, &lease_basis), + p_req("funding_weight", param_number, &funding_weight), + p_req("channel_fee_max_base_msat", param_msat, + &channel_fee_base_msat), + p_req("channel_fee_max_proportional_thousandths", + param_number, &channel_fee_max_ppt), + NULL)) + return command_param_failed(); + + rates = tal(tmpctx, struct lease_rates); + rates->lease_fee_basis = *lease_basis; + rates->lease_fee_base_sat = lease_base_sat->satoshis; /* Raw: conversion */ + rates->channel_fee_max_base_msat = channel_fee_base_msat->millisatoshis; /* Raw: conversion */ + + rates->funding_weight = *funding_weight; + rates->channel_fee_max_proportional_thousandths + = *channel_fee_max_ppt; + + /* Gotta check that we didn't overflow */ + if (lease_base_sat->satoshis > rates->lease_fee_base_sat) + return command_fail_badparam(cmd, "lease_fee_base_msat", + buffer, params, "Overflow"); + + if (channel_fee_base_msat->millisatoshis > rates->channel_fee_max_base_msat) + return command_fail_badparam(cmd, "channel_fee_max_base_msat", + buffer, params, "Overflow"); + + /* Call gossipd, let them know we've got new rates */ + subd_send_msg(cmd->ld->gossip, + take(towire_gossipd_new_lease_rates(NULL, rates))); + + res = json_stream_success(cmd); + json_add_amount_sat_only(res, "lease_fee_base_msat", + amount_sat(rates->lease_fee_base_sat)); + json_add_num(res, "lease_fee_basis", rates->lease_fee_basis); + json_add_num(res, "funding_weight", rates->funding_weight); + json_add_amount_msat_only(res, "channel_fee_max_base_msat", + amount_msat(rates->channel_fee_max_base_msat)); + json_add_num(res, "channel_fee_max_proportional_thousandths", + rates->channel_fee_max_proportional_thousandths); + + return command_success(cmd, res); +} + +static const struct json_command setleaserates_command = { + "setleaserates", + "channels", + json_setleaserates, + "Called by plugin to set the node's present channel lease rates." + " Not to be set without having a plugin which can handle" + " `openchannel2` hooks.", +}; + +AUTODATA(json_command, &setleaserates_command); + static void json_add_halfchan(struct json_stream *response, const struct gossip_getchannels_entry *e, int idx) diff --git a/lightningd/gossip_msg.c b/lightningd/gossip_msg.c index bc827c938b91..f8e8c0cfb8fb 100644 --- a/lightningd/gossip_msg.c +++ b/lightningd/gossip_msg.c @@ -2,6 +2,7 @@ #include #include #include +#include #include struct gossip_getnodes_entry *fromwire_gossip_getnodes_entry(const tal_t *ctx, @@ -10,6 +11,7 @@ struct gossip_getnodes_entry *fromwire_gossip_getnodes_entry(const tal_t *ctx, u8 numaddresses, i; struct gossip_getnodes_entry *entry; u16 flen; + bool has_rates; entry = tal(ctx, struct gossip_getnodes_entry); fromwire_node_id(pptr, max, &entry->nodeid); @@ -34,6 +36,13 @@ struct gossip_getnodes_entry *fromwire_gossip_getnodes_entry(const tal_t *ctx, fromwire(pptr, max, entry->alias, ARRAY_SIZE(entry->alias)); fromwire(pptr, max, entry->color, ARRAY_SIZE(entry->color)); + has_rates = fromwire_u8(pptr, max); + if (has_rates) { + entry->rates = tal(entry, struct lease_rates); + fromwire_lease_rates(pptr, max, entry->rates); + } else + entry->rates = NULL; + return entry; } @@ -54,6 +63,12 @@ void towire_gossip_getnodes_entry(u8 **pptr, } towire(pptr, entry->alias, ARRAY_SIZE(entry->alias)); towire(pptr, entry->color, ARRAY_SIZE(entry->color)); + + if (entry->rates) { + towire_u8(pptr, 1); + towire_lease_rates(pptr, entry->rates); + } else + towire_u8(pptr, 0); } struct route_hop *fromwire_route_hop(const tal_t *ctx, diff --git a/lightningd/gossip_msg.h b/lightningd/gossip_msg.h index 5f405619212a..58b1e3703871 100644 --- a/lightningd/gossip_msg.h +++ b/lightningd/gossip_msg.h @@ -5,6 +5,7 @@ #include struct route_info; +struct lease_rates; struct gossip_getnodes_entry { struct node_id nodeid; @@ -13,6 +14,7 @@ struct gossip_getnodes_entry { struct wireaddr *addresses; u8 alias[32]; u8 color[3]; + struct lease_rates *rates; }; struct gossip_halfchannel_entry { diff --git a/lightningd/onchain_control.c b/lightningd/onchain_control.c index 759ae4180333..33faacbd2bc3 100644 --- a/lightningd/onchain_control.c +++ b/lightningd/onchain_control.c @@ -382,14 +382,15 @@ static void onchain_add_utxo(struct channel *channel, const u8 *msg) struct chain_coin_mvt *mvt; u32 blockheight; struct bitcoin_txid txid; - u32 outnum; + u32 outnum, csv_lock; struct amount_sat amount; struct pubkey *commitment_point; u8 *scriptPubkey; if (!fromwire_onchaind_add_utxo( tmpctx, msg, &txid, &outnum, &commitment_point, - &amount, &blockheight, &scriptPubkey)) { + &amount, &blockheight, &scriptPubkey, + &csv_lock)) { log_broken(channel->log, "onchaind gave invalid add_utxo message: %s", tal_hex(msg, msg)); @@ -399,10 +400,15 @@ static void onchain_add_utxo(struct channel *channel, const u8 *msg) assert(blockheight); outpointfilter_add(channel->peer->ld->wallet->owned_outpoints, &txid, outnum); + log_debug(channel->log, "adding utxo to watch %s:%u, csv %u", + type_to_string(tmpctx, struct bitcoin_txid, &txid), + outnum, csv_lock); + wallet_add_onchaind_utxo(channel->peer->ld->wallet, &txid, outnum, scriptPubkey, blockheight, amount, channel, - commitment_point); + commitment_point, + csv_lock); mvt = new_coin_deposit_sat(msg, "wallet", &txid, outnum, blockheight, amount); diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 394b64525cf4..edba50d8462d 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +106,7 @@ wallet_commit_channel(struct lightningd *ld, s64 final_key_idx; u64 static_remotekey_start; bool option_anchor_outputs; + u32 lease_start_blockheight = 0; /* No leases on v1 */ /* We cannot both be the fundee *and* have a `fundchannel_start` * command running! @@ -216,7 +218,10 @@ wallet_commit_channel(struct lightningd *ld, option_anchor_outputs, NUM_SIDES, /* closer not yet known */ uc->fc ? REASON_USER : REASON_REMOTE, - NULL); + NULL, + take(new_height_states(NULL, uc->fc ? LOCAL : REMOTE, + &lease_start_blockheight)), + 0, NULL, 0, 0); /* No leases on v1s */ /* Now we finally put it in the database. */ wallet_channel_insert(ld->wallet, channel); diff --git a/lightningd/peer_control.c b/lightningd/peer_control.c index 9d74abd01d8d..ac738274801b 100644 --- a/lightningd/peer_control.c +++ b/lightningd/peer_control.c @@ -1338,6 +1338,18 @@ static void update_channel_from_inflight(struct lightningd *ld, channel->funding = inflight->funding->total_funds; channel->our_funds = inflight->funding->our_funds; + /* Lease infos ! */ + channel->lease_expiry = inflight->lease_expiry; + channel->lease_commit_sig + = tal_steal(channel, inflight->lease_commit_sig); + channel->lease_chan_max_msat = inflight->lease_chan_max_msat; + channel->lease_chan_max_ppt = inflight->lease_chan_max_ppt; + + tal_free(channel->blockheight_states); + channel->blockheight_states = new_height_states(channel, + channel->opener, + &inflight->lease_blockheight_start); + /* Make a 'clone' of this tx */ psbt_copy = clone_psbt(channel, inflight->last_tx->psbt); channel_set_last_tx(channel, @@ -1651,7 +1663,7 @@ static struct command_result *json_close(struct command *cmd, struct channel *channel COMPILER_WANTS_INIT("gcc 7.3.0 fails, 8.3 OK"); unsigned int *timeout; const u8 *close_to_script = NULL; - bool close_script_set, wrong_funding_changed; + bool close_script_set, wrong_funding_changed, *force_lease_close; const char *fee_negotiation_step_str; struct bitcoin_outpoint *wrong_funding; char* end; @@ -1664,6 +1676,8 @@ static struct command_result *json_close(struct command *cmd, p_opt("fee_negotiation_step", param_string, &fee_negotiation_step_str), p_opt("wrong_funding", param_outpoint, &wrong_funding), + p_opt_def("force_lease_closed", param_bool, + &force_lease_close, false), NULL)) return command_param_failed(); @@ -1693,6 +1707,15 @@ static struct command_result *json_close(struct command *cmd, "Peer has no active channel"); } + if (!*force_lease_close && channel->opener != LOCAL + && get_block_height(cmd->ld->topology) < channel->lease_expiry) + return command_fail(cmd, LIGHTNINGD, + "Peer leased this channel from us, we" + " shouldn't close until lease has expired" + " (lease expires block %u," + " current block %u)", + channel->lease_expiry, + get_block_height(cmd->ld->topology)); /* If we've set a local shutdown script for this peer, and it's not the * default upfront script, try to close to a different channel. diff --git a/lightningd/peer_htlcs.c b/lightningd/peer_htlcs.c index d0887d65074b..7f93870d1245 100644 --- a/lightningd/peer_htlcs.c +++ b/lightningd/peer_htlcs.c @@ -1730,6 +1730,7 @@ void peer_sending_commitsig(struct channel *channel, const u8 *msg) { u64 commitnum; struct fee_states *fee_states; + struct height_states *blockheight_states; struct changed_htlc *changed_htlcs; size_t i, maxid = 0, num_local_added = 0; struct bitcoin_signature commit_sig; @@ -1741,9 +1742,11 @@ void peer_sending_commitsig(struct channel *channel, const u8 *msg) &commitnum, &pbase, &fee_states, + &blockheight_states, &changed_htlcs, &commit_sig, &htlc_sigs) - || !fee_states_valid(fee_states, channel->opener)) { + || !fee_states_valid(fee_states, channel->opener) + || !height_states_valid(blockheight_states, channel->opener)) { channel_internal_error(channel, "bad channel_sending_commitsig %s", tal_hex(channel, msg)); return; @@ -1786,6 +1789,9 @@ void peer_sending_commitsig(struct channel *channel, const u8 *msg) channel->opener, REMOTE)); + tal_free(channel->blockheight_states); + channel->blockheight_states = tal_steal(channel, blockheight_states); + if (!peer_save_commitsig_sent(channel, commitnum)) return; @@ -1921,6 +1927,7 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) { u64 commitnum; struct fee_states *fee_states; + struct height_states *blockheight_states; struct bitcoin_signature commit_sig, *htlc_sigs; struct added_htlc *added; struct fulfilled_htlc *fulfilled; @@ -1933,6 +1940,7 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) if (!fromwire_channeld_got_commitsig(msg, msg, &commitnum, &fee_states, + &blockheight_states, &commit_sig, &htlc_sigs, &added, @@ -1940,7 +1948,8 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) &failed, &changed, &tx) - || !fee_states_valid(fee_states, channel->opener)) { + || !fee_states_valid(fee_states, channel->opener) + || !height_states_valid(blockheight_states, channel->opener)) { channel_internal_error(channel, "bad fromwire_channeld_got_commitsig %s", tal_hex(channel, msg)); @@ -1969,8 +1978,9 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) log_debug(channel->log, "got commitsig %"PRIu64 - ": feerate %u, %zu added, %zu fulfilled, %zu failed, %zu changed", + ": feerate %u, blockheight: %u, %zu added, %zu fulfilled, %zu failed, %zu changed", commitnum, get_feerate(fee_states, channel->opener, LOCAL), + get_blockheight(blockheight_states, channel->opener, LOCAL), tal_count(added), tal_count(fulfilled), tal_count(failed), tal_count(changed)); @@ -2005,6 +2015,8 @@ void peer_got_commitsig(struct channel *channel, const u8 *msg) get_feerate(fee_states, channel->opener, LOCAL)); + tal_free(channel->blockheight_states); + channel->blockheight_states = tal_steal(channel, blockheight_states); /* Since we're about to send revoke, bump state again. */ if (!peer_sending_revocation(channel, added, fulfilled, failed, changed)) @@ -2081,6 +2093,7 @@ void peer_got_revoke(struct channel *channel, const u8 *msg) size_t i; struct lightningd *ld = channel->peer->ld; struct fee_states *fee_states; + struct height_states *blockheight_states; struct penalty_base *pbase; struct commitment_revocation_payload *payload; struct bitcoin_tx *penalty_tx; @@ -2089,10 +2102,12 @@ void peer_got_revoke(struct channel *channel, const u8 *msg) &revokenum, &per_commitment_secret, &next_per_commitment_point, &fee_states, + &blockheight_states, &changed, &pbase, &penalty_tx) - || !fee_states_valid(fee_states, channel->opener)) { + || !fee_states_valid(fee_states, channel->opener) + || !height_states_valid(blockheight_states, channel->opener)) { channel_internal_error(channel, "bad fromwire_channeld_got_revoke %s", tal_hex(channel, msg)); return; @@ -2155,6 +2170,9 @@ void peer_got_revoke(struct channel *channel, const u8 *msg) tal_free(channel->fee_states); channel->fee_states = tal_steal(channel, fee_states); + tal_free(channel->blockheight_states); + channel->blockheight_states = tal_steal(channel, blockheight_states); + /* FIXME: Check per_commitment_secret -> per_commit_point */ update_per_commit_point(channel, &next_per_commitment_point); diff --git a/lightningd/test/run-invoice-select-inchan.c b/lightningd/test/run-invoice-select-inchan.c index 8709fb588644..409038e56331 100644 --- a/lightningd/test/run-invoice-select-inchan.c +++ b/lightningd/test/run-invoice-select-inchan.c @@ -423,6 +423,11 @@ void merkle_tlv(const struct tlv_field *fields UNNEEDED, struct sha256 *merkle U struct bolt11 *new_bolt11(const tal_t *ctx UNNEEDED, const struct amount_msat *msat TAKES UNNEEDED) { fprintf(stderr, "new_bolt11 called!\n"); abort(); } +/* Generated stub for new_height_states */ +struct height_states *new_height_states(const tal_t *ctx UNNEEDED, + enum side opener UNNEEDED, + const u32 *blockheight UNNEEDED) +{ fprintf(stderr, "new_height_states called!\n"); abort(); } /* Generated stub for new_reltimer_ */ struct oneshot *new_reltimer_(struct timers *timers UNNEEDED, const tal_t *ctx UNNEEDED, diff --git a/onchaind/onchaind.c b/onchaind/onchaind.c index 22b6413ce780..6baacd2b1e89 100644 --- a/onchaind/onchaind.c +++ b/onchaind/onchaind.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ /* stdin == requests */ #define REQ_FD STDIN_FILENO #define HSM_FD 3 +#define max(a, b) ((a) > (b) ? (a) : (b)) /* Required in various places: keys for commitment transaction. */ static const struct keyset *keyset; @@ -2557,7 +2559,8 @@ static void get_anchor_scriptpubkeys(const tal_t *ctx, u8 **anchor) } static u8 *scriptpubkey_to_remote(const tal_t *ctx, - const struct pubkey *remotekey) + const struct pubkey *remotekey, + u32 csv_lock) { /* BOLT #3: * @@ -2574,18 +2577,73 @@ static u8 *scriptpubkey_to_remote(const tal_t *ctx, if (option_anchor_outputs) { return scriptpubkey_p2wsh(ctx, anchor_to_remote_redeem(tmpctx, - remotekey)); + remotekey, + csv_lock)); } else { return scriptpubkey_p2wpkh(ctx, remotekey); } } +static void our_unilateral_to_us(struct tracked_output ***outs, + const struct tx_parts *tx, + u32 tx_blockheight, + size_t index, + struct amount_sat amt, + u16 sequence, + const u8 *local_scriptpubkey, + const u8 *local_wscript, + bool is_replay) +{ + struct bitcoin_tx *to_us; + struct tracked_output *out; + enum tx_type tx_type = OUR_DELAYED_RETURN_TO_WALLET; + + /* BOLT #5: + * + * A node: + * - upon discovering its *local commitment + * transaction*: + * - SHOULD spend the `to_local` output to a + * convenient address. + * - MUST wait until the `OP_CHECKSEQUENCEVERIFY` + * delay has passed (as specified by the remote + * node's `to_self_delay` field) before spending + * the output. + */ + out = new_tracked_output(outs, &tx->txid, tx_blockheight, + OUR_UNILATERAL, index, + amt, + DELAYED_OUTPUT_TO_US, + NULL, NULL, NULL); + /* BOLT #3: + * + * The output is spent by an input with + * `nSequence` field set to `to_self_delay` (which can + * only be valid after that duration has passed) and + * witness: + * + * <> + */ + to_us = tx_to_us(out, delayed_payment_to_us, out, + sequence, 0, NULL, 0, + local_wscript, &tx_type, + delayed_to_us_feerate); + + /* BOLT #5: + * + * Note: if the output is spent (as recommended), the + * output is *resolved* by the spending transaction + */ + propose_resolution(out, to_us, sequence, tx_type, is_replay); +} + static void handle_our_unilateral(const struct tx_parts *tx, u32 tx_blockheight, const struct basepoints basepoints[NUM_SIDES], const struct htlc_stub *htlcs, const bool *tell_if_missing, const bool *tell_immediately, + const enum side opener, const struct bitcoin_signature *remote_htlc_sigs, struct tracked_output **outs, bool is_replay) @@ -2642,14 +2700,15 @@ static void handle_our_unilateral(const struct tx_parts *tx, type_to_string(tmpctx, struct pubkey, &keyset->other_htlc_key)); - local_wscript = to_self_wscript(tmpctx, to_self_delay[LOCAL], keyset); + local_wscript = to_self_wscript(tmpctx, to_self_delay[LOCAL], + 1, keyset); /* Figure out what to-us output looks like. */ script[LOCAL] = scriptpubkey_p2wsh(tmpctx, local_wscript); /* Figure out what direct to-them output looks like. */ script[REMOTE] = scriptpubkey_to_remote(tmpctx, - &keyset->other_payment_key); + &keyset->other_payment_key, 1); /* Calculate all the HTLC scripts so we can match them */ htlc_scripts = derive_htlc_scripts(htlcs, LOCAL); @@ -2697,47 +2756,10 @@ static void handle_our_unilateral(const struct tx_parts *tx, } else if (script[LOCAL] && wally_tx_output_scripteq(tx->outputs[i], script[LOCAL])) { - struct bitcoin_tx *to_us; - enum tx_type tx_type = OUR_DELAYED_RETURN_TO_WALLET; - - /* BOLT #5: - * - * A node: - * - upon discovering its *local commitment - * transaction*: - * - SHOULD spend the `to_local` output to a - * convenient address. - * - MUST wait until the `OP_CHECKSEQUENCEVERIFY` - * delay has passed (as specified by the remote - * node's `to_self_delay` field) before spending - * the output. - */ - out = new_tracked_output(&outs, &tx->txid, tx_blockheight, - OUR_UNILATERAL, i, - amt, - DELAYED_OUTPUT_TO_US, - NULL, NULL, NULL); - /* BOLT #3: - * - * The output is spent by an input with - * `nSequence` field set to `to_self_delay` (which can - * only be valid after that duration has passed) and - * witness: - * - * <> - */ - to_us = tx_to_us(out, delayed_payment_to_us, out, - to_self_delay[LOCAL], 0, NULL, 0, - local_wscript, &tx_type, - delayed_to_us_feerate); - - /* BOLT #5: - * - * Note: if the output is spent (as recommended), the - * output is *resolved* by the spending transaction - */ - propose_resolution(out, to_us, to_self_delay[LOCAL], - tx_type, is_replay); + our_unilateral_to_us(&outs, tx, tx_blockheight, + i, amt, to_self_delay[LOCAL], + script[LOCAL], + local_wscript, is_replay); script[LOCAL] = NULL; add_amt(&our_outs, amt); @@ -2794,6 +2816,81 @@ static void handle_our_unilateral(const struct tx_parts *tx, matches = match_htlc_output(tmpctx, tx->outputs[i], htlc_scripts); /* FIXME: limp along when this happens! */ if (tal_count(matches) == 0) { + bool found = false; + + /* Maybe they're using option_will_fund? */ + if (opener == REMOTE && script[LOCAL]) { + status_debug("Grinding for our to_local"); + /* We already tried `1` */ + for (size_t csv = 2; + csv <= LEASE_RATE_DURATION; + csv++) { + + local_wscript + = to_self_wscript(tmpctx, + to_self_delay[LOCAL], + csv, keyset); + + script[LOCAL] + = scriptpubkey_p2wsh(tmpctx, + local_wscript); + if (!wally_tx_output_scripteq( + tx->outputs[i], + script[LOCAL])) + continue; + + our_unilateral_to_us(&outs, tx, + tx_blockheight, + i, amt, + max(to_self_delay[LOCAL], csv), + script[LOCAL], + local_wscript, + is_replay); + + script[LOCAL] = NULL; + add_amt(&our_outs, amt); + found = true; + break; + } + } else if (opener == LOCAL && script[REMOTE]) { + status_debug("Grinding for to_remote (ours)"); + /* We already tried `1` */ + for (size_t csv = 2; + csv <= LEASE_RATE_DURATION; + csv++) { + + script[REMOTE] + = scriptpubkey_to_remote(tmpctx, + &keyset->other_payment_key, + csv); + + if (!wally_tx_output_scripteq(tx->outputs[i], script[REMOTE])) + continue; + + /* BOLT #5: + * + * - MAY ignore the `to_remote` output. + * - Note: No action is required by the local + * node, as `to_remote` is considered *resolved* + * by the commitment transaction itself. + */ + out = new_tracked_output(&outs, &tx->txid, tx_blockheight, + OUR_UNILATERAL, i, + amt, + OUTPUT_TO_THEM, + NULL, NULL, NULL); + ignore_output(out); + script[REMOTE] = NULL; + add_amt(&their_outs, amt); + found = true; + break; + } + } + + + if (found) + continue; + onchain_annotate_txout(&tx->txid, i, TX_CHANNEL_PENALTY | TX_THEIRS); status_failed(STATUS_FAIL_INTERNAL_ERROR, "Could not find resolution for output %zu", @@ -2863,7 +2960,8 @@ static void handle_our_unilateral(const struct tx_parts *tx, /* We produce individual penalty txs. It's less efficient, but avoids them * using HTLC txs to block our penalties for long enough to pass the CSV * delay */ -static void steal_to_them_output(struct tracked_output *out, bool is_replay) +static void steal_to_them_output(struct tracked_output *out, + u32 csv, bool is_replay) { u8 *wscript; struct bitcoin_tx *tx; @@ -2876,7 +2974,7 @@ static void steal_to_them_output(struct tracked_output *out, bool is_replay) * * 1 */ - wscript = bitcoin_wscript_to_local(tmpctx, to_self_delay[REMOTE], + wscript = bitcoin_wscript_to_local(tmpctx, to_self_delay[REMOTE], csv, &keyset->self_revocation_key, &keyset->self_delayed_payment_key); @@ -2914,7 +3012,8 @@ static void tell_wallet_to_remote(const struct tx_parts *tx, u32 tx_blockheight, const u8 *scriptpubkey, const struct pubkey *per_commit_point, - bool option_static_remotekey) + bool option_static_remotekey, + u32 csv_lock) { struct amount_asset asset = wally_tx_output_get_amount(tx->outputs[outnum]); struct amount_sat amt; @@ -2932,7 +3031,8 @@ static void tell_wallet_to_remote(const struct tx_parts *tx, per_commit_point, amt, tx_blockheight, - scriptpubkey))); + scriptpubkey, + csv_lock))); } /* When a 'cheat' transaction comes through, our accounting is @@ -2966,6 +3066,47 @@ static void update_ledger_cheat(const struct bitcoin_txid *txid, blockheight, amt, true))); } +static void their_unilateral_local(struct tracked_output ***outs, + const struct tx_parts *tx, + u32 tx_blockheight, + size_t index, + struct amount_sat amt, + const u8 *local_scriptpubkey, + enum tx_type tx_type, + bool is_replay, + u32 csv_lock) +{ + struct tracked_output *out; + /* BOLT #5: + * + * - MAY take no action in regard to the associated + * `to_remote`, which is simply a P2WPKH output to + * the *local node*. + * - Note: `to_remote` is considered *resolved* by the + * commitment transaction itself. + */ + out = new_tracked_output(outs, + &tx->txid, + tx_blockheight, + tx_type, + index, amt, + OUTPUT_TO_US, + NULL, NULL, + NULL); + ignore_output(out); + + if (!is_replay) + record_channel_withdrawal(&tx->txid, tx_blockheight, out); + + tell_wallet_to_remote(tx, index, + tx_blockheight, + local_scriptpubkey, + remote_per_commitment_point, + commit_num >= static_remotekey_start[REMOTE], + csv_lock); +} + + /* BOLT #5: * * If any node tries to cheat by broadcasting an outdated commitment @@ -2980,6 +3121,7 @@ static void handle_their_cheat(const struct tx_parts *tx, const struct htlc_stub *htlcs, const bool *tell_if_missing, const bool *tell_immediately, + const enum side opener, struct tracked_output **outs, bool is_replay) { @@ -3080,14 +3222,15 @@ static void handle_their_cheat(const struct tx_parts *tx, static_remotekey_start[LOCAL], static_remotekey_start[REMOTE]); - remote_wscript = to_self_wscript(tmpctx, to_self_delay[REMOTE], keyset); + remote_wscript = to_self_wscript(tmpctx, to_self_delay[REMOTE], + 1, keyset); /* Figure out what to-them output looks like. */ script[REMOTE] = scriptpubkey_p2wsh(tmpctx, remote_wscript); /* Figure out what direct to-us output looks like. */ script[LOCAL] = scriptpubkey_to_remote(tmpctx, - &keyset->other_payment_key); + &keyset->other_payment_key, 1); /* Calculate all the HTLC scripts so we can match them */ htlc_scripts = derive_htlc_scripts(htlcs, REMOTE); @@ -3134,28 +3277,10 @@ static void handle_their_cheat(const struct tx_parts *tx, if (script[LOCAL] && wally_tx_output_scripteq(tx->outputs[i], script[LOCAL])) { - /* BOLT #5: - * - * - MAY take no action regarding the _local node's - * main output_, as this is a simple P2WPKH output - * to itself. - * - Note: this output is considered *resolved* by - * the commitment transaction itself. - */ - out = new_tracked_output(&outs, &tx->txid, tx_blockheight, - THEIR_REVOKED_UNILATERAL, - i, amt, - OUTPUT_TO_US, NULL, NULL, NULL); - ignore_output(out); - - if (!is_replay) - record_channel_withdrawal(&tx->txid, tx_blockheight, out); - - tell_wallet_to_remote(tx, i, - tx_blockheight, - script[LOCAL], - remote_per_commitment_point, - commit_num >= static_remotekey_start[REMOTE]); + their_unilateral_local(&outs, tx, tx_blockheight, + i, amt, script[LOCAL], + THEIR_REVOKED_UNILATERAL, + is_replay, 1); script[LOCAL] = NULL; add_amt(&total_outs, amt); continue; @@ -3173,7 +3298,7 @@ static void handle_their_cheat(const struct tx_parts *tx, amt, DELAYED_CHEAT_OUTPUT_TO_THEM, NULL, NULL, NULL); - steal_to_them_output(out, is_replay); + steal_to_them_output(out, 1, is_replay); script[REMOTE] = NULL; add_amt(&total_outs, amt); continue; @@ -3208,7 +3333,71 @@ static void handle_their_cheat(const struct tx_parts *tx, matches = match_htlc_output(tmpctx, tx->outputs[i], htlc_scripts); if (tal_count(matches) == 0) { - status_broken("Could not find resolution for output %zu: did *we* cheat?", i); + bool found = false; + if (opener == REMOTE && script[LOCAL]) { + status_debug("Grinding for commitment to_remote" + " (ours)"); + /* We already tried `1` */ + for (size_t csv = 2; + csv <= LEASE_RATE_DURATION; + csv++) { + script[LOCAL] + = scriptpubkey_to_remote(tmpctx, + &keyset->other_payment_key, + csv); + if (!wally_tx_output_scripteq( + tx->outputs[i], + script[LOCAL])) + continue; + + their_unilateral_local(&outs, tx, + tx_blockheight, + i, amt, + script[LOCAL], + THEIR_REVOKED_UNILATERAL, + is_replay, + csv); + script[LOCAL] = NULL; + add_amt(&total_outs, amt); + found = true; + break; + } + } else if (opener == LOCAL && script[REMOTE]) { + status_debug("Grinding for commitment to_local" + " (theirs)"); + for (size_t csv = 2; + csv <= LEASE_RATE_DURATION; + csv++) { + remote_wscript + = to_self_wscript(tmpctx, + to_self_delay[REMOTE], + csv, keyset); + script[REMOTE] + = scriptpubkey_p2wsh(tmpctx, + remote_wscript); + + + if (!wally_tx_output_scripteq(tx->outputs[i], script[REMOTE])) + continue; + + out = new_tracked_output(&outs, &tx->txid, tx_blockheight, + THEIR_REVOKED_UNILATERAL, i, + amt, + DELAYED_CHEAT_OUTPUT_TO_THEM, + NULL, NULL, NULL); + steal_to_them_output(out, csv, + is_replay); + script[REMOTE] = NULL; + add_amt(&total_outs, amt); + found = true; + break; + } + } + + if (!found) + status_broken("Could not find resolution" + " for output %zu: did" + " *we* cheat?", i); continue; } @@ -3277,6 +3466,7 @@ static void handle_their_unilateral(const struct tx_parts *tx, const struct htlc_stub *htlcs, const bool *tell_if_missing, const bool *tell_immediately, + const enum side opener, struct tracked_output **outs, bool is_replay) { @@ -3362,25 +3552,9 @@ static void handle_their_unilateral(const struct tx_parts *tx, type_to_string(tmpctx, struct pubkey, &keyset->other_htlc_key)); - remote_wscript = to_self_wscript(tmpctx, to_self_delay[REMOTE], keyset); - - /* Figure out what to-them output looks like. */ - script[REMOTE] = scriptpubkey_p2wsh(tmpctx, remote_wscript); - - /* Figure out what direct to-us output looks like. */ - script[LOCAL] = scriptpubkey_to_remote(tmpctx, - &keyset->other_payment_key); - /* Calculate all the HTLC scripts so we can match them */ htlc_scripts = derive_htlc_scripts(htlcs, REMOTE); - status_debug("Script to-them: %u: %s (%s)", - to_self_delay[REMOTE], - tal_hex(tmpctx, script[REMOTE]), - tal_hex(tmpctx, remote_wscript)); - status_debug("Script to-me: %s", - tal_hex(tmpctx, script[LOCAL])); - get_anchor_scriptpubkeys(tmpctx, anchor); for (i = 0; i < tal_count(tx->outputs); i++) { @@ -3391,6 +3565,21 @@ static void handle_their_unilateral(const struct tx_parts *tx, tx->outputs[i]->script_len)); } + remote_wscript = to_self_wscript(tmpctx, to_self_delay[REMOTE], + 1, keyset); + script[REMOTE] = scriptpubkey_p2wsh(tmpctx, remote_wscript); + + script[LOCAL] = scriptpubkey_to_remote(tmpctx, + &keyset->other_payment_key, + 1); + + status_debug("Script to-them: %u: %s (%s)", + to_self_delay[REMOTE], + tal_hex(tmpctx, script[REMOTE]), + tal_hex(tmpctx, remote_wscript)); + status_debug("Script to-me: %s", + tal_hex(tmpctx, script[LOCAL])); + for (i = 0; i < tal_count(tx->outputs); i++) { struct tracked_output *out; const size_t *matches; @@ -3414,28 +3603,11 @@ static void handle_their_unilateral(const struct tx_parts *tx, } else if (script[LOCAL] && wally_tx_output_scripteq(tx->outputs[i], script[LOCAL])) { - /* BOLT #5: - * - * - MAY take no action in regard to the associated - * `to_remote`, which is simply a P2WPKH output to - * the *local node*. - * - Note: `to_remote` is considered *resolved* by the - * commitment transaction itself. - */ - out = new_tracked_output(&outs, &tx->txid, tx_blockheight, - THEIR_UNILATERAL, - i, amt, - OUTPUT_TO_US, NULL, NULL, NULL); - ignore_output(out); + their_unilateral_local(&outs, tx, tx_blockheight, + i, amt, script[LOCAL], + THEIR_UNILATERAL, + is_replay, 1); - if (!is_replay) - record_channel_withdrawal(&tx->txid, tx_blockheight, out); - - tell_wallet_to_remote(tx, i, - tx_blockheight, - script[LOCAL], - remote_per_commitment_point, - commit_num >= static_remotekey_start[REMOTE]); script[LOCAL] = NULL; add_amt(&our_outs, amt); continue; @@ -3489,10 +3661,75 @@ static void handle_their_unilateral(const struct tx_parts *tx, } matches = match_htlc_output(tmpctx, tx->outputs[i], htlc_scripts); - if (tal_count(matches) == 0) + if (tal_count(matches) == 0) { + bool found = false; + + /* We need to hunt for it (option_will_fund?) */ + if (opener == REMOTE && script[LOCAL]) { + status_debug("Grinding for commitment to_remote" + " (ours)"); + /* We already tried `1` */ + for (size_t csv = 2; + csv <= LEASE_RATE_DURATION; + csv++) { + script[LOCAL] + = scriptpubkey_to_remote(tmpctx, + &keyset->other_payment_key, + csv); + if (!wally_tx_output_scripteq( + tx->outputs[i], + script[LOCAL])) + continue; + + their_unilateral_local(&outs, tx, + tx_blockheight, + i, amt, + script[LOCAL], + THEIR_UNILATERAL, + is_replay, csv); + script[LOCAL] = NULL; + add_amt(&our_outs, amt); + found = true; + break; + } + } else if (opener == LOCAL && script[REMOTE]) { + status_debug("Grinding for commitment to_local" + " (theirs)"); + /* We already tried `1` */ + for (size_t csv = 2; + csv <= LEASE_RATE_DURATION; + csv++) { + remote_wscript + = to_self_wscript(tmpctx, + to_self_delay[REMOTE], + csv, keyset); + script[REMOTE] + = scriptpubkey_p2wsh(tmpctx, + remote_wscript); + + + if (!wally_tx_output_scripteq(tx->outputs[i], script[REMOTE])) + continue; + + out = new_tracked_output(&outs, &tx->txid, tx_blockheight, + THEIR_UNILATERAL, i, + amt, + DELAYED_OUTPUT_TO_THEM, + NULL, NULL, NULL); + ignore_output(out); + add_amt(&their_outs, amt); + found = true; + break; + } + } + + if (found) + continue; + status_failed(STATUS_FAIL_INTERNAL_ERROR, "Could not find resolution for output %zu", i); + } if (matches_direction(matches, htlcs) == LOCAL) { /* BOLT #5: @@ -3622,58 +3859,66 @@ static void handle_unknown_commitment(const struct tx_parts *tx, local_scripts[0] = NULL; } - /* Other possible local script is for option_static_remotekey */ - local_scripts[1] = scriptpubkey_to_remote(tmpctx, - &basepoints[LOCAL].payment); + /* For option_will_fund, we need to figure out what CSV lock was used */ + for (size_t csv = 1; csv <= LEASE_RATE_DURATION; csv++) { - for (size_t i = 0; i < tal_count(tx->outputs); i++) { - struct tracked_output *out; - struct amount_asset asset = wally_tx_output_get_amount(tx->outputs[i]); - struct amount_sat amt; - int which_script; + /* Other possible local script is for option_static_remotekey */ + local_scripts[1] = scriptpubkey_to_remote(tmpctx, + &basepoints[LOCAL].payment, + csv); - assert(amount_asset_is_main(&asset)); - amt = amount_asset_to_sat(&asset); + for (size_t i = 0; i < tal_count(tx->outputs); i++) { + struct tracked_output *out; + struct amount_asset asset = wally_tx_output_get_amount(tx->outputs[i]); + struct amount_sat amt; + int which_script; - /* Elements can have empty output scripts (fee output) */ - if (local_scripts[0] - && wally_tx_output_scripteq(tx->outputs[i], local_scripts[0])) - which_script = 0; - else if (local_scripts[1] - && wally_tx_output_scripteq(tx->outputs[i], - local_scripts[1])) - which_script = 1; - else - continue; + assert(amount_asset_is_main(&asset)); + amt = amount_asset_to_sat(&asset); + + /* Elements can have empty output scripts (fee output) */ + if (local_scripts[0] + && wally_tx_output_scripteq(tx->outputs[i], local_scripts[0])) + which_script = 0; + else if (local_scripts[1] + && wally_tx_output_scripteq(tx->outputs[i], + local_scripts[1])) + which_script = 1; + else + continue; - /* BOLT #5: - * - * - MAY take no action in regard to the associated - * `to_remote`, which is simply a P2WPKH output to - * the *local node*. - * - Note: `to_remote` is considered *resolved* by the - * commitment transaction itself. - */ - out = new_tracked_output(&outs, &tx->txid, tx_blockheight, - UNKNOWN_UNILATERAL, - i, amt, - OUTPUT_TO_US, NULL, NULL, NULL); - ignore_output(out); + /* BOLT #5: + * + * - MAY take no action in regard to the associated + * `to_remote`, which is simply a P2WPKH output to + * the *local node*. + * - Note: `to_remote` is considered *resolved* by the + * commitment transaction itself. + */ + out = new_tracked_output(&outs, &tx->txid, tx_blockheight, + UNKNOWN_UNILATERAL, + i, amt, + OUTPUT_TO_US, NULL, NULL, NULL); + ignore_output(out); - if (!is_replay) - record_channel_withdrawal(&tx->txid, tx_blockheight, out); + if (!is_replay) + record_channel_withdrawal(&tx->txid, tx_blockheight, out); - add_amt(&amt_salvaged, amt); + add_amt(&amt_salvaged, amt); - tell_wallet_to_remote(tx, i, - tx_blockheight, - local_scripts[which_script], - possible_remote_per_commitment_point, - which_script == 1); - local_scripts[0] = local_scripts[1] = NULL; - to_us_output = i; + tell_wallet_to_remote(tx, i, + tx_blockheight, + local_scripts[which_script], + possible_remote_per_commitment_point, + which_script == 1, + csv); + local_scripts[0] = local_scripts[1] = NULL; + to_us_output = i; + goto script_found; + } } +script_found: if (to_us_output == -1) { status_broken("FUNDS LOST. Unknown commitment #%"PRIu64"!", commit_num); @@ -3862,6 +4107,7 @@ int main(int argc, char *argv[]) basepoints, htlcs, tell_if_missing, tell_immediately, + opener, remote_htlc_sigs, outs, open_is_replay); @@ -3880,6 +4126,7 @@ int main(int argc, char *argv[]) basepoints, htlcs, tell_if_missing, tell_immediately, + opener, outs, open_is_replay); /* BOLT #5: @@ -3899,6 +4146,7 @@ int main(int argc, char *argv[]) htlcs, tell_if_missing, tell_immediately, + opener, outs, open_is_replay); } else if (commit_num == revocations_received(&shachain) + 1) { @@ -3909,6 +4157,7 @@ int main(int argc, char *argv[]) htlcs, tell_if_missing, tell_immediately, + opener, outs, open_is_replay); } else { diff --git a/onchaind/onchaind_wire.csv b/onchaind/onchaind_wire.csv index 926ffb3924de..694ac7498dd4 100644 --- a/onchaind/onchaind_wire.csv +++ b/onchaind/onchaind_wire.csv @@ -119,6 +119,7 @@ msgdata,onchaind_add_utxo,value,amount_sat, msgdata,onchaind_add_utxo,blockheight,u32, msgdata,onchaind_add_utxo,len,u16, msgdata,onchaind_add_utxo,scriptpubkey,u8,len +msgdata,onchaind_add_utxo,csv_lock,u32, # master -> onchaind: do you have a memleak? msgtype,onchaind_dev_memleak,5033 diff --git a/onchaind/onchaind_wiregen.c b/onchaind/onchaind_wiregen.c index 381c0376cdd6..b2daec9a26c3 100644 --- a/onchaind/onchaind_wiregen.c +++ b/onchaind/onchaind_wiregen.c @@ -476,7 +476,7 @@ bool fromwire_onchaind_all_irrevocably_resolved(const void *p) /* WIRE: ONCHAIND_ADD_UTXO */ /* onchaind->master: hey */ -u8 *towire_onchaind_add_utxo(const tal_t *ctx, const struct bitcoin_txid *prev_out_tx, u32 prev_out_index, const struct pubkey *per_commit_point, struct amount_sat value, u32 blockheight, const u8 *scriptpubkey) +u8 *towire_onchaind_add_utxo(const tal_t *ctx, const struct bitcoin_txid *prev_out_tx, u32 prev_out_index, const struct pubkey *per_commit_point, struct amount_sat value, u32 blockheight, const u8 *scriptpubkey, u32 csv_lock) { u16 len = tal_count(scriptpubkey); u8 *p = tal_arr(ctx, u8, 0); @@ -494,10 +494,11 @@ u8 *towire_onchaind_add_utxo(const tal_t *ctx, const struct bitcoin_txid *prev_o towire_u32(&p, blockheight); towire_u16(&p, len); towire_u8_array(&p, scriptpubkey, len); + towire_u32(&p, csv_lock); return memcheck(p, tal_count(p)); } -bool fromwire_onchaind_add_utxo(const tal_t *ctx, const void *p, struct bitcoin_txid *prev_out_tx, u32 *prev_out_index, struct pubkey **per_commit_point, struct amount_sat *value, u32 *blockheight, u8 **scriptpubkey) +bool fromwire_onchaind_add_utxo(const tal_t *ctx, const void *p, struct bitcoin_txid *prev_out_tx, u32 *prev_out_index, struct pubkey **per_commit_point, struct amount_sat *value, u32 *blockheight, u8 **scriptpubkey, u32 *csv_lock) { u16 len; @@ -520,6 +521,7 @@ bool fromwire_onchaind_add_utxo(const tal_t *ctx, const void *p, struct bitcoin_ // 2nd case scriptpubkey *scriptpubkey = len ? tal_arr(ctx, u8, len) : NULL; fromwire_u8_array(&cursor, &plen, *scriptpubkey, len); + *csv_lock = fromwire_u32(&cursor, &plen); return cursor != NULL; } @@ -637,4 +639,4 @@ bool fromwire_onchaind_notify_coin_mvt(const void *p, struct chain_coin_mvt *mvt fromwire_chain_coin_mvt(&cursor, &plen, mvt); return cursor != NULL; } -// SHA256STAMP:66e19538be7f5a9e9076bfe995a9bf0cbb5d303df8f6c383e427c11ef2e85e2e +// SHA256STAMP:a1bc391f00c9cabd0194111f21966b39dcd1ab43819c0e126f0f390783e64980 diff --git a/onchaind/onchaind_wiregen.h b/onchaind/onchaind_wiregen.h index 48692bae739b..e1ea9fc709d9 100644 --- a/onchaind/onchaind_wiregen.h +++ b/onchaind/onchaind_wiregen.h @@ -132,8 +132,8 @@ bool fromwire_onchaind_all_irrevocably_resolved(const void *p); /* WIRE: ONCHAIND_ADD_UTXO */ /* onchaind->master: hey */ -u8 *towire_onchaind_add_utxo(const tal_t *ctx, const struct bitcoin_txid *prev_out_tx, u32 prev_out_index, const struct pubkey *per_commit_point, struct amount_sat value, u32 blockheight, const u8 *scriptpubkey); -bool fromwire_onchaind_add_utxo(const tal_t *ctx, const void *p, struct bitcoin_txid *prev_out_tx, u32 *prev_out_index, struct pubkey **per_commit_point, struct amount_sat *value, u32 *blockheight, u8 **scriptpubkey); +u8 *towire_onchaind_add_utxo(const tal_t *ctx, const struct bitcoin_txid *prev_out_tx, u32 prev_out_index, const struct pubkey *per_commit_point, struct amount_sat value, u32 blockheight, const u8 *scriptpubkey, u32 csv_lock); +bool fromwire_onchaind_add_utxo(const tal_t *ctx, const void *p, struct bitcoin_txid *prev_out_tx, u32 *prev_out_index, struct pubkey **per_commit_point, struct amount_sat *value, u32 *blockheight, u8 **scriptpubkey, u32 *csv_lock); /* WIRE: ONCHAIND_DEV_MEMLEAK */ /* master -> onchaind: do you have a memleak? */ @@ -161,4 +161,4 @@ bool fromwire_onchaind_notify_coin_mvt(const void *p, struct chain_coin_mvt *mvt #endif /* LIGHTNING_ONCHAIND_ONCHAIND_WIREGEN_H */ -// SHA256STAMP:66e19538be7f5a9e9076bfe995a9bf0cbb5d303df8f6c383e427c11ef2e85e2e +// SHA256STAMP:a1bc391f00c9cabd0194111f21966b39dcd1ab43819c0e126f0f390783e64980 diff --git a/onchaind/test/run-grind_feerate-bug.c b/onchaind/test/run-grind_feerate-bug.c index f6a77c2ecb62..15dbcb75d746 100644 --- a/onchaind/test/run-grind_feerate-bug.c +++ b/onchaind/test/run-grind_feerate-bug.c @@ -213,6 +213,7 @@ void subdaemon_setup(int argc UNNEEDED, char *argv[]) /* Generated stub for to_self_wscript */ u8 *to_self_wscript(const tal_t *ctx UNNEEDED, u16 to_self_delay UNNEEDED, + u32 csv UNNEEDED, const struct keyset *keyset UNNEEDED) { fprintf(stderr, "to_self_wscript called!\n"); abort(); } /* Generated stub for towire */ @@ -234,7 +235,7 @@ u8 *towire_hsmd_sign_penalty_to_us(const tal_t *ctx UNNEEDED, const struct secre u8 *towire_hsmd_sign_remote_htlc_to_us(const tal_t *ctx UNNEEDED, const struct pubkey *remote_per_commitment_point UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, bool option_anchor_outputs UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_remote_htlc_to_us called!\n"); abort(); } /* Generated stub for towire_onchaind_add_utxo */ -u8 *towire_onchaind_add_utxo(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *prev_out_tx UNNEEDED, u32 prev_out_index UNNEEDED, const struct pubkey *per_commit_point UNNEEDED, struct amount_sat value UNNEEDED, u32 blockheight UNNEEDED, const u8 *scriptpubkey UNNEEDED) +u8 *towire_onchaind_add_utxo(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *prev_out_tx UNNEEDED, u32 prev_out_index UNNEEDED, const struct pubkey *per_commit_point UNNEEDED, struct amount_sat value UNNEEDED, u32 blockheight UNNEEDED, const u8 *scriptpubkey UNNEEDED, u32 csv_lock UNNEEDED) { fprintf(stderr, "towire_onchaind_add_utxo called!\n"); abort(); } /* Generated stub for towire_onchaind_all_irrevocably_resolved */ u8 *towire_onchaind_all_irrevocably_resolved(const tal_t *ctx UNNEEDED) diff --git a/onchaind/test/run-grind_feerate.c b/onchaind/test/run-grind_feerate.c index 0a9b3d4ca945..61bdbeda771d 100644 --- a/onchaind/test/run-grind_feerate.c +++ b/onchaind/test/run-grind_feerate.c @@ -230,6 +230,7 @@ void subdaemon_setup(int argc UNNEEDED, char *argv[]) /* Generated stub for to_self_wscript */ u8 *to_self_wscript(const tal_t *ctx UNNEEDED, u16 to_self_delay UNNEEDED, + u32 csv UNNEEDED, const struct keyset *keyset UNNEEDED) { fprintf(stderr, "to_self_wscript called!\n"); abort(); } /* Generated stub for towire */ @@ -254,7 +255,7 @@ u8 *towire_hsmd_sign_penalty_to_us(const tal_t *ctx UNNEEDED, const struct secre u8 *towire_hsmd_sign_remote_htlc_to_us(const tal_t *ctx UNNEEDED, const struct pubkey *remote_per_commitment_point UNNEEDED, const struct bitcoin_tx *tx UNNEEDED, const u8 *wscript UNNEEDED, bool option_anchor_outputs UNNEEDED) { fprintf(stderr, "towire_hsmd_sign_remote_htlc_to_us called!\n"); abort(); } /* Generated stub for towire_onchaind_add_utxo */ -u8 *towire_onchaind_add_utxo(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *prev_out_tx UNNEEDED, u32 prev_out_index UNNEEDED, const struct pubkey *per_commit_point UNNEEDED, struct amount_sat value UNNEEDED, u32 blockheight UNNEEDED, const u8 *scriptpubkey UNNEEDED) +u8 *towire_onchaind_add_utxo(const tal_t *ctx UNNEEDED, const struct bitcoin_txid *prev_out_tx UNNEEDED, u32 prev_out_index UNNEEDED, const struct pubkey *per_commit_point UNNEEDED, struct amount_sat value UNNEEDED, u32 blockheight UNNEEDED, const u8 *scriptpubkey UNNEEDED, u32 csv_lock UNNEEDED) { fprintf(stderr, "towire_onchaind_add_utxo called!\n"); abort(); } /* Generated stub for towire_onchaind_all_irrevocably_resolved */ u8 *towire_onchaind_all_irrevocably_resolved(const tal_t *ctx UNNEEDED) diff --git a/openingd/Makefile b/openingd/Makefile index 425a7021a033..655119611f5c 100644 --- a/openingd/Makefile +++ b/openingd/Makefile @@ -36,6 +36,7 @@ OPENINGD_COMMON_OBJS := \ common/bigsize.o \ common/billboard.o \ common/bip32.o \ + common/blockheight_states.o \ common/channel_config.o \ common/channel_id.o \ common/crypto_state.o \ @@ -57,6 +58,7 @@ OPENINGD_COMMON_OBJS := \ common/initial_commit_tx.o \ common/key_derive.o \ common/keyset.o \ + common/lease_rates.o \ common/memleak.o \ common/msg_queue.o \ common/node_id.o \ diff --git a/openingd/dualopend.c b/openingd/dualopend.c index ed5f58300198..3805e5c93633 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -119,6 +121,24 @@ struct tx_state { /* Have we gotten the peer's tx-sigs yet? */ bool remote_funding_sigs_rcvd; + + /* Rates that we're using for this open... */ + struct lease_rates *rates; + + /* Lease blockheight start */ + u32 blockheight; + + /* If delay til the channel funds lease expires */ + u32 lease_expiry; + + /* Lease's commit sig */ + secp256k1_ecdsa_signature *lease_commit_sig; + + /* Lease's commited chan max msat */ + u32 lease_chan_max_msat; + + /* Lease's commited chan max ppt */ + u16 lease_chan_max_ppt; }; static struct tx_state *new_tx_state(const tal_t *ctx) @@ -127,6 +147,12 @@ static struct tx_state *new_tx_state(const tal_t *ctx) tx_state->psbt = NULL; tx_state->remote_funding_sigs_rcvd = false; + tx_state->lease_expiry = 0; + tx_state->blockheight = 0; + tx_state->lease_commit_sig = NULL; + tx_state->lease_chan_max_msat = 0; + tx_state->lease_chan_max_ppt = 0; + for (size_t i = 0; i < NUM_TX_MSGS; i++) tx_state->tx_msg_count[i] = 0; @@ -512,32 +538,6 @@ static bool is_openers(const struct wally_map *unknowns) return serial_id % 2 == TX_INITIATOR; } -static size_t psbt_input_weight(struct wally_psbt *psbt, - size_t in) -{ - size_t weight; - - /* txid + txout + sequence */ - weight = (32 + 4 + 4) * 4; - weight += - (psbt->inputs[in].redeem_script_len + - (varint_t) varint_size(psbt->inputs[in].redeem_script_len)) * 4; - - /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #3: - * - * The minimum witness weight for an input is 110. - */ - weight += 110; - return weight; -} - -static size_t psbt_output_weight(struct wally_psbt *psbt, - size_t outnum) -{ - return (8 + psbt->tx->outputs[outnum].script_len + - varint_size(psbt->tx->outputs[outnum].script_len)) * 4; -} - static bool find_txout(struct wally_psbt *psbt, const u8 *wscript, u16 *funding_txout) { for (size_t i = 0; i < psbt->num_outputs; i++) { @@ -554,6 +554,7 @@ static char *check_balances(const tal_t *ctx, struct state *state, struct tx_state *tx_state, struct wally_psbt *psbt, + struct amount_sat lease_fee, u32 feerate_per_kw_funding) { struct amount_sat initiator_inputs, initiator_outs, @@ -700,8 +701,18 @@ static char *check_balances(const tal_t *ctx, } } tot_output_amt = AMOUNT_SAT(0); + initiator_outs = tx_state->opener_funding; accepter_outs = tx_state->accepter_funding; + + /* The lease_fee has been added to the accepter_funding, + * but the opener_funding is responsible for covering it, + * so we do a little switcheroo here */ + if (!amount_sat_add(&initiator_outs, initiator_outs, lease_fee)) + return "overflow adding lease_fee to initiator's funding"; + if (!amount_sat_sub(&accepter_outs, accepter_outs, lease_fee)) + return "unable to subtract lease_fee from accepter's funding"; + for (size_t i = 0; i < psbt->num_outputs; i++) { struct amount_sat amt = psbt_output_get_amount(psbt, i); @@ -754,8 +765,19 @@ static char *check_balances(const tal_t *ctx, * - the peer's total input satoshis is less than their outputs */ /* We check both, why not? */ - if (!amount_sat_greater_eq(initiator_inputs, initiator_outs)) - return "initiator inputs less than outputs"; + if (!amount_sat_greater_eq(initiator_inputs, initiator_outs)) { + return tal_fmt(tmpctx, + "initiator inputs less than outputs (%s < %s)" + " (lease fee %s)", + type_to_string(tmpctx, struct amount_sat, + &initiator_inputs), + type_to_string(tmpctx, struct amount_sat, + &initiator_outs), + type_to_string(tmpctx, struct amount_sat, + &lease_fee)); + + + } /* BOLT-f53ca2301232db780843e894f55d95d512f297f9 #2: * The receiving node: ... @@ -768,7 +790,10 @@ static char *check_balances(const tal_t *ctx, */ if (!amount_sat_sub(&accepter_diff, accepter_inputs, accepter_outs)) { - return "accepter inputs less than outputs"; + return tal_fmt(tmpctx, "accepter inputs %s less than outputs %s (lease fee %s)", + type_to_string(tmpctx, struct amount_sat, &accepter_inputs), + type_to_string(tmpctx, struct amount_sat, &accepter_outs), + type_to_string(tmpctx, struct amount_sat, &lease_fee)); } if (!amount_sat_sub(&initiator_diff, initiator_inputs, @@ -1252,6 +1277,7 @@ static u8 *opening_negotiate_msg(const tal_t *ctx, struct state *state) case WIRE_COMMITMENT_SIGNED: case WIRE_REVOKE_AND_ACK: case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: @@ -1599,6 +1625,7 @@ static bool run_tx_interactive(struct state *state, case WIRE_COMMITMENT_SIGNED: case WIRE_REVOKE_AND_ACK: case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: @@ -1664,6 +1691,9 @@ static void revert_channel_state(struct state *state) &tx_state->funding_txid, tx_state->funding_txout, state->minimum_depth, + take(new_height_states(NULL, opener, + &tx_state->blockheight)), + tx_state->lease_expiry, total, our_msats, take(new_fee_states( @@ -1684,6 +1714,7 @@ static void revert_channel_state(struct state *state) static u8 *accepter_commits(struct state *state, struct tx_state *tx_state, struct amount_sat total, + struct amount_sat lease_fee, char **err_reason) { struct wally_tx_output *direct_outputs[NUM_SIDES]; @@ -1718,6 +1749,7 @@ static u8 *accepter_commits(struct state *state, /* Check tx funds are sane */ error = check_balances(tmpctx, state, tx_state, tx_state->psbt, + lease_fee, tx_state->feerate_per_kw_funding); if (error) { *err_reason = tal_fmt(tmpctx, "Insufficiently funded" @@ -1760,6 +1792,10 @@ static u8 *accepter_commits(struct state *state, &tx_state->funding_txid, tx_state->funding_txout, state->minimum_depth, + take(new_height_states(NULL, REMOTE, + &tx_state->blockheight)), + + tx_state->lease_expiry, total, our_msats, take(new_fee_states( @@ -1884,7 +1920,12 @@ static u8 *accepter_commits(struct state *state, tx_state->feerate_per_kw_funding, state->feerate_per_kw_commitment, state->upfront_shutdown_script[LOCAL], - state->upfront_shutdown_script[REMOTE]); + state->upfront_shutdown_script[REMOTE], + tx_state->blockheight, + tx_state->lease_expiry, + tx_state->lease_commit_sig, + tx_state->lease_chan_max_msat, + tx_state->lease_chan_max_ppt); wire_sync_write(REQ_FD, take(msg)); msg = wire_sync_read(tmpctx, REQ_FD); @@ -1900,6 +1941,60 @@ static u8 *accepter_commits(struct state *state, return msg; } +static void accept_tlv_add_offer(struct tlv_accept_tlvs *a_tlv, + struct tx_state *tx_state, + struct lease_rates *rates, + struct pubkey funding_pubkey, + u32 blockheight) +{ + u8 *msg; + u32 lease_expiry = blockheight + LEASE_RATE_DURATION; + tx_state->lease_commit_sig = tal(tx_state, secp256k1_ecdsa_signature); + + /* Go get the signature for this lease offer from HSMD */ + msg = towire_hsmd_sign_option_will_fund_offer(NULL, + &funding_pubkey, + lease_expiry, + rates->channel_fee_max_base_msat, + rates->channel_fee_max_proportional_thousandths); + if (!wire_sync_write(HSM_FD, take(msg))) + status_failed(STATUS_FAIL_HSM_IO, + "Could not write to HSM: %s", + strerror(errno)); + msg = wire_sync_read(tmpctx, HSM_FD); + if (!fromwire_hsmd_sign_option_will_fund_offer_reply(msg, + tx_state->lease_commit_sig)) + status_failed(STATUS_FAIL_HSM_IO, + "Bad sign_option_will_fund_offer_reply %s", + tal_hex(tmpctx, msg)); + + /* BOLT- #2: + * The accepting node: + * ... + * - MUST set `funding_fee_base_sat` to the base fee + * (in satoshi) it will charge for the `funding_satoshis` + * - MUST set `funding_fee_proportional_basis` to the amount + * (in thousandths of satoshi) it will charge per `funding_satoshi` + * - MUST set `funding_weight` to the weight they + * will contribute to this channel, to fund the request. + * - MUST set `channel_fee_base_max_msat` to the base fee + * (in millisatoshi) it will charge for any HTLC on this channel + * during the funding period. + * - MUST set `channel_fee_proportional_basis_max` to the amount + * (in thousandths of a satoshi) it will charge per transferred + * satoshi during the funding period. + */ + a_tlv->will_fund = tal(a_tlv, struct tlv_accept_tlvs_will_fund); + a_tlv->will_fund->lease_rates = *rates; + a_tlv->will_fund->signature = *tx_state->lease_commit_sig; + + tx_state->lease_expiry = lease_expiry; + tx_state->lease_chan_max_msat + = rates->channel_fee_max_base_msat; + tx_state->lease_chan_max_ppt + = rates->channel_fee_max_proportional_thousandths; +} + static void accepter_start(struct state *state, const u8 *oc2_msg) { struct bitcoin_blkid chain_hash; @@ -1907,7 +2002,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) struct channel_id cid, full_cid; char *err_reason; u8 *msg; - struct amount_sat total; + struct amount_sat total, requested_amt, lease_fee, our_accept; enum dualopend_wire msg_type; struct tx_state *tx_state = state->tx_state; @@ -1942,6 +2037,15 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) } else state->upfront_shutdown_script[REMOTE] = NULL; + /* This is an `option_will_fund` request */ + if (open_tlv->request_funds) { + requested_amt + = amount_sat(open_tlv->request_funds->requested_sats); + tx_state->blockheight + = open_tlv->request_funds->blockheight; + } else + requested_amt = AMOUNT_SAT(0); + /* BOLT-* #2 * If the peer's revocation basepoint is unknown (e.g. * `open_channel2`), a temporary `channel_id` should be found @@ -2008,7 +2112,9 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) tx_state->remoteconf.max_accepted_htlcs, state->channel_flags, tx_state->tx_locktime, - state->upfront_shutdown_script[REMOTE]); + state->upfront_shutdown_script[REMOTE], + requested_amt, + tx_state->blockheight); wire_sync_write(REQ_FD, take(msg)); msg = wire_sync_read(tmpctx, REQ_FD); @@ -2023,7 +2129,8 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) if (!fromwire_dualopend_got_offer_reply(state, msg, &tx_state->accepter_funding, &tx_state->psbt, - &state->upfront_shutdown_script[LOCAL])) + &state->upfront_shutdown_script[LOCAL], + &tx_state->rates)) master_badmsg(WIRE_DUALOPEND_GOT_OFFER_REPLY, msg); if (!tx_state->psbt) @@ -2033,6 +2140,63 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) /* Locktimes must match! */ tx_state->psbt->tx->locktime = tx_state->tx_locktime; + /* BOLT- #2: + * + * - if they decide to accept the offer: + * ... + * - MUST set `funding_satoshis` to a value greater than 0msat + */ + if (tx_state->rates && amount_sat_zero(tx_state->accepter_funding)) { + status_broken("opt_will_fund ad passed in, but no funding"); + negotiation_failed(state, "We're unable to accept" + " your lease offer."); + return; + } + + /* This we bump the accepter_funding iff there's a lease, + * so we stash this here so we tell our peer the right amount */ + our_accept = tx_state->accepter_funding; + + /* Add our fee to our amount now */ + if (tx_state->rates) { + tx_state->lease_expiry + = tx_state->blockheight + LEASE_RATE_DURATION; + + /* BOLT- #2: + * The lease fee is added to the accepter's balance + * in a channel, in addition to the `funding_satoshi` + * that they are contributing. The channel initiator + * must contribute enough funds to cover + * `open_channel2`.`funding_satoshis`, the lease fee, + * and their tx weight * `funding_feerate_perkw` / 1000. + */ + if (!lease_rates_calc_fee(tx_state->rates, + tx_state->accepter_funding, + requested_amt, + state->feerate_per_kw_funding, + &lease_fee)) + negotiation_failed(state, + "Unable to calculate lease fee"); + + /* Add it to the accepter's total */ + if (!amount_sat_add(&tx_state->accepter_funding, + tx_state->accepter_funding, lease_fee)) + + negotiation_failed(state, + "Unable to add accepter's funding" + " and channel lease fee (%s + %s)", + type_to_string(tmpctx, + struct amount_sat, + &tx_state->accepter_funding), + type_to_string(tmpctx, + struct amount_sat, + &lease_fee)); + + } else { + tx_state->lease_expiry = 0; + lease_fee = AMOUNT_SAT(0); + } + /* Check that total funding doesn't overflow */ if (!amount_sat_add(&total, tx_state->opener_funding, tx_state->accepter_funding)) @@ -2097,8 +2261,22 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) 0); } + /* BOLT- #2: + * The accepting node: + * ... + * - if the `option_will_fund` tlv was sent in `open_channel2`: + * - if they decide to accept the offer: + * - MUST include a `will_fund` tlv + */ + if (open_tlv->request_funds && tx_state->rates) + accept_tlv_add_offer(a_tlv, tx_state, tx_state->rates, + state->our_funding_pubkey, + tx_state->blockheight); + + msg = towire_accept_channel2(tmpctx, &state->channel_id, - tx_state->accepter_funding, + /* Our amount w/o the lease fee */ + our_accept, tx_state->localconf.dust_limit, tx_state->localconf.max_htlc_value_in_flight, tx_state->localconf.htlc_minimum, @@ -2129,7 +2307,8 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) if (!run_tx_interactive(state, tx_state, &tx_state->psbt, TX_ACCEPTER)) return; - msg = accepter_commits(state, tx_state, total, &err_reason); + msg = accepter_commits(state, tx_state, total, + lease_fee, &err_reason); if (!msg) { if (err_reason) negotiation_failed(state, "%s", err_reason); @@ -2166,6 +2345,7 @@ static void add_funding_output(struct tx_state *tx_state, static u8 *opener_commits(struct state *state, struct tx_state *tx_state, struct amount_sat total, + struct amount_sat lease_fee, char **err_reason) { struct channel_id cid; @@ -2199,6 +2379,7 @@ static u8 *opener_commits(struct state *state, error = check_balances(tmpctx, state, tx_state, tx_state->psbt, + lease_fee, tx_state->feerate_per_kw_funding); if (error) { *err_reason = tal_fmt(tmpctx, "Insufficiently funded funding " @@ -2224,6 +2405,9 @@ static u8 *opener_commits(struct state *state, &tx_state->funding_txid, tx_state->funding_txout, state->minimum_depth, + take(new_height_states(NULL, LOCAL, + &state->tx_state->blockheight)), + tx_state->lease_expiry, total, our_msats, take(new_fee_states(NULL, LOCAL, @@ -2392,7 +2576,12 @@ static u8 *opener_commits(struct state *state, tx_state->feerate_per_kw_funding, state->feerate_per_kw_commitment, state->upfront_shutdown_script[LOCAL], - state->upfront_shutdown_script[REMOTE]); + state->upfront_shutdown_script[REMOTE], + tx_state->blockheight, + tx_state->lease_expiry, + tx_state->lease_commit_sig, + tx_state->lease_chan_max_msat, + tx_state->lease_chan_max_ppt); } @@ -2402,7 +2591,9 @@ static void opener_start(struct state *state, u8 *msg) struct tlv_accept_tlvs *a_tlv; struct channel_id cid; char *err_reason; - struct amount_sat total; + struct amount_sat total, requested_sats, lease_fee; + bool dry_run; + struct lease_rates *expected_rates; struct tx_state *tx_state = state->tx_state; if (!fromwire_dualopend_opener_init(state, msg, @@ -2411,13 +2602,17 @@ static void opener_start(struct state *state, u8 *msg) &state->upfront_shutdown_script[LOCAL], &state->feerate_per_kw_commitment, &state->feerate_per_kw_funding, - &state->channel_flags)) + &state->channel_flags, + &requested_sats, + &tx_state->blockheight, + &dry_run, + &expected_rates)) master_badmsg(WIRE_DUALOPEND_OPENER_INIT, msg); state->our_role = TX_INITIATOR; tx_state->tx_locktime = tx_state->psbt->tx->locktime; tx_state->feerate_per_kw_funding = state->feerate_per_kw_funding; - open_tlv = tlv_opening_tlvs_new(tmpctx); + open_tlv = tlv_opening_tlvs_new(state); /* BOLT-* #2 * If the peer's revocation basepoint is unknown (e.g. @@ -2441,6 +2636,14 @@ static void opener_start(struct state *state, u8 *msg) state->upfront_shutdown_script[LOCAL]; } + if (!amount_sat_zero(requested_sats)) { + open_tlv->request_funds = + tal(open_tlv, struct tlv_opening_tlvs_request_funds); + open_tlv->request_funds->requested_sats = + requested_sats.satoshis; /* Raw: struct -> wire */ + open_tlv->request_funds->blockheight = tx_state->blockheight; + } + msg = towire_open_channel2(NULL, &chainparams->genesis_blockhash, &state->channel_id, @@ -2522,6 +2725,123 @@ static void opener_start(struct state *state, u8 *msg) &state->our_points.revocation, &state->their_points.revocation); + /* If this is a dry run, we just wanted to know + * how much they'd put into the channel and their terms */ + if (dry_run) { + msg = towire_dualopend_dry_run(NULL, &state->channel_id, + tx_state->opener_funding, + tx_state->accepter_funding, + a_tlv->will_fund + ? &a_tlv->will_fund->lease_rates : NULL); + + + wire_sync_write(REQ_FD, take(msg)); + + /* Note that this *normally* would return an error + * to the RPC caller. We head this off by + * sending a message to master just before this, + * which works as expected as long as + * these messages are queued+processed sequentially */ + open_err_warn(state, "%s", "Abort requested"); + } + + /* If we've requested funds and they've failed to provide + * to lease us (or give them to us for free?!) then we fail. + * This isn't spec'd but it makes the UX predictable */ + if (open_tlv->request_funds + && amount_sat_less(tx_state->accepter_funding, requested_sats)) + negotiation_failed(state, + "We requested %s, which is more" + " than they've offered to provide" + " (%s)", + type_to_string(tmpctx, + struct amount_sat, + &requested_sats), + type_to_string(tmpctx, + struct amount_sat, + &tx_state->accepter_funding)); + + + /* BOLT- #2: + * The accepting node: ... + * - if they decide to accept the offer: + * - MUST include a `will_fund` tlv + */ + if (open_tlv->request_funds && a_tlv->will_fund) { + char *err_msg; + struct lease_rates *rates = &a_tlv->will_fund->lease_rates; + + if (!lease_rates_eq(rates, expected_rates)) + negotiation_failed(state, + "Expected lease rates (%s)," + " their returned lease rates (%s)", + lease_rates_fmt(tmpctx, + expected_rates), + lease_rates_fmt(tmpctx, + rates)); + + + tx_state->lease_expiry = tx_state->blockheight + LEASE_RATE_DURATION; + + msg = towire_dualopend_validate_lease(NULL, + &a_tlv->will_fund->signature, + tx_state->lease_expiry, + rates->channel_fee_max_base_msat, + rates->channel_fee_max_proportional_thousandths, + &state->their_funding_pubkey); + + + wire_sync_write(REQ_FD, take(msg)); + msg = wire_sync_read(tmpctx, REQ_FD); + + if (!fromwire_dualopend_validate_lease_reply(tmpctx, msg, + &err_msg)) + master_badmsg(WIRE_DUALOPEND_VALIDATE_LEASE_REPLY, msg); + + if (err_msg) + open_err_warn(state, "%s", err_msg); + + /* BOLT- #2: + * The lease fee is added to the accepter's balance + * in a channel, in addition to the `funding_satoshi` + * that they are contributing. The channel initiator + * must contribute enough funds to cover + * `open_channel2`.`funding_satoshis`, the lease fee, + * and their tx weight * `funding_feerate_perkw` / 1000. + */ + if (!lease_rates_calc_fee(rates, tx_state->accepter_funding, + requested_sats, + state->feerate_per_kw_funding, + &lease_fee)) + negotiation_failed(state, + "Unable to calculate lease fee"); + + /* Add it to the accepter's total */ + if (!amount_sat_add(&tx_state->accepter_funding, + tx_state->accepter_funding, lease_fee)) { + + negotiation_failed(state, + "Unable to add accepter's funding" + " and channel lease fee (%s + %s)", + type_to_string(tmpctx, + struct amount_sat, + &tx_state->accepter_funding), + type_to_string(tmpctx, + struct amount_sat, + &lease_fee)); + return; + } + + tx_state->lease_commit_sig + = tal_dup(tx_state, secp256k1_ecdsa_signature, + &a_tlv->will_fund->signature); + tx_state->lease_chan_max_msat + = rates->channel_fee_max_base_msat; + tx_state->lease_chan_max_ppt + = rates->channel_fee_max_proportional_thousandths; + } else + lease_fee = AMOUNT_SAT(0); + /* Check that total funding doesn't overflow */ if (!amount_sat_add(&total, tx_state->opener_funding, tx_state->accepter_funding)) @@ -2585,7 +2905,7 @@ static void opener_start(struct state *state, u8 *msg) if (!run_tx_interactive(state, tx_state, &tx_state->psbt, TX_INITIATOR)) return; - msg = opener_commits(state, tx_state, total, &err_reason); + msg = opener_commits(state, tx_state, total, lease_fee, &err_reason); if (!msg) { if (err_reason) open_err_warn(state, "%s", err_reason); @@ -2707,9 +3027,12 @@ static void rbf_wrap_up(struct state *state, psbt_txid(NULL, tx_state->psbt, &tx_state->funding_txid, NULL); if (state->our_role == TX_ACCEPTER) - msg = accepter_commits(state, tx_state, total, &err_reason); + /* FIXME: lease fee rate !? */ + msg = accepter_commits(state, tx_state, total, + AMOUNT_SAT(0), &err_reason); else - msg = opener_commits(state, tx_state, total, &err_reason); + msg = opener_commits(state, tx_state, total, + AMOUNT_SAT(0), &err_reason); if (!msg) { if (err_reason) @@ -3165,7 +3488,7 @@ static void do_reconnect_dance(struct state *state) struct pubkey remote_current_per_commit_point; struct tx_state *tx_state = state->tx_state; #if EXPERIMENTAL_FEATURES - struct tlv_channel_reestablish_tlvs *tlvs = tlv_channel_reestablish_tlvs_new(tmpctx); + struct tlv_channel_reestablish_tlvs *tlvs = tlv_channel_reestablish_tlvs_new(NULL); #endif /* BOLT #2: @@ -3216,6 +3539,9 @@ static void do_reconnect_dance(struct state *state) peer_wire_name(fromwire_peektype(msg)), tal_hex(msg, msg)); +#if EXPERIMENTAL_FEATURES + tal_free(tlvs); +#endif /* EXPERIMENTAL_FEATURES */ check_channel_id(state, &cid, &state->channel_id); status_debug("Got dualopend reestablish commit=%"PRIu64 @@ -3301,6 +3627,7 @@ static u8 *handle_master_in(struct state *state) case WIRE_DUALOPEND_GOT_OFFER_REPLY: case WIRE_DUALOPEND_GOT_RBF_OFFER_REPLY: case WIRE_DUALOPEND_RBF_VALID: + case WIRE_DUALOPEND_VALIDATE_LEASE_REPLY: /* Messages we send */ case WIRE_DUALOPEND_GOT_OFFER: @@ -3315,6 +3642,8 @@ static u8 *handle_master_in(struct state *state) case WIRE_DUALOPEND_GOT_SHUTDOWN: case WIRE_DUALOPEND_SHUTDOWN_COMPLETE: case WIRE_DUALOPEND_FAIL_FALLEN_BEHIND: + case WIRE_DUALOPEND_DRY_RUN: + case WIRE_DUALOPEND_VALIDATE_LEASE: break; } @@ -3380,6 +3709,7 @@ static u8 *handle_peer_in(struct state *state) case WIRE_COMMITMENT_SIGNED: case WIRE_REVOKE_AND_ACK: case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: @@ -3526,7 +3856,12 @@ int main(int argc, char *argv[]) &state->upfront_shutdown_script[REMOTE], &state->tx_state->remote_funding_sigs_rcvd, &fee_states, - &state->channel_flags)) { + &state->channel_flags, + &state->tx_state->blockheight, + &state->tx_state->lease_expiry, + &state->tx_state->lease_commit_sig, + &state->tx_state->lease_chan_max_msat, + &state->tx_state->lease_chan_max_ppt)) { /*~ We only reconnect on channels that the * saved the the database (exchanged commitment sigs) */ @@ -3535,6 +3870,9 @@ int main(int argc, char *argv[]) &state->tx_state->funding_txid, state->tx_state->funding_txout, state->minimum_depth, + take(new_height_states(NULL, opener, + &state->tx_state->blockheight)), + state->tx_state->lease_expiry, total_funding, our_msat, fee_states, diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 217f935aa1a2..5608b2e41eb2 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -9,6 +9,8 @@ #include #include #include +#include +#include msgtype,dualopend_init,7000 # Which network are we configured for? @@ -64,6 +66,11 @@ msgdata,dualopend_reinit,remote_shutdown_scriptpubkey,u8,remote_shutdown_len msgdata,dualopend_reinit,remote_funding_sigs_received,bool, msgdata,dualopend_reinit,fee_states,fee_states, msgdata,dualopend_reinit,channel_flags,u8, +msgdata,dualopend_reinit,lease_start_blockheight,u32, +msgdata,dualopend_reinit,lease_expiry,u32, +msgdata,dualopend_reinit,lease_commit_sig,?secp256k1_ecdsa_signature, +msgdata,dualopend_reinit,lease_chan_max_msat,u32, +msgdata,dualopend_reinit,lease_chan_max_ppt,u16, # dualopend->master: they offered channel, should we continue? msgtype,dualopend_got_offer,7005 @@ -80,6 +87,8 @@ msgdata,dualopend_got_offer,channel_flags,u8, msgdata,dualopend_got_offer,locktime,u32, msgdata,dualopend_got_offer,shutdown_len,u16, msgdata,dualopend_got_offer,shutdown_scriptpubkey,u8,shutdown_len +msgdata,dualopend_got_offer,requested_amt,amount_sat, +msgdata,dualopend_got_offer,lease_blockheight_start,u32, # master->dualopend: reply back with our first funding info/contribs msgtype,dualopend_got_offer_reply,7105 @@ -87,6 +96,8 @@ msgdata,dualopend_got_offer_reply,accepter_funding,amount_sat, msgdata,dualopend_got_offer_reply,psbt,wally_psbt, msgdata,dualopend_got_offer_reply,shutdown_len,u16, msgdata,dualopend_got_offer_reply,our_shutdown_scriptpubkey,?u8,shutdown_len +# must go last because of embedded tu32 +msgdata,dualopend_got_offer_reply,lease_rates,?lease_rates, # dualopend->master: they offered a RBF, should we continue? msgtype,dualopend_got_rbf_offer,7500 @@ -137,6 +148,11 @@ msgdata,dualopend_commit_rcvd,local_shutdown_len,u16, msgdata,dualopend_commit_rcvd,local_shutdown_scriptpubkey,u8,local_shutdown_len msgdata,dualopend_commit_rcvd,remote_shutdown_len,u16, msgdata,dualopend_commit_rcvd,remote_shutdown_scriptpubkey,u8,remote_shutdown_len +msgdata,dualopend_commit_rcvd,lease_start_blockheight,u32, +msgdata,dualopend_commit_rcvd,lease_expiry,u32, +msgdata,dualopend_commit_rcvd,lease_commit_sig,?secp256k1_ecdsa_signature, +msgdata,dualopend_commit_rcvd,lease_chan_max_msat,u32, +msgdata,dualopend_commit_rcvd,lease_chan_max_ppt,u16, # dualopend->master: peer updated the psbt msgtype,dualopend_psbt_changed,7107 @@ -161,6 +177,11 @@ msgdata,dualopend_opener_init,local_shutdown_scriptpubkey,u8,local_shutdown_len msgdata,dualopend_opener_init,feerate_per_kw,u32, msgdata,dualopend_opener_init,feerate_per_kw_funding,u32, msgdata,dualopend_opener_init,channel_flags,u8, +msgdata,dualopend_opener_init,requested_sats,amount_sat, +msgdata,dualopend_opener_init,blockheight,u32, +msgdata,dualopend_opener_init,dry_run,bool, +# must go last because embedded tu32 +msgdata,dualopend_opener_init,expected_rates,?lease_rates, # dualopend->master received tx_sigs from peer msgtype,dualopend_funding_sigs,7010 @@ -207,3 +228,22 @@ msgtype,dualopend_dev_memleak,7033 msgtype,dualopend_dev_memleak_reply,7133 msgdata,dualopend_dev_memleak_reply,leak,bool, + +# dualopend -> master: this was a dry run, here's some info about this open +msgtype,dualopend_dry_run,7026 +msgdata,dualopend_dry_run,channel_id,channel_id, +msgdata,dualopend_dry_run,our_funding,amount_sat, +msgdata,dualopend_dry_run,their_funding,amount_sat, +# must go last because of embedded tu32 +msgdata,dualopend_dry_run,lease_rates,?lease_rates, + +# dualopend -> master: validate liqudity offer sig +msgtype,dualopend_validate_lease,7027 +msgdata,dualopend_validate_lease,sig,secp256k1_ecdsa_signature, +msgdata,dualopend_validate_lease,lease_expiry,u32, +msgdata,dualopend_validate_lease,chan_fee_max_base_msat,u32, +msgdata,dualopend_validate_lease,chan_fee_max_ppt,u16, +msgdata,dualopend_validate_lease,their_pubkey,pubkey, + +msgtype,dualopend_validate_lease_reply,7127 +msgdata,dualopend_validate_lease_reply,err_msg,?wirestring, diff --git a/openingd/dualopend_wiregen.c b/openingd/dualopend_wiregen.c index 21d658ecb710..5ca2984cc69f 100644 --- a/openingd/dualopend_wiregen.c +++ b/openingd/dualopend_wiregen.c @@ -46,6 +46,9 @@ const char *dualopend_wire_name(int e) case WIRE_DUALOPEND_SHUTDOWN_COMPLETE: return "WIRE_DUALOPEND_SHUTDOWN_COMPLETE"; case WIRE_DUALOPEND_DEV_MEMLEAK: return "WIRE_DUALOPEND_DEV_MEMLEAK"; case WIRE_DUALOPEND_DEV_MEMLEAK_REPLY: return "WIRE_DUALOPEND_DEV_MEMLEAK_REPLY"; + case WIRE_DUALOPEND_DRY_RUN: return "WIRE_DUALOPEND_DRY_RUN"; + case WIRE_DUALOPEND_VALIDATE_LEASE: return "WIRE_DUALOPEND_VALIDATE_LEASE"; + case WIRE_DUALOPEND_VALIDATE_LEASE_REPLY: return "WIRE_DUALOPEND_VALIDATE_LEASE_REPLY"; } snprintf(invalidbuf, sizeof(invalidbuf), "INVALID %i", e); @@ -81,6 +84,9 @@ bool dualopend_wire_is_defined(u16 type) case WIRE_DUALOPEND_SHUTDOWN_COMPLETE:; case WIRE_DUALOPEND_DEV_MEMLEAK:; case WIRE_DUALOPEND_DEV_MEMLEAK_REPLY:; + case WIRE_DUALOPEND_DRY_RUN:; + case WIRE_DUALOPEND_VALIDATE_LEASE:; + case WIRE_DUALOPEND_VALIDATE_LEASE_REPLY:; return true; } return false; @@ -146,7 +152,7 @@ bool fromwire_dualopend_init(const tal_t *ctx, const void *p, const struct chain /* WIRE: DUALOPEND_REINIT */ /* master-dualopend: peer has reconnected */ -u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, const struct channel_config *their_config, const struct channel_id *channel_id, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct pubkey *their_funding_pubkey, u32 minimum_depth, const struct bitcoin_txid *funding_txid, u16 funding_txout, u32 orignal_feerate_per_kw_funding, u32 most_recent_feerate_per_kw_funding, struct amount_sat funding_satoshi, struct amount_msat our_funding, const struct basepoints *their_basepoints, const struct pubkey *remote_per_commit, const struct wally_psbt *funding_psbt, enum side opener, bool local_funding_locked, bool remote_funding_locked, bool send_shutdown, bool remote_shutdown_received, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, bool remote_funding_sigs_received, const struct fee_states *fee_states, u8 channel_flags) +u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, const struct channel_config *their_config, const struct channel_id *channel_id, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct pubkey *their_funding_pubkey, u32 minimum_depth, const struct bitcoin_txid *funding_txid, u16 funding_txout, u32 orignal_feerate_per_kw_funding, u32 most_recent_feerate_per_kw_funding, struct amount_sat funding_satoshi, struct amount_msat our_funding, const struct basepoints *their_basepoints, const struct pubkey *remote_per_commit, const struct wally_psbt *funding_psbt, enum side opener, bool local_funding_locked, bool remote_funding_locked, bool send_shutdown, bool remote_shutdown_received, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, bool remote_funding_sigs_received, const struct fee_states *fee_states, u8 channel_flags, u32 lease_start_blockheight, u32 lease_expiry, const secp256k1_ecdsa_signature *lease_commit_sig, u32 lease_chan_max_msat, u16 lease_chan_max_ppt) { u16 their_init_features_len = tal_count(their_init_features); u16 local_shutdown_len = tal_count(local_shutdown_scriptpubkey); @@ -189,10 +195,20 @@ u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainpar towire_bool(&p, remote_funding_sigs_received); towire_fee_states(&p, fee_states); towire_u8(&p, channel_flags); + towire_u32(&p, lease_start_blockheight); + towire_u32(&p, lease_expiry); + if (!lease_commit_sig) + towire_bool(&p, false); + else { + towire_bool(&p, true); + towire_secp256k1_ecdsa_signature(&p, lease_commit_sig); + } + towire_u32(&p, lease_chan_max_msat); + towire_u16(&p, lease_chan_max_ppt); return memcheck(p, tal_count(p)); } -bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, struct channel_config *their_config, struct channel_id *channel_id, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct pubkey *their_funding_pubkey, u32 *minimum_depth, struct bitcoin_txid *funding_txid, u16 *funding_txout, u32 *orignal_feerate_per_kw_funding, u32 *most_recent_feerate_per_kw_funding, struct amount_sat *funding_satoshi, struct amount_msat *our_funding, struct basepoints *their_basepoints, struct pubkey *remote_per_commit, struct wally_psbt **funding_psbt, enum side *opener, bool *local_funding_locked, bool *remote_funding_locked, bool *send_shutdown, bool *remote_shutdown_received, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, bool *remote_funding_sigs_received, struct fee_states **fee_states, u8 *channel_flags) +bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, struct channel_config *their_config, struct channel_id *channel_id, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct pubkey *their_funding_pubkey, u32 *minimum_depth, struct bitcoin_txid *funding_txid, u16 *funding_txout, u32 *orignal_feerate_per_kw_funding, u32 *most_recent_feerate_per_kw_funding, struct amount_sat *funding_satoshi, struct amount_msat *our_funding, struct basepoints *their_basepoints, struct pubkey *remote_per_commit, struct wally_psbt **funding_psbt, enum side *opener, bool *local_funding_locked, bool *remote_funding_locked, bool *send_shutdown, bool *remote_shutdown_received, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, bool *remote_funding_sigs_received, struct fee_states **fee_states, u8 *channel_flags, u32 *lease_start_blockheight, u32 *lease_expiry, secp256k1_ecdsa_signature **lease_commit_sig, u32 *lease_chan_max_msat, u16 *lease_chan_max_ppt) { u16 their_init_features_len; u16 local_shutdown_len; @@ -244,12 +260,22 @@ bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct cha *remote_funding_sigs_received = fromwire_bool(&cursor, &plen); *fee_states = fromwire_fee_states(ctx, &cursor, &plen); *channel_flags = fromwire_u8(&cursor, &plen); + *lease_start_blockheight = fromwire_u32(&cursor, &plen); + *lease_expiry = fromwire_u32(&cursor, &plen); + if (!fromwire_bool(&cursor, &plen)) + *lease_commit_sig = NULL; + else { + *lease_commit_sig = tal(ctx, secp256k1_ecdsa_signature); + fromwire_secp256k1_ecdsa_signature(&cursor, &plen, *lease_commit_sig); + } + *lease_chan_max_msat = fromwire_u32(&cursor, &plen); + *lease_chan_max_ppt = fromwire_u16(&cursor, &plen); return cursor != NULL; } /* WIRE: DUALOPEND_GOT_OFFER */ /* dualopend->master: they offered channel */ -u8 *towire_dualopend_got_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat opener_funding, struct amount_sat dust_limit_satoshis, struct amount_msat max_htlc_value_in_flight_msat, struct amount_msat htlc_minimum_msat, u32 feerate_per_kw_funding, u32 feerate_per_kw_commitment, u16 to_self_delay, u16 max_accepted_htlcs, u8 channel_flags, u32 locktime, const u8 *shutdown_scriptpubkey) +u8 *towire_dualopend_got_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat opener_funding, struct amount_sat dust_limit_satoshis, struct amount_msat max_htlc_value_in_flight_msat, struct amount_msat htlc_minimum_msat, u32 feerate_per_kw_funding, u32 feerate_per_kw_commitment, u16 to_self_delay, u16 max_accepted_htlcs, u8 channel_flags, u32 locktime, const u8 *shutdown_scriptpubkey, struct amount_sat requested_amt, u32 lease_blockheight_start) { u16 shutdown_len = tal_count(shutdown_scriptpubkey); u8 *p = tal_arr(ctx, u8, 0); @@ -268,10 +294,12 @@ u8 *towire_dualopend_got_offer(const tal_t *ctx, const struct channel_id *channe towire_u32(&p, locktime); towire_u16(&p, shutdown_len); towire_u8_array(&p, shutdown_scriptpubkey, shutdown_len); + towire_amount_sat(&p, requested_amt); + towire_u32(&p, lease_blockheight_start); return memcheck(p, tal_count(p)); } -bool fromwire_dualopend_got_offer(const tal_t *ctx, const void *p, struct channel_id *channel_id, struct amount_sat *opener_funding, struct amount_sat *dust_limit_satoshis, struct amount_msat *max_htlc_value_in_flight_msat, struct amount_msat *htlc_minimum_msat, u32 *feerate_per_kw_funding, u32 *feerate_per_kw_commitment, u16 *to_self_delay, u16 *max_accepted_htlcs, u8 *channel_flags, u32 *locktime, u8 **shutdown_scriptpubkey) +bool fromwire_dualopend_got_offer(const tal_t *ctx, const void *p, struct channel_id *channel_id, struct amount_sat *opener_funding, struct amount_sat *dust_limit_satoshis, struct amount_msat *max_htlc_value_in_flight_msat, struct amount_msat *htlc_minimum_msat, u32 *feerate_per_kw_funding, u32 *feerate_per_kw_commitment, u16 *to_self_delay, u16 *max_accepted_htlcs, u8 *channel_flags, u32 *locktime, u8 **shutdown_scriptpubkey, struct amount_sat *requested_amt, u32 *lease_blockheight_start) { u16 shutdown_len; @@ -295,12 +323,14 @@ bool fromwire_dualopend_got_offer(const tal_t *ctx, const void *p, struct channe // 2nd case shutdown_scriptpubkey *shutdown_scriptpubkey = shutdown_len ? tal_arr(ctx, u8, shutdown_len) : NULL; fromwire_u8_array(&cursor, &plen, *shutdown_scriptpubkey, shutdown_len); + *requested_amt = fromwire_amount_sat(&cursor, &plen); + *lease_blockheight_start = fromwire_u32(&cursor, &plen); return cursor != NULL; } /* WIRE: DUALOPEND_GOT_OFFER_REPLY */ /* master->dualopend: reply back with our first funding info/contribs */ -u8 *towire_dualopend_got_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, const struct wally_psbt *psbt, const u8 *our_shutdown_scriptpubkey) +u8 *towire_dualopend_got_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, const struct wally_psbt *psbt, const u8 *our_shutdown_scriptpubkey, const struct lease_rates *lease_rates) { u16 shutdown_len = tal_count(our_shutdown_scriptpubkey); u8 *p = tal_arr(ctx, u8, 0); @@ -310,10 +340,17 @@ u8 *towire_dualopend_got_offer_reply(const tal_t *ctx, struct amount_sat accepte towire_wally_psbt(&p, psbt); towire_u16(&p, shutdown_len); towire_u8_array(&p, our_shutdown_scriptpubkey, shutdown_len); + /* must go last because of embedded tu32 */ + if (!lease_rates) + towire_bool(&p, false); + else { + towire_bool(&p, true); + towire_lease_rates(&p, lease_rates); + } return memcheck(p, tal_count(p)); } -bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, struct wally_psbt **psbt, u8 **our_shutdown_scriptpubkey) +bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, struct wally_psbt **psbt, u8 **our_shutdown_scriptpubkey, struct lease_rates **lease_rates) { u16 shutdown_len; @@ -328,6 +365,13 @@ bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct // 2nd case our_shutdown_scriptpubkey *our_shutdown_scriptpubkey = shutdown_len ? tal_arr(ctx, u8, shutdown_len) : NULL; fromwire_u8_array(&cursor, &plen, *our_shutdown_scriptpubkey, shutdown_len); + /* must go last because of embedded tu32 */ + if (!fromwire_bool(&cursor, &plen)) + *lease_rates = NULL; + else { + *lease_rates = tal(ctx, struct lease_rates); + fromwire_lease_rates(&cursor, &plen, *lease_rates); + } return cursor != NULL; } @@ -452,7 +496,7 @@ bool fromwire_dualopend_rbf_init(const tal_t *ctx, const void *p, struct amount_ /* WIRE: DUALOPEND_COMMIT_RCVD */ /* dualopend->master: ready to commit channel open to database and */ /* get some signatures for the funding_tx. */ -u8 *towire_dualopend_commit_rcvd(const tal_t *ctx, const struct channel_config *their_config, const struct bitcoin_tx *remote_first_commit, const struct penalty_base *pbase, const struct bitcoin_signature *first_commit_sig, const struct wally_psbt *psbt, const struct pubkey *revocation_basepoint, const struct pubkey *payment_basepoint, const struct pubkey *htlc_basepoint, const struct pubkey *delayed_payment_basepoint, const struct pubkey *their_per_commit_point, const struct pubkey *remote_fundingkey, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshis, struct amount_sat our_funding_sats, u8 channel_flags, u32 feerate_per_kw_funding, u32 feerate_per_kw_commitment, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey) +u8 *towire_dualopend_commit_rcvd(const tal_t *ctx, const struct channel_config *their_config, const struct bitcoin_tx *remote_first_commit, const struct penalty_base *pbase, const struct bitcoin_signature *first_commit_sig, const struct wally_psbt *psbt, const struct pubkey *revocation_basepoint, const struct pubkey *payment_basepoint, const struct pubkey *htlc_basepoint, const struct pubkey *delayed_payment_basepoint, const struct pubkey *their_per_commit_point, const struct pubkey *remote_fundingkey, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshis, struct amount_sat our_funding_sats, u8 channel_flags, u32 feerate_per_kw_funding, u32 feerate_per_kw_commitment, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, u32 lease_start_blockheight, u32 lease_expiry, const secp256k1_ecdsa_signature *lease_commit_sig, u32 lease_chan_max_msat, u16 lease_chan_max_ppt) { u16 local_shutdown_len = tal_count(local_shutdown_scriptpubkey); u16 remote_shutdown_len = tal_count(remote_shutdown_scriptpubkey); @@ -486,10 +530,20 @@ u8 *towire_dualopend_commit_rcvd(const tal_t *ctx, const struct channel_config * towire_u8_array(&p, local_shutdown_scriptpubkey, local_shutdown_len); towire_u16(&p, remote_shutdown_len); towire_u8_array(&p, remote_shutdown_scriptpubkey, remote_shutdown_len); + towire_u32(&p, lease_start_blockheight); + towire_u32(&p, lease_expiry); + if (!lease_commit_sig) + towire_bool(&p, false); + else { + towire_bool(&p, true); + towire_secp256k1_ecdsa_signature(&p, lease_commit_sig); + } + towire_u32(&p, lease_chan_max_msat); + towire_u16(&p, lease_chan_max_ppt); return memcheck(p, tal_count(p)); } -bool fromwire_dualopend_commit_rcvd(const tal_t *ctx, const void *p, struct channel_config *their_config, struct bitcoin_tx **remote_first_commit, struct penalty_base **pbase, struct bitcoin_signature *first_commit_sig, struct wally_psbt **psbt, struct pubkey *revocation_basepoint, struct pubkey *payment_basepoint, struct pubkey *htlc_basepoint, struct pubkey *delayed_payment_basepoint, struct pubkey *their_per_commit_point, struct pubkey *remote_fundingkey, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshis, struct amount_sat *our_funding_sats, u8 *channel_flags, u32 *feerate_per_kw_funding, u32 *feerate_per_kw_commitment, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey) +bool fromwire_dualopend_commit_rcvd(const tal_t *ctx, const void *p, struct channel_config *their_config, struct bitcoin_tx **remote_first_commit, struct penalty_base **pbase, struct bitcoin_signature *first_commit_sig, struct wally_psbt **psbt, struct pubkey *revocation_basepoint, struct pubkey *payment_basepoint, struct pubkey *htlc_basepoint, struct pubkey *delayed_payment_basepoint, struct pubkey *their_per_commit_point, struct pubkey *remote_fundingkey, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshis, struct amount_sat *our_funding_sats, u8 *channel_flags, u32 *feerate_per_kw_funding, u32 *feerate_per_kw_commitment, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, u32 *lease_start_blockheight, u32 *lease_expiry, secp256k1_ecdsa_signature **lease_commit_sig, u32 *lease_chan_max_msat, u16 *lease_chan_max_ppt) { u16 local_shutdown_len; u16 remote_shutdown_len; @@ -530,6 +584,16 @@ bool fromwire_dualopend_commit_rcvd(const tal_t *ctx, const void *p, struct chan // 2nd case remote_shutdown_scriptpubkey *remote_shutdown_scriptpubkey = remote_shutdown_len ? tal_arr(ctx, u8, remote_shutdown_len) : NULL; fromwire_u8_array(&cursor, &plen, *remote_shutdown_scriptpubkey, remote_shutdown_len); + *lease_start_blockheight = fromwire_u32(&cursor, &plen); + *lease_expiry = fromwire_u32(&cursor, &plen); + if (!fromwire_bool(&cursor, &plen)) + *lease_commit_sig = NULL; + else { + *lease_commit_sig = tal(ctx, secp256k1_ecdsa_signature); + fromwire_secp256k1_ecdsa_signature(&cursor, &plen, *lease_commit_sig); + } + *lease_chan_max_msat = fromwire_u32(&cursor, &plen); + *lease_chan_max_ppt = fromwire_u16(&cursor, &plen); return cursor != NULL; } @@ -605,7 +669,7 @@ bool fromwire_dualopend_fail(const tal_t *ctx, const void *p, wirestring **reaso /* WIRE: DUALOPEND_OPENER_INIT */ /* master->dualopend: hello */ -u8 *towire_dualopend_opener_init(const tal_t *ctx, const struct wally_psbt *psbt, struct amount_sat funding_amount, const u8 *local_shutdown_scriptpubkey, u32 feerate_per_kw, u32 feerate_per_kw_funding, u8 channel_flags) +u8 *towire_dualopend_opener_init(const tal_t *ctx, const struct wally_psbt *psbt, struct amount_sat funding_amount, const u8 *local_shutdown_scriptpubkey, u32 feerate_per_kw, u32 feerate_per_kw_funding, u8 channel_flags, struct amount_sat requested_sats, u32 blockheight, bool dry_run, const struct lease_rates *expected_rates) { u16 local_shutdown_len = tal_count(local_shutdown_scriptpubkey); u8 *p = tal_arr(ctx, u8, 0); @@ -618,10 +682,20 @@ u8 *towire_dualopend_opener_init(const tal_t *ctx, const struct wally_psbt *psbt towire_u32(&p, feerate_per_kw); towire_u32(&p, feerate_per_kw_funding); towire_u8(&p, channel_flags); + towire_amount_sat(&p, requested_sats); + towire_u32(&p, blockheight); + towire_bool(&p, dry_run); + /* must go last because embedded tu32 */ + if (!expected_rates) + towire_bool(&p, false); + else { + towire_bool(&p, true); + towire_lease_rates(&p, expected_rates); + } return memcheck(p, tal_count(p)); } -bool fromwire_dualopend_opener_init(const tal_t *ctx, const void *p, struct wally_psbt **psbt, struct amount_sat *funding_amount, u8 **local_shutdown_scriptpubkey, u32 *feerate_per_kw, u32 *feerate_per_kw_funding, u8 *channel_flags) +bool fromwire_dualopend_opener_init(const tal_t *ctx, const void *p, struct wally_psbt **psbt, struct amount_sat *funding_amount, u8 **local_shutdown_scriptpubkey, u32 *feerate_per_kw, u32 *feerate_per_kw_funding, u8 *channel_flags, struct amount_sat *requested_sats, u32 *blockheight, bool *dry_run, struct lease_rates **expected_rates) { u16 local_shutdown_len; @@ -639,6 +713,16 @@ bool fromwire_dualopend_opener_init(const tal_t *ctx, const void *p, struct wall *feerate_per_kw = fromwire_u32(&cursor, &plen); *feerate_per_kw_funding = fromwire_u32(&cursor, &plen); *channel_flags = fromwire_u8(&cursor, &plen); + *requested_sats = fromwire_amount_sat(&cursor, &plen); + *blockheight = fromwire_u32(&cursor, &plen); + *dry_run = fromwire_bool(&cursor, &plen); + /* must go last because embedded tu32 */ + if (!fromwire_bool(&cursor, &plen)) + *expected_rates = NULL; + else { + *expected_rates = tal(ctx, struct lease_rates); + fromwire_lease_rates(&cursor, &plen, *expected_rates); + } return cursor != NULL; } @@ -912,4 +996,104 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:0a1ed6e8461512630be3bb328083495d5c5f682c59dfb24561024ba8fa0d3b70 + +/* WIRE: DUALOPEND_DRY_RUN */ +/* dualopend -> master: this was a dry run */ +u8 *towire_dualopend_dry_run(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat our_funding, struct amount_sat their_funding, const struct lease_rates *lease_rates) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_DUALOPEND_DRY_RUN); + towire_channel_id(&p, channel_id); + towire_amount_sat(&p, our_funding); + towire_amount_sat(&p, their_funding); + /* must go last because of embedded tu32 */ + if (!lease_rates) + towire_bool(&p, false); + else { + towire_bool(&p, true); + towire_lease_rates(&p, lease_rates); + } + + return memcheck(p, tal_count(p)); +} +bool fromwire_dualopend_dry_run(const tal_t *ctx, const void *p, struct channel_id *channel_id, struct amount_sat *our_funding, struct amount_sat *their_funding, struct lease_rates **lease_rates) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_DRY_RUN) + return false; + fromwire_channel_id(&cursor, &plen, channel_id); + *our_funding = fromwire_amount_sat(&cursor, &plen); + *their_funding = fromwire_amount_sat(&cursor, &plen); + /* must go last because of embedded tu32 */ + if (!fromwire_bool(&cursor, &plen)) + *lease_rates = NULL; + else { + *lease_rates = tal(ctx, struct lease_rates); + fromwire_lease_rates(&cursor, &plen, *lease_rates); + } + return cursor != NULL; +} + +/* WIRE: DUALOPEND_VALIDATE_LEASE */ +/* dualopend -> master: validate liqudity offer sig */ +u8 *towire_dualopend_validate_lease(const tal_t *ctx, const secp256k1_ecdsa_signature *sig, u32 lease_expiry, u32 chan_fee_max_base_msat, u16 chan_fee_max_ppt, const struct pubkey *their_pubkey) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_DUALOPEND_VALIDATE_LEASE); + towire_secp256k1_ecdsa_signature(&p, sig); + towire_u32(&p, lease_expiry); + towire_u32(&p, chan_fee_max_base_msat); + towire_u16(&p, chan_fee_max_ppt); + towire_pubkey(&p, their_pubkey); + + return memcheck(p, tal_count(p)); +} +bool fromwire_dualopend_validate_lease(const void *p, secp256k1_ecdsa_signature *sig, u32 *lease_expiry, u32 *chan_fee_max_base_msat, u16 *chan_fee_max_ppt, struct pubkey *their_pubkey) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_VALIDATE_LEASE) + return false; + fromwire_secp256k1_ecdsa_signature(&cursor, &plen, sig); + *lease_expiry = fromwire_u32(&cursor, &plen); + *chan_fee_max_base_msat = fromwire_u32(&cursor, &plen); + *chan_fee_max_ppt = fromwire_u16(&cursor, &plen); + fromwire_pubkey(&cursor, &plen, their_pubkey); + return cursor != NULL; +} + +/* WIRE: DUALOPEND_VALIDATE_LEASE_REPLY */ +u8 *towire_dualopend_validate_lease_reply(const tal_t *ctx, const wirestring *err_msg) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_DUALOPEND_VALIDATE_LEASE_REPLY); + if (!err_msg) + towire_bool(&p, false); + else { + towire_bool(&p, true); + towire_wirestring(&p, err_msg); + } + + return memcheck(p, tal_count(p)); +} +bool fromwire_dualopend_validate_lease_reply(const tal_t *ctx, const void *p, wirestring **err_msg) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_DUALOPEND_VALIDATE_LEASE_REPLY) + return false; + if (!fromwire_bool(&cursor, &plen)) + *err_msg = NULL; + else { + *err_msg = fromwire_wirestring(ctx, &cursor, &plen); + } + return cursor != NULL; +} +// SHA256STAMP:b849d2454ff747b97adbf68a13675b91673d06f05b454a9e96744a2c4a12930f diff --git a/openingd/dualopend_wiregen.h b/openingd/dualopend_wiregen.h index de36baf2f782..443ff8453b7e 100644 --- a/openingd/dualopend_wiregen.h +++ b/openingd/dualopend_wiregen.h @@ -18,6 +18,8 @@ #include #include #include +#include +#include enum dualopend_wire { WIRE_DUALOPEND_INIT = 7000, @@ -71,6 +73,11 @@ enum dualopend_wire { /* master -> dualopend: do you have a memleak? */ WIRE_DUALOPEND_DEV_MEMLEAK = 7033, WIRE_DUALOPEND_DEV_MEMLEAK_REPLY = 7133, + /* dualopend -> master: this was a dry run */ + WIRE_DUALOPEND_DRY_RUN = 7026, + /* dualopend -> master: validate liqudity offer sig */ + WIRE_DUALOPEND_VALIDATE_LEASE = 7027, + WIRE_DUALOPEND_VALIDATE_LEASE_REPLY = 7127, }; const char *dualopend_wire_name(int e); @@ -91,18 +98,18 @@ bool fromwire_dualopend_init(const tal_t *ctx, const void *p, const struct chain /* WIRE: DUALOPEND_REINIT */ /* master-dualopend: peer has reconnected */ -u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, const struct channel_config *their_config, const struct channel_id *channel_id, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct pubkey *their_funding_pubkey, u32 minimum_depth, const struct bitcoin_txid *funding_txid, u16 funding_txout, u32 orignal_feerate_per_kw_funding, u32 most_recent_feerate_per_kw_funding, struct amount_sat funding_satoshi, struct amount_msat our_funding, const struct basepoints *their_basepoints, const struct pubkey *remote_per_commit, const struct wally_psbt *funding_psbt, enum side opener, bool local_funding_locked, bool remote_funding_locked, bool send_shutdown, bool remote_shutdown_received, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, bool remote_funding_sigs_received, const struct fee_states *fee_states, u8 channel_flags); -bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, struct channel_config *their_config, struct channel_id *channel_id, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct pubkey *their_funding_pubkey, u32 *minimum_depth, struct bitcoin_txid *funding_txid, u16 *funding_txout, u32 *orignal_feerate_per_kw_funding, u32 *most_recent_feerate_per_kw_funding, struct amount_sat *funding_satoshi, struct amount_msat *our_funding, struct basepoints *their_basepoints, struct pubkey *remote_per_commit, struct wally_psbt **funding_psbt, enum side *opener, bool *local_funding_locked, bool *remote_funding_locked, bool *send_shutdown, bool *remote_shutdown_received, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, bool *remote_funding_sigs_received, struct fee_states **fee_states, u8 *channel_flags); +u8 *towire_dualopend_reinit(const tal_t *ctx, const struct chainparams *chainparams, const struct feature_set *our_feature_set, const u8 *their_init_features, const struct channel_config *our_config, const struct channel_config *their_config, const struct channel_id *channel_id, u32 max_to_self_delay, struct amount_msat min_effective_htlc_capacity_msat, const struct per_peer_state *pps, const struct basepoints *our_basepoints, const struct pubkey *our_funding_pubkey, const struct pubkey *their_funding_pubkey, u32 minimum_depth, const struct bitcoin_txid *funding_txid, u16 funding_txout, u32 orignal_feerate_per_kw_funding, u32 most_recent_feerate_per_kw_funding, struct amount_sat funding_satoshi, struct amount_msat our_funding, const struct basepoints *their_basepoints, const struct pubkey *remote_per_commit, const struct wally_psbt *funding_psbt, enum side opener, bool local_funding_locked, bool remote_funding_locked, bool send_shutdown, bool remote_shutdown_received, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, bool remote_funding_sigs_received, const struct fee_states *fee_states, u8 channel_flags, u32 lease_start_blockheight, u32 lease_expiry, const secp256k1_ecdsa_signature *lease_commit_sig, u32 lease_chan_max_msat, u16 lease_chan_max_ppt); +bool fromwire_dualopend_reinit(const tal_t *ctx, const void *p, const struct chainparams **chainparams, struct feature_set **our_feature_set, u8 **their_init_features, struct channel_config *our_config, struct channel_config *their_config, struct channel_id *channel_id, u32 *max_to_self_delay, struct amount_msat *min_effective_htlc_capacity_msat, struct per_peer_state **pps, struct basepoints *our_basepoints, struct pubkey *our_funding_pubkey, struct pubkey *their_funding_pubkey, u32 *minimum_depth, struct bitcoin_txid *funding_txid, u16 *funding_txout, u32 *orignal_feerate_per_kw_funding, u32 *most_recent_feerate_per_kw_funding, struct amount_sat *funding_satoshi, struct amount_msat *our_funding, struct basepoints *their_basepoints, struct pubkey *remote_per_commit, struct wally_psbt **funding_psbt, enum side *opener, bool *local_funding_locked, bool *remote_funding_locked, bool *send_shutdown, bool *remote_shutdown_received, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, bool *remote_funding_sigs_received, struct fee_states **fee_states, u8 *channel_flags, u32 *lease_start_blockheight, u32 *lease_expiry, secp256k1_ecdsa_signature **lease_commit_sig, u32 *lease_chan_max_msat, u16 *lease_chan_max_ppt); /* WIRE: DUALOPEND_GOT_OFFER */ /* dualopend->master: they offered channel */ -u8 *towire_dualopend_got_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat opener_funding, struct amount_sat dust_limit_satoshis, struct amount_msat max_htlc_value_in_flight_msat, struct amount_msat htlc_minimum_msat, u32 feerate_per_kw_funding, u32 feerate_per_kw_commitment, u16 to_self_delay, u16 max_accepted_htlcs, u8 channel_flags, u32 locktime, const u8 *shutdown_scriptpubkey); -bool fromwire_dualopend_got_offer(const tal_t *ctx, const void *p, struct channel_id *channel_id, struct amount_sat *opener_funding, struct amount_sat *dust_limit_satoshis, struct amount_msat *max_htlc_value_in_flight_msat, struct amount_msat *htlc_minimum_msat, u32 *feerate_per_kw_funding, u32 *feerate_per_kw_commitment, u16 *to_self_delay, u16 *max_accepted_htlcs, u8 *channel_flags, u32 *locktime, u8 **shutdown_scriptpubkey); +u8 *towire_dualopend_got_offer(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat opener_funding, struct amount_sat dust_limit_satoshis, struct amount_msat max_htlc_value_in_flight_msat, struct amount_msat htlc_minimum_msat, u32 feerate_per_kw_funding, u32 feerate_per_kw_commitment, u16 to_self_delay, u16 max_accepted_htlcs, u8 channel_flags, u32 locktime, const u8 *shutdown_scriptpubkey, struct amount_sat requested_amt, u32 lease_blockheight_start); +bool fromwire_dualopend_got_offer(const tal_t *ctx, const void *p, struct channel_id *channel_id, struct amount_sat *opener_funding, struct amount_sat *dust_limit_satoshis, struct amount_msat *max_htlc_value_in_flight_msat, struct amount_msat *htlc_minimum_msat, u32 *feerate_per_kw_funding, u32 *feerate_per_kw_commitment, u16 *to_self_delay, u16 *max_accepted_htlcs, u8 *channel_flags, u32 *locktime, u8 **shutdown_scriptpubkey, struct amount_sat *requested_amt, u32 *lease_blockheight_start); /* WIRE: DUALOPEND_GOT_OFFER_REPLY */ /* master->dualopend: reply back with our first funding info/contribs */ -u8 *towire_dualopend_got_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, const struct wally_psbt *psbt, const u8 *our_shutdown_scriptpubkey); -bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, struct wally_psbt **psbt, u8 **our_shutdown_scriptpubkey); +u8 *towire_dualopend_got_offer_reply(const tal_t *ctx, struct amount_sat accepter_funding, const struct wally_psbt *psbt, const u8 *our_shutdown_scriptpubkey, const struct lease_rates *lease_rates); +bool fromwire_dualopend_got_offer_reply(const tal_t *ctx, const void *p, struct amount_sat *accepter_funding, struct wally_psbt **psbt, u8 **our_shutdown_scriptpubkey, struct lease_rates **lease_rates); /* WIRE: DUALOPEND_GOT_RBF_OFFER */ /* dualopend->master: they offered a RBF */ @@ -132,8 +139,8 @@ bool fromwire_dualopend_rbf_init(const tal_t *ctx, const void *p, struct amount_ /* WIRE: DUALOPEND_COMMIT_RCVD */ /* dualopend->master: ready to commit channel open to database and */ /* get some signatures for the funding_tx. */ -u8 *towire_dualopend_commit_rcvd(const tal_t *ctx, const struct channel_config *their_config, const struct bitcoin_tx *remote_first_commit, const struct penalty_base *pbase, const struct bitcoin_signature *first_commit_sig, const struct wally_psbt *psbt, const struct pubkey *revocation_basepoint, const struct pubkey *payment_basepoint, const struct pubkey *htlc_basepoint, const struct pubkey *delayed_payment_basepoint, const struct pubkey *their_per_commit_point, const struct pubkey *remote_fundingkey, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshis, struct amount_sat our_funding_sats, u8 channel_flags, u32 feerate_per_kw_funding, u32 feerate_per_kw_commitment, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey); -bool fromwire_dualopend_commit_rcvd(const tal_t *ctx, const void *p, struct channel_config *their_config, struct bitcoin_tx **remote_first_commit, struct penalty_base **pbase, struct bitcoin_signature *first_commit_sig, struct wally_psbt **psbt, struct pubkey *revocation_basepoint, struct pubkey *payment_basepoint, struct pubkey *htlc_basepoint, struct pubkey *delayed_payment_basepoint, struct pubkey *their_per_commit_point, struct pubkey *remote_fundingkey, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshis, struct amount_sat *our_funding_sats, u8 *channel_flags, u32 *feerate_per_kw_funding, u32 *feerate_per_kw_commitment, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey); +u8 *towire_dualopend_commit_rcvd(const tal_t *ctx, const struct channel_config *their_config, const struct bitcoin_tx *remote_first_commit, const struct penalty_base *pbase, const struct bitcoin_signature *first_commit_sig, const struct wally_psbt *psbt, const struct pubkey *revocation_basepoint, const struct pubkey *payment_basepoint, const struct pubkey *htlc_basepoint, const struct pubkey *delayed_payment_basepoint, const struct pubkey *their_per_commit_point, const struct pubkey *remote_fundingkey, const struct bitcoin_txid *funding_txid, u16 funding_txout, struct amount_sat funding_satoshis, struct amount_sat our_funding_sats, u8 channel_flags, u32 feerate_per_kw_funding, u32 feerate_per_kw_commitment, const u8 *local_shutdown_scriptpubkey, const u8 *remote_shutdown_scriptpubkey, u32 lease_start_blockheight, u32 lease_expiry, const secp256k1_ecdsa_signature *lease_commit_sig, u32 lease_chan_max_msat, u16 lease_chan_max_ppt); +bool fromwire_dualopend_commit_rcvd(const tal_t *ctx, const void *p, struct channel_config *their_config, struct bitcoin_tx **remote_first_commit, struct penalty_base **pbase, struct bitcoin_signature *first_commit_sig, struct wally_psbt **psbt, struct pubkey *revocation_basepoint, struct pubkey *payment_basepoint, struct pubkey *htlc_basepoint, struct pubkey *delayed_payment_basepoint, struct pubkey *their_per_commit_point, struct pubkey *remote_fundingkey, struct bitcoin_txid *funding_txid, u16 *funding_txout, struct amount_sat *funding_satoshis, struct amount_sat *our_funding_sats, u8 *channel_flags, u32 *feerate_per_kw_funding, u32 *feerate_per_kw_commitment, u8 **local_shutdown_scriptpubkey, u8 **remote_shutdown_scriptpubkey, u32 *lease_start_blockheight, u32 *lease_expiry, secp256k1_ecdsa_signature **lease_commit_sig, u32 *lease_chan_max_msat, u16 *lease_chan_max_ppt); /* WIRE: DUALOPEND_PSBT_CHANGED */ /* dualopend->master: peer updated the psbt */ @@ -152,8 +159,8 @@ bool fromwire_dualopend_fail(const tal_t *ctx, const void *p, wirestring **reaso /* WIRE: DUALOPEND_OPENER_INIT */ /* master->dualopend: hello */ -u8 *towire_dualopend_opener_init(const tal_t *ctx, const struct wally_psbt *psbt, struct amount_sat funding_amount, const u8 *local_shutdown_scriptpubkey, u32 feerate_per_kw, u32 feerate_per_kw_funding, u8 channel_flags); -bool fromwire_dualopend_opener_init(const tal_t *ctx, const void *p, struct wally_psbt **psbt, struct amount_sat *funding_amount, u8 **local_shutdown_scriptpubkey, u32 *feerate_per_kw, u32 *feerate_per_kw_funding, u8 *channel_flags); +u8 *towire_dualopend_opener_init(const tal_t *ctx, const struct wally_psbt *psbt, struct amount_sat funding_amount, const u8 *local_shutdown_scriptpubkey, u32 feerate_per_kw, u32 feerate_per_kw_funding, u8 channel_flags, struct amount_sat requested_sats, u32 blockheight, bool dry_run, const struct lease_rates *expected_rates); +bool fromwire_dualopend_opener_init(const tal_t *ctx, const void *p, struct wally_psbt **psbt, struct amount_sat *funding_amount, u8 **local_shutdown_scriptpubkey, u32 *feerate_per_kw, u32 *feerate_per_kw_funding, u8 *channel_flags, struct amount_sat *requested_sats, u32 *blockheight, bool *dry_run, struct lease_rates **expected_rates); /* WIRE: DUALOPEND_FUNDING_SIGS */ /* dualopend->master received tx_sigs from peer */ @@ -214,6 +221,20 @@ bool fromwire_dualopend_dev_memleak(const void *p); u8 *towire_dualopend_dev_memleak_reply(const tal_t *ctx, bool leak); bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak); +/* WIRE: DUALOPEND_DRY_RUN */ +/* dualopend -> master: this was a dry run */ +u8 *towire_dualopend_dry_run(const tal_t *ctx, const struct channel_id *channel_id, struct amount_sat our_funding, struct amount_sat their_funding, const struct lease_rates *lease_rates); +bool fromwire_dualopend_dry_run(const tal_t *ctx, const void *p, struct channel_id *channel_id, struct amount_sat *our_funding, struct amount_sat *their_funding, struct lease_rates **lease_rates); + +/* WIRE: DUALOPEND_VALIDATE_LEASE */ +/* dualopend -> master: validate liqudity offer sig */ +u8 *towire_dualopend_validate_lease(const tal_t *ctx, const secp256k1_ecdsa_signature *sig, u32 lease_expiry, u32 chan_fee_max_base_msat, u16 chan_fee_max_ppt, const struct pubkey *their_pubkey); +bool fromwire_dualopend_validate_lease(const void *p, secp256k1_ecdsa_signature *sig, u32 *lease_expiry, u32 *chan_fee_max_base_msat, u16 *chan_fee_max_ppt, struct pubkey *their_pubkey); + +/* WIRE: DUALOPEND_VALIDATE_LEASE_REPLY */ +u8 *towire_dualopend_validate_lease_reply(const tal_t *ctx, const wirestring *err_msg); +bool fromwire_dualopend_validate_lease_reply(const tal_t *ctx, const void *p, wirestring **err_msg); + #endif /* LIGHTNING_OPENINGD_DUALOPEND_WIREGEN_H */ -// SHA256STAMP:0a1ed6e8461512630be3bb328083495d5c5f682c59dfb24561024ba8fa0d3b70 +// SHA256STAMP:b849d2454ff747b97adbf68a13675b91673d06f05b454a9e96744a2c4a12930f diff --git a/openingd/openingd.c b/openingd/openingd.c index 7a0d1bdeed2d..70bde6b0d057 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -510,6 +510,7 @@ static bool funder_finalize_channel_setup(struct state *state, &state->funding_txid, state->funding_txout, state->minimum_depth, + NULL, 0, /* No channel lease */ state->funding, local_msat, take(new_fee_states(NULL, LOCAL, @@ -1000,6 +1001,7 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) &state->funding_txid, state->funding_txout, state->minimum_depth, + NULL, 0, /* No channel lease */ state->funding, state->push_msat, take(new_fee_states(NULL, REMOTE, diff --git a/openingd/openingd_wiregen.c b/openingd/openingd_wiregen.c index 7f615aa0996d..8b0623357fdd 100644 --- a/openingd/openingd_wiregen.c +++ b/openingd/openingd_wiregen.c @@ -569,4 +569,4 @@ bool fromwire_openingd_dev_memleak_reply(const void *p, bool *leak) *leak = fromwire_bool(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:d2fcabdf157b098608e47dcdc37db0f46fe8d466d74159969544d7c4bb77f061 +// SHA256STAMP:293e24db57d783e163f97f3b696f3ea0a74f1bb31e4b06fd37e5cc915b177f52 diff --git a/openingd/openingd_wiregen.h b/openingd/openingd_wiregen.h index 047c82110783..5e48fd3884ad 100644 --- a/openingd/openingd_wiregen.h +++ b/openingd/openingd_wiregen.h @@ -121,4 +121,4 @@ bool fromwire_openingd_dev_memleak_reply(const void *p, bool *leak); #endif /* LIGHTNING_OPENINGD_OPENINGD_WIREGEN_H */ -// SHA256STAMP:d2fcabdf157b098608e47dcdc37db0f46fe8d466d74159969544d7c4bb77f061 +// SHA256STAMP:293e24db57d783e163f97f3b696f3ea0a74f1bb31e4b06fd37e5cc915b177f52 diff --git a/plugins/Makefile b/plugins/Makefile index 410a2a7be9e6..3d2cf3cd3005 100644 --- a/plugins/Makefile +++ b/plugins/Makefile @@ -89,7 +89,9 @@ ALL_PROGRAMS += $(PLUGINS) PLUGIN_COMMON_OBJS := \ bitcoin/base58.o \ + bitcoin/block.o \ bitcoin/feerate.o \ + bitcoin/preimage.o \ bitcoin/privkey.o \ bitcoin/psbt.o \ bitcoin/pubkey.o \ @@ -113,6 +115,7 @@ PLUGIN_COMMON_OBJS := \ common/json_helpers.o \ common/json_stream.o \ common/json_tok.o \ + common/lease_rates.o \ common/memleak.o \ common/node_id.o \ common/param.o \ @@ -127,6 +130,7 @@ PLUGIN_COMMON_OBJS := \ common/wireaddr.o \ wire/fromwire.o \ wire/onion$(EXP)_wiregen.o \ + wire/peer$(EXP)_wiregen.o \ wire/tlvstream.o \ wire/towire.o @@ -142,7 +146,7 @@ plugins/bcli: bitcoin/chainparams.o $(PLUGIN_BCLI_OBJS) $(PLUGIN_LIB_OBJS) $(PLU plugins/keysend: bitcoin/chainparams.o wire/tlvstream.o wire/onion$(EXP)_wiregen.o $(PLUGIN_KEYSEND_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_PAY_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) common/gossmap.o common/fp16.o common/route.o common/dijkstra.o $(PLUGIN_KEYSEND_OBJS): $(PLUGIN_PAY_LIB_HEADER) -plugins/spenderp: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) +plugins/spenderp: bitcoin/block.o bitcoin/chainparams.o bitcoin/preimage.o bitcoin/psbt.o common/psbt_open.o wire/peer${EXP}_wiregen.o $(PLUGIN_SPENDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS) plugins/offers: bitcoin/chainparams.o $(PLUGIN_OFFERS_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/bolt11_json.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) $(CCAN_OBJS) diff --git a/plugins/funder.c b/plugins/funder.c index 1731f7fbd1a2..9dbbba5a4bf5 100644 --- a/plugins/funder.c +++ b/plugins/funder.c @@ -10,24 +10,27 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include #include #include +#include /* In-progress channel opens */ static struct list_head pending_opens; /* Current set policy */ -static struct funder_policy current_policy; +static struct funder_policy *current_policy; struct pending_open { struct list_node list; @@ -279,6 +282,9 @@ struct open_info { struct amount_sat channel_max; u64 funding_feerate_perkw; u32 locktime; + u32 lease_blockheight; + u32 node_blockheight; + struct amount_sat requested_lease; }; static struct command_result * @@ -319,6 +325,11 @@ psbt_funded(struct command *cmd, json_add_amount_msat_only(response, "our_funding_msat", our_funding_msat); + /* If we're accepting an lease request, *and* they've + * requested one, fill in our most recent infos */ + if (current_policy->rates && !amount_sat_zero(info->requested_lease)) + json_add_lease_rates(response, current_policy->rates); + return command_finished(cmd, response); } @@ -416,6 +427,7 @@ listfunds_success(struct command *cmd, info->their_funding, available_funds, info->channel_max, + info->requested_lease, &info->our_funding); plugin_log(cmd->plugin, LOG_DBG, "Policy %s returned funding amount of %s. %s", @@ -424,7 +436,7 @@ listfunds_success(struct command *cmd, &info->our_funding), funding_err ? funding_err : ""); - if (amount_sat_eq(info->our_funding, AMOUNT_SAT(0))) + if (amount_sat_zero(info->our_funding)) return command_hook_success(cmd); plugin_log(cmd->plugin, LOG_DBG, @@ -528,6 +540,21 @@ json_openchannel2_call(struct command *cmd, err, json_tok_full_len(params), json_tok_full(buf, params)); + err = json_scan(tmpctx, buf, params, + "{openchannel2:{" + "requested_lease_msat:%" + ",lease_blockheight_start:%" + ",node_blockheight:%}}", + JSON_SCAN(json_to_sat, &info->requested_lease), + JSON_SCAN(json_to_u32, &info->node_blockheight), + JSON_SCAN(json_to_u32, &info->lease_blockheight)); + + /* These aren't necessarily included */ + if (err) { + info->requested_lease = AMOUNT_SAT(0); + info->node_blockheight = 0; + info->lease_blockheight = 0; + } /* If there's no channel_max, it's actually infinity */ err = json_scan(tmpctx, buf, params, @@ -550,6 +577,53 @@ json_openchannel2_call(struct command *cmd, return command_hook_success(cmd); } + /* If they've requested funds, but we're not actually + * supporting requested funds...*/ + if (!current_policy->rates && + !amount_sat_zero(info->requested_lease)) { + struct json_stream *res = jsonrpc_stream_success(cmd); + json_add_string(res, "result", "reject"); + json_add_string(res, "error_message", + "Peer requested funds but we're not advertising" + " liquidity right now"); + return command_finished(cmd, res); + } + + + /* Check that their block height isn't too far behind */ + if (!amount_sat_zero(info->requested_lease)) { + u32 upper_bound, lower_bound; + + /* BOLT- #2: + * The receiving node: + * - MAY fail the negotiation if: ... + * - if the `option_will_fund` tlv is present and: + * - the `blockheight` is considered too far in the + * past or future + */ + /* We consider 24 hrs too far out */ + upper_bound = info->node_blockheight + 24 * 6; + lower_bound = info->node_blockheight - 24 * 6; + + /* Check overflow */ + if (upper_bound < info->node_blockheight) + upper_bound = -1; + if (lower_bound > info->node_blockheight) + lower_bound = 0; + + if (upper_bound < info->lease_blockheight + || lower_bound > info->lease_blockheight) { + + plugin_log(cmd->plugin, LOG_DBG, + "their blockheight %d is out of" + " our bounds (ours is %d)", + info->lease_blockheight, + info->node_blockheight); + + return command_hook_success(cmd); + } + } + /* Figure out what our funds are */ req = jsonrpc_request_start(cmd->plugin, cmd, "listfunds", @@ -679,8 +753,8 @@ static void json_channel_open_failed(struct command *cmd, unreserve_psbt(open); } -static void policy_to_json(struct json_stream *stream, - struct funder_policy *policy) +static void json_add_policy(struct json_stream *stream, + struct funder_policy *policy) { json_add_string(stream, "summary", funder_policy_desc(stream, current_policy)); @@ -699,6 +773,12 @@ static void policy_to_json(struct json_stream *stream, policy->reserve_tank); json_add_num(stream, "fuzz_percent", policy->fuzz_factor); json_add_num(stream, "fund_probability", policy->fund_probability); + + if (policy->rates) { + json_add_lease_rates(stream, policy->rates); + json_add_string(stream, "compact_lease", + lease_rates_tohex(tmpctx, policy->rates)); + } } static struct command_result * @@ -744,85 +824,265 @@ param_policy_mod(struct command *cmd, const char *name, return NULL; } +static struct command_result * +parse_lease_rates(struct command *cmd, const char *buffer, + const jsmntok_t *tok, + struct funder_policy *policy, + struct funder_policy *current_policy, + u32 *lease_fee_basis, + struct amount_sat *lease_fee_sats, + u32 *funding_weight, + u32 *chan_fee_ppt, + struct amount_msat *chan_fee_msats) + +{ + /* If there's already rates set, we start with those */ + if (!lease_rates_empty(current_policy->rates)) + policy->rates = tal_dup(policy, struct lease_rates, + current_policy->rates); + else if (lease_fee_basis + || lease_fee_sats + || funding_weight + || chan_fee_ppt + || chan_fee_msats) + policy->rates = default_lease_rates(policy); + else + policy->rates = NULL; + + + if (lease_fee_basis) { + policy->rates->lease_fee_basis = *lease_fee_basis; + + /* Check for overflow */ + if (policy->rates->lease_fee_basis != *lease_fee_basis) + return command_fail_badparam(cmd, "lease_fee_basis", + buffer, tok, "overflow"); + } + + if (lease_fee_sats) { + policy->rates->lease_fee_base_sat + = lease_fee_sats->satoshis; /* Raw: conversion */ + if (policy->rates->lease_fee_base_sat + != lease_fee_sats->satoshis) /* Raw: comparison */ + return command_fail_badparam(cmd, "lease_fee_base_msat", + buffer, tok, "overflow"); + } + + if (funding_weight) { + policy->rates->funding_weight = *funding_weight; + + /* Check for overflow */ + if (policy->rates->funding_weight != *funding_weight) + return command_fail_badparam(cmd, "funding_weight", + buffer, tok, "overflow"); + } + + if (chan_fee_ppt) { + policy->rates->channel_fee_max_proportional_thousandths + = *chan_fee_ppt; + + /* Check for overflow */ + if (policy->rates->channel_fee_max_proportional_thousandths + != *chan_fee_ppt) + return command_fail_badparam(cmd, "channel_fee_max_proportional_thousandths", + buffer, tok, "overflow"); + } + + if (chan_fee_msats) { + policy->rates->channel_fee_max_base_msat + = chan_fee_msats->millisatoshis; /* Raw: conversion */ + if (policy->rates->channel_fee_max_base_msat + != chan_fee_msats->millisatoshis) /* Raw: comparison */ + return command_fail_badparam(cmd, + "channel_fee_max_base_msat", + buffer, tok, "overflow"); + } + + return NULL; +} + +static struct command_result * +leaserates_set(struct command *cmd, const char *buf, + const jsmntok_t *result, + struct funder_policy *policy) +{ + struct json_stream *res; + + /* Ok, we updated lightningd with latest info */ + res = jsonrpc_stream_success(cmd); + json_add_policy(res, policy); + return command_finished(cmd, res); +} + static struct command_result * json_funderupdate(struct command *cmd, const char *buf, const jsmntok_t *params) { - struct json_stream *res; struct amount_sat *min_their_funding, *max_their_funding, *per_channel_min, *per_channel_max, - *reserve_tank; - u32 *fuzz_factor, *fund_probability; + *reserve_tank, *lease_fee_sats; + struct amount_msat *channel_fee_msats; + u32 *fuzz_factor, *fund_probability, *chan_fee_ppt, + *lease_fee_basis, *funding_weight; u64 *mod; + bool *leases_only; enum funder_opt *opt; + const struct out_req *req; const char *err; - struct funder_policy policy = current_policy; + struct command_result *res; + + struct funder_policy *policy = tal(tal_parent(current_policy), + struct funder_policy); if (!param(cmd, buf, params, - p_opt("policy", param_funder_opt, &opt), - p_opt("policy_mod", param_policy_mod, &mod), - p_opt("min_their_funding", param_sat, &min_their_funding), - p_opt("max_their_funding", param_sat, &max_their_funding), - p_opt("per_channel_min", param_sat, &per_channel_min), - p_opt("per_channel_max", param_sat, &per_channel_max), - p_opt("reserve_tank", param_sat, &reserve_tank), - p_opt("fuzz_percent", param_number, &fuzz_factor), - p_opt("fund_probability", param_number, &fund_probability), + p_opt_def("policy", param_funder_opt, &opt, + current_policy->opt), + p_opt_def("policy_mod", param_policy_mod, &mod, + current_policy->mod), + p_opt_def("leases_only", param_bool, &leases_only, + current_policy->leases_only), + p_opt_def("min_their_funding", param_sat, + &min_their_funding, + current_policy->min_their_funding), + p_opt_def("max_their_funding", param_sat, + &max_their_funding, + current_policy->max_their_funding), + p_opt_def("per_channel_min", param_sat, + &per_channel_min, + current_policy->per_channel_min), + p_opt_def("per_channel_max", param_sat, + &per_channel_max, + current_policy->per_channel_max), + p_opt_def("reserve_tank", param_sat, &reserve_tank, + current_policy->reserve_tank), + p_opt_def("fuzz_percent", param_number, + &fuzz_factor, + current_policy->fuzz_factor), + p_opt_def("fund_probability", param_number, + &fund_probability, + current_policy->fund_probability), + p_opt("lease_fee_base_msat", param_sat, &lease_fee_sats), + p_opt("lease_fee_basis", param_number, &lease_fee_basis), + p_opt("funding_weight", param_number, &funding_weight), + p_opt("channel_fee_max_base_msat", param_msat, + &channel_fee_msats), + p_opt("channel_fee_max_proportional_thousandths", + param_number, &chan_fee_ppt), NULL)) return command_param_failed(); - if (opt) - policy.opt = *opt; - if (mod) - policy.mod = *mod; - if (min_their_funding) - policy.min_their_funding = *min_their_funding; - if (max_their_funding) - policy.max_their_funding = *max_their_funding; - if (per_channel_min) - policy.per_channel_min = *per_channel_min; - if (per_channel_max) - policy.per_channel_max = *per_channel_max; - if (reserve_tank) - policy.reserve_tank = *reserve_tank; - if (fuzz_factor) - policy.fuzz_factor = *fuzz_factor; - if (fund_probability) - policy.fund_probability = *fund_probability; - - err = funder_check_policy(&policy); - if (err) + policy->opt = *opt; + policy->mod = *mod; + policy->min_their_funding = *min_their_funding; + policy->max_their_funding = *max_their_funding; + policy->per_channel_min = *per_channel_min; + policy->per_channel_max = *per_channel_max; + policy->reserve_tank = *reserve_tank; + policy->fuzz_factor = *fuzz_factor; + policy->fund_probability = *fund_probability; + policy->leases_only = *leases_only; + + res = parse_lease_rates(cmd, buf, params, + policy, current_policy, + lease_fee_basis, + lease_fee_sats, + funding_weight, + chan_fee_ppt, + channel_fee_msats); + if (res) + return res; + + err = funder_check_policy(policy); + if (err) { + tal_free(policy); return command_done_err(cmd, JSONRPC2_INVALID_PARAMS, err, NULL); + } + tal_free(current_policy); current_policy = policy; - res = jsonrpc_stream_success(cmd); - policy_to_json(res, ¤t_policy); - return command_finished(cmd, res); + + /* Update lightningd, also */ + req = jsonrpc_request_start(cmd->plugin, cmd, + "setleaserates", + &leaserates_set, + &forward_error, + current_policy); + + if (current_policy->rates) + json_add_lease_rates(req->js, current_policy->rates); + else { + /* Add empty rates to turn off */ + struct lease_rates rates; + memset(&rates, 0, sizeof(rates)); + json_add_lease_rates(req->js, &rates); + } + + return send_outreq(cmd->plugin, req); } -static const struct plugin_command commands[] = { { +static const struct plugin_command commands[] = { + { "funderupdate", - "channels", - "Update configuration for dual-funding offer", - "Update current funder settings. Modifies how node" - " reacts to incoming channel open requests. Responds with list" + "liquidity", + "Configuration for dual-funding settings.", + "Update current settings. Modifies how node reacts to" + " incoming channel open requests. Responds with list" " of current configs.", json_funderupdate - } + }, }; +static void tell_lightningd_lease_rates(struct plugin *p, + struct lease_rates *rates) +{ + struct json_out *jout; + struct amount_sat val; + struct amount_msat mval; + + /* Tell lightningd with our lease rates*/ + jout = json_out_new(NULL); + json_out_start(jout, NULL, '{'); + + val = amount_sat(rates->lease_fee_base_sat); + json_out_addstr(jout, "lease_fee_base_msat", + type_to_string(tmpctx, struct amount_sat, &val)); + json_out_add(jout, "lease_fee_basis", false, + "%d", rates->lease_fee_basis); + + json_out_add(jout, "funding_weight", false, + "%d", rates->funding_weight); + + mval = amount_msat(rates->channel_fee_max_base_msat); + json_out_addstr(jout, "channel_fee_max_base_msat", + type_to_string(tmpctx, struct amount_msat, &mval)); + json_out_add(jout, "channel_fee_max_proportional_thousandths", false, + "%d", rates->channel_fee_max_proportional_thousandths); + + json_out_end(jout, '}'); + json_out_finished(jout); + + rpc_scan(p, "setleaserates", take(jout), + /* Unused */ + "{lease_fee_base_msat:%}", + JSON_SCAN(json_to_sat, &val)); + +} + static const char *init(struct plugin *p, const char *b, const jsmntok_t *t) { const char *err; list_head_init(&pending_opens); - err = funder_check_policy(¤t_policy); + err = funder_check_policy(current_policy); if (err) plugin_err(p, "Invalid parameter combination: %s", err); + if (current_policy->rates) + tell_lightningd_lease_rates(p, current_policy->rates); + return NULL; } @@ -856,6 +1116,33 @@ const struct plugin_notification notifs[] = { }, }; +static char *option_channel_base(const char *arg, struct funder_policy *policy) +{ + struct amount_msat amt; + + if (!parse_amount_msat(&amt, arg, strlen(arg))) + return tal_fmt(NULL, "Unable to parse amount '%s'", arg); + + if (!policy->rates) + policy->rates = default_lease_rates(policy); + + policy->rates->channel_fee_max_base_msat = amt.millisatoshis; /* Raw: conversion */ + + if (policy->rates->channel_fee_max_base_msat != amt.millisatoshis) /* Raw: comparison */ + return tal_fmt(NULL, "channel_fee_max_base_msat overflowed"); + + return NULL; +} + +static char * +option_channel_fee_proportional_thousandths_max(const char *arg, + struct funder_policy *policy) +{ + if (!policy->rates) + policy->rates = default_lease_rates(policy); + return u16_option(arg, &policy->rates->channel_fee_max_proportional_thousandths); +} + static char *amount_option(const char *arg, struct amount_sat *amt) { if (!parse_amount_sat(amt, arg, strlen(arg))) @@ -864,6 +1151,41 @@ static char *amount_option(const char *arg, struct amount_sat *amt) return NULL; } +static char *option_lease_fee_base(const char *arg, + struct funder_policy *policy) +{ + struct amount_sat amt; + char *err; + if (!policy->rates) + policy->rates = default_lease_rates(policy); + + err = amount_option(arg, &amt); + if (err) + return err; + + policy->rates->lease_fee_base_sat = amt.satoshis; /* Raw: conversion */ + if (policy->rates->lease_fee_base_sat != amt.satoshis) /* Raw: comparison */ + return tal_fmt(NULL, "lease_fee_base_sat overflowed"); + + return NULL; +} + +static char *option_lease_fee_basis(const char *arg, + struct funder_policy *policy) +{ + if (!policy->rates) + policy->rates = default_lease_rates(policy); + return u16_option(arg, &policy->rates->lease_fee_basis); +} + +static char *option_lease_weight_max(const char *arg, + struct funder_policy *policy) +{ + if (!policy->rates) + policy->rates = default_lease_rates(policy); + return u16_option(arg, &policy->rates->funding_weight); +} + static char *amount_sat_or_u64_option(const char *arg, u64 *amt) { struct amount_sat sats; @@ -890,7 +1212,7 @@ int main(int argc, char **argv) setup_locale(); /* Our default funding policy is fixed (0msat) */ - current_policy = default_funder_policy(FIXED, 0); + current_policy = default_funder_policy(owner, FIXED, 0); plugin_main(argv, init, PLUGIN_RESTARTABLE, true, NULL, @@ -902,57 +1224,93 @@ int main(int argc, char **argv) "string", "Policy to use for dual-funding requests." " [match, available, fixed]", - funding_option, ¤t_policy.opt), + funding_option, ¤t_policy->opt), plugin_option("funder-policy-mod", "string", "Percent to apply policy at" " (match/available); or amount to fund" " (fixed)", amount_sat_or_u64_option, - ¤t_policy.mod), + ¤t_policy->mod), plugin_option("funder-min-their-funding", "string", "Minimum funding peer must open with" " to activate our policy", amount_option, - ¤t_policy.min_their_funding), + ¤t_policy->min_their_funding), plugin_option("funder-max-their-funding", "string", "Maximum funding peer may open with" " to activate our policy", amount_option, - ¤t_policy.max_their_funding), + ¤t_policy->max_their_funding), plugin_option("funder-per-channel-min", "string", "Minimum funding we'll add to a channel." " If we can't meet this, we don't fund", amount_option, - ¤t_policy.per_channel_min), + ¤t_policy->per_channel_min), plugin_option("funder-per-channel-max", "string", "Maximum funding we'll add to a channel." " We cap all contributions to this", amount_option, - ¤t_policy.per_channel_max), + ¤t_policy->per_channel_max), plugin_option("funder-reserve-tank", "string", "Amount of funds we'll always leave" " available.", amount_option, - ¤t_policy.reserve_tank), + ¤t_policy->reserve_tank), plugin_option("funder-fuzz-percent", "int", "Percent to fuzz the policy contribution by." " Defaults to 5%. Max is 100%", u32_option, - ¤t_policy.fuzz_factor), + ¤t_policy->fuzz_factor), plugin_option("funder-fund-probability", "int", "Percent of requests to consider." " Defaults to 100%. Setting to 0% will" " disable dual-funding", u32_option, - ¤t_policy.fund_probability), + ¤t_policy->fund_probability), + plugin_option("funder-lease-requests-only", + "bool", + "Only fund lease requests. Defaults to" + " true if channel lease rates are" + " being advertised", + bool_option, + ¤t_policy->leases_only), + plugin_option("lease-fee-base-msat", + "string", + "Channel lease rates, base fee for leased" + " funds, in satoshi.", + option_lease_fee_base, current_policy), + plugin_option("lease-fee-basis", + "int", + "Channel lease rates, basis charged" + " for leased funds (per 10,000 satoshi.)", + option_lease_fee_basis, current_policy), + plugin_option("lease-funding-weight", + "int", + "Channel lease rates, weight" + " we'll ask opening peer to pay for in" + " funding transaction", + option_lease_weight_max, current_policy), + plugin_option("channel-fee-max-base-msat", + "string", + "Channel lease rates, maximum channel" + " fee base we'll charge for funds" + " routed through a leased channel.", + option_channel_base, current_policy), + plugin_option("channel-fee-max-proportional-thousandths", + "int", + "Channel lease rates, maximum" + " proportional fee (in thousandths, or ppt)" + " we'll charge for funds routed through a" + " leased channel. Note: 1ppt = 1,000ppm", + option_channel_fee_proportional_thousandths_max, current_policy), NULL); tal_free(owner); diff --git a/plugins/funder_policy.c b/plugins/funder_policy.c index 956fb15425df..1aefdfef5221 100644 --- a/plugins/funder_policy.c +++ b/plugins/funder_policy.c @@ -1,5 +1,7 @@ #include +#include #include +#include #include #include #include @@ -35,22 +37,23 @@ char *funding_option(const char *arg, enum funder_opt *opt) } const char *funder_policy_desc(const tal_t *ctx, - struct funder_policy policy) + const struct funder_policy *policy) { - if (policy.opt == FIXED) { - struct amount_sat amt = amount_sat(policy.mod); + if (policy->opt == FIXED) { + struct amount_sat amt = amount_sat(policy->mod); return tal_fmt(ctx, "%s (%s)", - funder_opt_name(policy.opt), + funder_opt_name(policy->opt), type_to_string(ctx, struct amount_sat, &amt)); } else return tal_fmt(ctx, "%s (%"PRIu64"%%)", - funder_opt_name(policy.opt), policy.mod); + funder_opt_name(policy->opt), policy->mod); /* FIXME: add in more info? */ } -struct funder_policy -new_funder_policy(enum funder_opt opt, +struct funder_policy * +new_funder_policy(const tal_t *ctx, + enum funder_opt opt, u64 policy_mod, struct amount_sat min_their_funding, struct amount_sat max_their_funding, @@ -58,35 +61,67 @@ new_funder_policy(enum funder_opt opt, struct amount_sat per_channel_max, u32 fuzz_factor, struct amount_sat reserve_tank, - u32 fund_probability) + u32 fund_probability, + bool leases_only, + struct lease_rates *rates) { - struct funder_policy policy; - - policy.opt = opt; - policy.mod = policy_mod; - policy.min_their_funding = min_their_funding; - policy.max_their_funding = max_their_funding; - policy.per_channel_min = per_channel_min; - policy.per_channel_max = per_channel_max; - policy.fuzz_factor = fuzz_factor; - policy.reserve_tank = reserve_tank; - policy.fund_probability = fund_probability; + struct funder_policy *policy = tal(ctx, struct funder_policy); + + policy->opt = opt; + policy->mod = policy_mod; + policy->min_their_funding = min_their_funding; + policy->max_their_funding = max_their_funding; + policy->per_channel_min = per_channel_min; + policy->per_channel_max = per_channel_max; + policy->fuzz_factor = fuzz_factor; + policy->reserve_tank = reserve_tank; + policy->fund_probability = fund_probability; + policy->leases_only = leases_only; + policy->rates = rates; return policy; } -struct funder_policy -default_funder_policy(enum funder_opt policy, +struct funder_policy * +default_funder_policy(const tal_t *ctx, + enum funder_opt policy, u64 policy_mod) { - return new_funder_policy(policy, policy_mod, + return new_funder_policy(ctx, policy, policy_mod, AMOUNT_SAT(10000), AMOUNT_SAT(UINT_MAX), AMOUNT_SAT(10000), AMOUNT_SAT(UINT_MAX), - 5, /* fuzz_factor */ + 0, /* fuzz_factor */ AMOUNT_SAT(0), /* reserve_tank */ - 100); + 100, + /* Defaults to true iif we're advertising + * offers */ + false, + NULL); +} + +struct lease_rates * +default_lease_rates(const tal_t *ctx) +{ + struct lease_rates *rates = tal(ctx, struct lease_rates); + + /* Default basis is .65%, (7.8% APR) */ + rates->lease_fee_basis = 65; + /* 2000sat base rate */ + rates->lease_fee_base_sat = 2000; + /* Max of 100,000ppm (10%) */ + rates->channel_fee_max_proportional_thousandths = 100; + /* Max of 5000sat */ + rates->channel_fee_max_base_msat = 5000000; + + /* Let's set our default max weight to two inputs + an output + * (use helpers b/c elements) */ + rates->funding_weight + = 2 * bitcoin_tx_simple_input_weight(false) + + bitcoin_tx_output_weight(BITCOIN_SCRIPTPUBKEY_P2WPKH_LEN); + + return rates; } char *funder_check_policy(const struct funder_policy *policy) @@ -137,47 +172,63 @@ apply_fuzz(u32 fuzz_factor, struct amount_sat val) } static struct amount_sat -apply_policy(struct funder_policy policy, +apply_policy(struct funder_policy *policy, struct amount_sat their_funding, + struct amount_sat requested_lease, struct amount_sat available_funds) { struct amount_sat our_funding; - switch (policy.opt) { + switch (policy->opt) { case MATCH: + /* For matches, we use requested funding, if availalbe */ + if (!amount_sat_zero(requested_lease)) + their_funding = requested_lease; + /* if this fails, it implies ludicrous funding offer, *and* * > 100% match. Just Say No, kids. */ if (!amount_sat_scale(&our_funding, their_funding, - policy.mod / 100.0)) + policy->mod / 100.0)) our_funding = AMOUNT_SAT(0); return our_funding; case AVAILABLE: /* Use the 'available_funds' as the starting * point for your contribution */ if (!amount_sat_scale(&our_funding, available_funds, - policy.mod / 100.0)) + policy->mod / 100.0)) abort(); return our_funding; case FIXED: /* Use a static amount */ - return amount_sat(policy.mod); + return amount_sat(policy->mod); } abort(); } const char * -calculate_our_funding(struct funder_policy policy, +calculate_our_funding(struct funder_policy *policy, struct node_id id, struct amount_sat their_funding, struct amount_sat available_funds, struct amount_sat channel_max, + struct amount_sat requested_lease, struct amount_sat *our_funding) { struct amount_sat avail_channel_space, net_available_funds; + /* Are we only funding lease requests ? */ + if (policy->leases_only && amount_sat_zero(requested_lease)) { + *our_funding = AMOUNT_SAT(0); + return tal_fmt(tmpctx, + "Skipping funding open; leases-only=true" + " and this open isn't asking for a lease"); + } + /* Are we skipping this one? */ - if (pseudorand(100) >= policy.fund_probability) { + if (pseudorand(100) >= policy->fund_probability + /* We don't skip lease requests */ + && amount_sat_zero(requested_lease)) { *our_funding = AMOUNT_SAT(0); return tal_fmt(tmpctx, "Skipping, failed fund_probability test"); @@ -185,7 +236,7 @@ calculate_our_funding(struct funder_policy policy, /* Figure out amount of actual headroom we have */ if (!amount_sat_sub(&avail_channel_space, channel_max, their_funding) - || amount_sat_eq(avail_channel_space, AMOUNT_SAT(0))) { + || amount_sat_zero(avail_channel_space)) { *our_funding = AMOUNT_SAT(0); return tal_fmt(tmpctx, "No space available in channel." " channel_max %s, their_funding %s", @@ -198,19 +249,19 @@ calculate_our_funding(struct funder_policy policy, /* Figure out actual available funds, given our requested * 'reserve_tank' */ if (!amount_sat_sub(&net_available_funds, available_funds, - policy.reserve_tank) - || amount_sat_eq(net_available_funds, AMOUNT_SAT(0))) { + policy->reserve_tank) + || amount_sat_zero(net_available_funds)) { *our_funding = AMOUNT_SAT(0); return tal_fmt(tmpctx, "Reserve tank too low." " available_funds %s, reserve_tank requires %s", type_to_string(tmpctx, struct amount_sat, &available_funds), type_to_string(tmpctx, struct amount_sat, - &policy.reserve_tank)); + &policy->reserve_tank)); } /* Are they funding enough ? */ - if (amount_sat_less(their_funding, policy.min_their_funding)) { + if (amount_sat_less(their_funding, policy->min_their_funding)) { *our_funding = AMOUNT_SAT(0); return tal_fmt(tmpctx, "Peer's funding too little." " their_funding %s," @@ -218,11 +269,11 @@ calculate_our_funding(struct funder_policy policy, type_to_string(tmpctx, struct amount_sat, &their_funding), type_to_string(tmpctx, struct amount_sat, - &policy.min_their_funding)); + &policy->min_their_funding)); } /* Are they funding too much ? */ - if (amount_sat_greater(their_funding, policy.max_their_funding)) { + if (amount_sat_greater(their_funding, policy->max_their_funding)) { *our_funding = AMOUNT_SAT(0); return tal_fmt(tmpctx, "Peer's funding too much." " their_funding %s," @@ -230,18 +281,21 @@ calculate_our_funding(struct funder_policy policy, type_to_string(tmpctx, struct amount_sat, &their_funding), type_to_string(tmpctx, struct amount_sat, - &policy.max_their_funding)); + &policy->max_their_funding)); } /* What's our amount, given our policy */ - *our_funding = apply_policy(policy, their_funding, available_funds); + *our_funding = apply_policy(policy, + their_funding, + requested_lease, + available_funds); /* Don't return an 'error' if we're already at 0 */ - if (amount_sat_eq(*our_funding, AMOUNT_SAT(0))) + if (amount_sat_zero(*our_funding)) return NULL; /* our_funding is probably sane, so let's fuzz this amount a bit */ - *our_funding = apply_fuzz(policy.fuzz_factor, *our_funding); + *our_funding = apply_fuzz(policy->fuzz_factor, *our_funding); /* Is our_funding more than we can fit? if so set to avail space */ if (amount_sat_greater(*our_funding, avail_channel_space)) @@ -249,8 +303,8 @@ calculate_our_funding(struct funder_policy policy, /* Is our_funding more than we want to fund in a channel? * if so set at our desired per-channel max */ - if (amount_sat_greater(*our_funding, policy.per_channel_max)) - *our_funding = policy.per_channel_max; + if (amount_sat_greater(*our_funding, policy->per_channel_max)) + *our_funding = policy->per_channel_max; /* Is our_funding more than we have available? if so * set to max available */ @@ -259,7 +313,7 @@ calculate_our_funding(struct funder_policy policy, /* Is our_funding less than our per-channel minimum? * if so, don't fund */ - if (amount_sat_less(*our_funding, policy.per_channel_min)) { + if (amount_sat_less(*our_funding, policy->per_channel_min)) { *our_funding = AMOUNT_SAT(0); return tal_fmt(tmpctx, "Can't meet our min channel requirement." " our_funding %s," @@ -267,7 +321,7 @@ calculate_our_funding(struct funder_policy policy, type_to_string(tmpctx, struct amount_sat, our_funding), type_to_string(tmpctx, struct amount_sat, - &policy.per_channel_min)); + &policy->per_channel_min)); } return NULL; diff --git a/plugins/funder_policy.h b/plugins/funder_policy.h index 9c7bd9ef6232..f554bdbe5104 100644 --- a/plugins/funder_policy.h +++ b/plugins/funder_policy.h @@ -2,12 +2,14 @@ #define LIGHTNING_PLUGINS_FUNDER_POLICY_H #include "config.h" #include +#include +struct lease_rates; struct node_id; /* Policy Options */ enum funder_opt { - /* Use their_funding as the starting + /* Use their_funding/requested_amt as the starting * point for your contribution */ MATCH, @@ -53,10 +55,18 @@ struct funder_policy { /* Percent of open offers we'll consider funding. */ u32 fund_probability; + + /* If we're advertising liquidity, only + * provide funds for lease requests */ + bool leases_only; + + /* Rates we're currently charging for channel leases */ + struct lease_rates *rates; }; -struct funder_policy -new_funder_policy(enum funder_opt opt, +struct funder_policy * +new_funder_policy(const tal_t *ctx, + enum funder_opt opt, u64 policy_mod, struct amount_sat min_their_funding, struct amount_sat max_their_funding, @@ -64,21 +74,29 @@ new_funder_policy(enum funder_opt opt, struct amount_sat per_channel_max, u32 fuzz_factor, struct amount_sat reserve_tank, - u32 fund_probability); + u32 fund_probability, + bool leases_only, + struct lease_rates *rates); /* Get a new funder_policy, set to the defaults */ -struct funder_policy -default_funder_policy(enum funder_opt policy, +struct funder_policy * +default_funder_policy(const tal_t *ctx, + enum funder_opt policy, u64 policy_mod); +/* Defaults to use for the lease_rates */ +struct lease_rates * +default_lease_rates(const tal_t *ctx); + /* Given the policy and this request's details, figure * out how much we should contribute to this channel */ const char * -calculate_our_funding(struct funder_policy policy, +calculate_our_funding(struct funder_policy *policy, struct node_id id, struct amount_sat their_funding, struct amount_sat available_funds, struct amount_sat channel_max, + struct amount_sat lease_request, struct amount_sat *our_funding); /* Get the name of this policy option */ @@ -86,7 +104,7 @@ const char *funder_opt_name(enum funder_opt opt); /* Get a (short, for now) description of the provided policy */ const char *funder_policy_desc(const tal_t *ctx, - const struct funder_policy policy); + const struct funder_policy *policy); /* Convert a cmdline option to a funding_opt */ char *funding_option(const char *arg, enum funder_opt *opt); diff --git a/plugins/libplugin.c b/plugins/libplugin.c index f28f5be722ed..b5a87d6e7e5a 100644 --- a/plugins/libplugin.c +++ b/plugins/libplugin.c @@ -484,11 +484,12 @@ static struct json_out *start_json_request(const tal_t *ctx, } /* Synchronous routine to send command and extract fields from response */ -void rpc_scan(struct plugin *plugin, - const char *method, - const struct json_out *params TAKES, - const char *guide, - ...) +static const char *rpc_scan_list(const tal_t *ctx, + struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + va_list *ap) { bool error; const char *err; @@ -496,26 +497,59 @@ void rpc_scan(struct plugin *plugin, int reqlen; const char *p; struct json_out *jout; - va_list ap; jout = start_json_request(tmpctx, 0, method, params); finish_and_send_json(plugin->rpc_conn->fd, jout); read_rpc_reply(tmpctx, plugin, &contents, &error, &reqlen); if (error) - plugin_err(plugin, "Got error reply to %s: '%.*s'", - method, reqlen, membuf_elems(&plugin->rpc_conn->mb)); + return tal_fmt(ctx,"Got error reply to %s: '%.*s'", + method, reqlen, + membuf_elems(&plugin->rpc_conn->mb)); p = membuf_consume(&plugin->rpc_conn->mb, reqlen); + err = json_scanv(tmpctx, p, contents, guide, *ap); + if (err) + return tal_fmt(ctx, + "Could not parse %s in reply to %s: %s: '%.*s'", + guide, method, err, reqlen, + membuf_elems(&plugin->rpc_conn->mb)); + return NULL; +} + +const char *rpc_scan_err(const tal_t *ctx, + struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + ...) +{ + va_list ap; + const char *err; + + va_start(ap, guide); + err = rpc_scan_list(ctx, plugin, method, params, guide, &ap); + va_end(ap); + + return err; +} + +void rpc_scan(struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + ...) +{ + va_list ap; + const char *err; + va_start(ap, guide); - err = json_scanv(tmpctx, p, contents, guide, ap); + err = rpc_scan_list(plugin, plugin, method, params, guide, &ap); va_end(ap); if (err) - plugin_err(plugin, "Could not parse %s in reply to %s: %s: '%.*s'", - guide, method, err, - reqlen, membuf_elems(&plugin->rpc_conn->mb)); + plugin_err(plugin, err); } static void handle_rpc_reply(struct plugin *plugin, const jsmntok_t *toks) @@ -925,6 +959,25 @@ char *u32_option(const char *arg, u32 *i) return NULL; } +char *u16_option(const char *arg, u16 *i) +{ + char *endp; + u64 n; + + errno = 0; + n = strtoul(arg, &endp, 0); + if (*endp || !arg[0]) + return tal_fmt(NULL, "'%s' is not a number", arg); + if (errno) + return tal_fmt(NULL, "'%s' is out of range", arg); + + *i = n; + if (*i != n) + return tal_fmt(NULL, "'%s' is too large (overflow)", arg); + + return NULL; +} + char *bool_option(const char *arg, bool *i) { if (!streq(arg, "true") && !streq(arg, "false")) diff --git a/plugins/libplugin.h b/plugins/libplugin.h index 6ae600fd9b90..d4dd02790518 100644 --- a/plugins/libplugin.h +++ b/plugins/libplugin.h @@ -193,6 +193,16 @@ void rpc_scan(struct plugin *plugin, const char *guide, ...); +/* Synchronous helper to send command and extract fields from + * response. Returns an error message if fails. + **/ +const char *rpc_scan_err(const tal_t *ctx, + struct plugin *plugin, + const char *method, + const struct json_out *params TAKES, + const char *guide, + ...); + /* Send an async rpc request to lightningd. */ struct command_result *send_outreq(struct plugin *plugin, const struct out_req *req); @@ -277,6 +287,7 @@ void plugin_notify_progress(struct command *cmd, /* Standard helpers */ char *u64_option(const char *arg, u64 *i); char *u32_option(const char *arg, u32 *i); +char *u16_option(const char *arg, u16 *i); char *bool_option(const char *arg, bool *i); char *charp_option(const char *arg, char **p); char *flag_option(const char *arg, bool *i); diff --git a/plugins/spender/fundchannel.c b/plugins/spender/fundchannel.c index 39cdae088e81..1423b191c805 100644 --- a/plugins/spender/fundchannel.c +++ b/plugins/spender/fundchannel.c @@ -42,6 +42,8 @@ json_fundchannel(struct command *cmd, const jsmntok_t *utxos; const jsmntok_t *push_msat; const jsmntok_t *close_to; + const jsmntok_t *request_amt; + const jsmntok_t *compact_lease; struct out_req *req; @@ -54,9 +56,16 @@ json_fundchannel(struct command *cmd, p_opt("utxos", param_tok, &utxos), p_opt("push_msat", param_tok, &push_msat), p_opt("close_to", param_tok, &close_to), + p_opt("request_amt", param_tok, &request_amt), + p_opt("compact_lease", param_tok, &compact_lease), NULL)) return command_param_failed(); + if (request_amt && !compact_lease) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "Must pass in 'compact_lease' if requesting" + " funds from peer"); + req = jsonrpc_request_start(cmd->plugin, cmd, "multifundchannel", &fundchannel_get_result, &forward_error, NULL); @@ -71,6 +80,10 @@ json_fundchannel(struct command *cmd, json_add_tok(req->js, "push_msat", push_msat, buf); if (close_to) json_add_tok(req->js, "close_to", close_to, buf); + if (request_amt) { + json_add_tok(req->js, "request_amt", request_amt, buf); + json_add_tok(req->js, "compact_lease", compact_lease, buf); + } json_object_end(req->js); json_array_end(req->js); if (feerate) diff --git a/plugins/spender/multifundchannel.c b/plugins/spender/multifundchannel.c index 72fbe4e8cacf..5cce683f0966 100644 --- a/plugins/spender/multifundchannel.c +++ b/plugins/spender/multifundchannel.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -1374,9 +1376,8 @@ after_fundpsbt(struct command *cmd, } - static struct command_result * -perform_fundpsbt(struct multifundchannel_command *mfc) +perform_fundpsbt(struct multifundchannel_command *mfc, u32 feerate) { struct out_req *req; @@ -1423,11 +1424,34 @@ perform_fundpsbt(struct multifundchannel_command *mfc) else { struct amount_sat sum = AMOUNT_SAT(0); for (size_t i = 0; i < tal_count(mfc->destinations); ++i) { + struct amount_sat requested + = mfc->destinations[i].request_amt; + if (!amount_sat_add(&sum, sum, mfc->destinations[i].amount)) return mfc_fail(mfc, JSONRPC2_INVALID_PARAMS, "Overflow while summing " "destination values."); + /* Also add in any fees for requested amt! */ + if (!amount_sat_zero(requested)) { + struct amount_sat fee; + + /* Assume they send >= what we've + * requested (otherwise we error) */ + if (!lease_rates_calc_fee(mfc->destinations[i].rates, + requested, requested, + feerate, + &fee)) + return mfc_fail(mfc, JSONRPC2_INVALID_PARAMS, + "Overflow calculating" + " lease fee."); + + + if (!amount_sat_add(&sum, sum, fee)) + return mfc_fail(mfc, JSONRPC2_INVALID_PARAMS, + "Overflow while summing" + " lease fee"); + } } json_add_string(req->js, "satoshi", type_to_string(tmpctx, struct amount_sat, @@ -1465,6 +1489,54 @@ perform_fundpsbt(struct multifundchannel_command *mfc) return send_outreq(mfc->cmd->plugin, req); } +static struct command_result * +after_getfeerate(struct command *cmd, + const char *buf, + const jsmntok_t *result, + struct multifundchannel_command *mfc) +{ + const char *err; + u32 feerate; + + plugin_log(mfc->cmd->plugin, LOG_DBG, + "mfc %"PRIu64": 'parsefeerate' done", mfc->id); + + err = json_scan(tmpctx, buf, result, + "{perkw:%}", + JSON_SCAN(json_to_number, &feerate)); + if (err) + mfc_fail(mfc, JSONRPC2_INVALID_PARAMS, + "Unable to parse feerate %s: %*.s", + err, json_tok_full_len(result), + json_tok_full(buf, result)); + + return perform_fundpsbt(mfc, feerate); +} + +static struct command_result * +getfeerate(struct multifundchannel_command *mfc) +{ + struct out_req *req; + /* With the introduction of channel leases (option_will_fund), + * we need to include enough in the PSBT to cover our expected + * fees for the channel open. This requires that we know + * the feerate ahead of time, so that we can figure the + * expected lease fees, and add that to the funding amount. */ + req = jsonrpc_request_start(mfc->cmd->plugin, + mfc->cmd, + "parsefeerate", + &after_getfeerate, + &mfc_forward_error, + mfc); + + /* Internally, it defaults to 'opening', so we use that here */ + if (!mfc->feerate_str) + mfc->feerate_str = "opening"; + json_add_string(req->js, "feerate", mfc->feerate_str); + + return send_outreq(mfc->cmd->plugin, req); +} + /*---------------------------------------------------------------------------*/ /*~ First, connect to all the peers. @@ -1509,7 +1581,7 @@ after_multiconnect(struct multifundchannel_command *mfc) dest->error_message); } - return perform_fundpsbt(mfc); + return getfeerate(mfc); } static struct command_result * @@ -1813,9 +1885,10 @@ param_destinations_array(struct command *cmd, const char *name, struct multifundchannel_destination *dest; const char *id; char *addrhint; - struct amount_sat *amount; + struct amount_sat *amount, *request_amt; bool *announce; struct amount_msat *push_msat; + struct lease_rates *rates; dest = &(*dests)[i]; @@ -1830,6 +1903,9 @@ param_destinations_array(struct command *cmd, const char *name, * passed in to fundchannel_start) if invalid*/ p_opt("close_to", param_string, &dest->close_to_str), + p_opt_def("request_amt", param_sat, &request_amt, + AMOUNT_SAT(0)), + p_opt("compact_lease", param_lease_hex, &rates), NULL)) return command_param_failed(); @@ -1852,6 +1928,12 @@ param_destinations_array(struct command *cmd, const char *name, json_dest, "output would be dust"); + if (!amount_sat_zero(*request_amt) && !rates) + return command_fail_badparam(cmd, name, buffer, + json_dest, + "Must pass in 'compact_" + "lease' if requesting" + " funds from peer"); dest->index = i; dest->addrhint = addrhint; dest->their_features = NULL; @@ -1864,6 +1946,8 @@ param_destinations_array(struct command *cmd, const char *name, dest->psbt = NULL; dest->updated_psbt = NULL; dest->protocol = FUND_CHANNEL; + dest->request_amt = *request_amt; + dest->rates = tal_steal(*dests, rates); /* Only one destination can have "all" indicator. */ if (dest->all) { diff --git a/plugins/spender/multifundchannel.h b/plugins/spender/multifundchannel.h index 1519ccfa105c..e9cfe5d46633 100644 --- a/plugins/spender/multifundchannel.h +++ b/plugins/spender/multifundchannel.h @@ -143,6 +143,12 @@ struct multifundchannel_destination { /* serial of the funding output for this channel (OPEN_CHANNEL) */ u64 funding_serial; + + /* amount to request peer to lease (OPEN_CHANNEL) */ + struct amount_sat request_amt; + + /* Channel lease rates that we expect the peer to respond with */ + struct lease_rates *rates; }; diff --git a/plugins/spender/openchannel.c b/plugins/spender/openchannel.c index 43deb9beed10..22cbb171f086 100644 --- a/plugins/spender/openchannel.c +++ b/plugins/spender/openchannel.c @@ -1025,6 +1025,14 @@ openchannel_init_dest(struct multifundchannel_destination *dest) type_to_string(tmpctx, struct amount_msat, &dest->push_msat)); + /* Request some sats from the peer! */ + if (!amount_sat_zero(dest->request_amt)) { + json_add_string(req->js, "request_amt", + fmt_amount_sat(tmpctx, dest->request_amt)); + json_add_string(req->js, "compact_lease", + lease_rates_tohex(tmpctx, dest->rates)); + } + return send_outreq(cmd->plugin, req); } diff --git a/plugins/test/run-funder_policy.c b/plugins/test/run-funder_policy.c index b397f70a1dc0..09ca6a3a9ee8 100644 --- a/plugins/test/run-funder_policy.c +++ b/plugins/test/run-funder_policy.c @@ -33,6 +33,7 @@ struct test_case { struct amount_sat their_funds; struct amount_sat available_funds; struct amount_sat channel_max; + struct amount_sat lease_request; struct funder_policy policy; @@ -46,6 +47,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(100000), .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 1111, @@ -56,6 +58,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(1111), @@ -66,6 +69,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(500), .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = MATCH, .mod = 0, @@ -76,6 +80,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(0), @@ -86,6 +91,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(6000), .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = MATCH, .mod = 100, @@ -96,6 +102,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(5000), @@ -106,6 +113,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(2500), .available_funds = AMOUNT_SAT(6000), .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = MATCH, .mod = 200, @@ -116,6 +124,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(5000), @@ -126,6 +135,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(2500), .available_funds = AMOUNT_SAT(5000), .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = AVAILABLE, .mod = 0, @@ -136,6 +146,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(0), @@ -146,6 +157,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(2500), .available_funds = AMOUNT_SAT(3000), .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = AVAILABLE, .mod = 50, @@ -156,6 +168,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(1500), @@ -166,6 +179,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(2500), .available_funds = AMOUNT_SAT(5000), .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = AVAILABLE, .mod = 100, @@ -176,6 +190,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(5000), @@ -198,6 +213,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(900), @@ -208,6 +224,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(5000), .channel_max = AMOUNT_SAT(5500), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 1002, @@ -218,6 +235,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(500), @@ -228,6 +246,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(500), .channel_max = AMOUNT_SAT(10000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 1001, @@ -238,6 +257,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(500), @@ -248,6 +268,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(1000), .channel_max = AMOUNT_SAT(10000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 999, @@ -258,6 +279,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(0), @@ -268,6 +290,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5001), .available_funds = AMOUNT_SAT(5000), .channel_max = AMOUNT_SAT(10000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 998, @@ -278,6 +301,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(0), @@ -288,6 +312,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(1000), .channel_max = AMOUNT_SAT(10000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 997, @@ -298,6 +323,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(100), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(900), @@ -308,6 +334,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(999), .channel_max = AMOUNT_SAT(10000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 996, @@ -318,6 +345,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(1000), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(0), @@ -328,6 +356,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(5000), .channel_max = AMOUNT_SAT(5000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 995, @@ -338,6 +367,7 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(1000), .fund_probability = 100, + .leases_only = false, }, .exp_our_funds = AMOUNT_SAT(0), @@ -348,6 +378,7 @@ struct test_case cases[] = { .their_funds = AMOUNT_SAT(5000), .available_funds = AMOUNT_SAT(5000), .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), .policy = { .opt = FIXED, .mod = 988, @@ -358,6 +389,51 @@ struct test_case cases[] = { .fuzz_factor = 0, .reserve_tank = AMOUNT_SAT(0), .fund_probability = 100, + .leases_only = false, + }, + + .exp_our_funds = AMOUNT_SAT(0), + .expect_err = true, + }, + /* By default, use lease request as ceiling */ + { + .their_funds = AMOUNT_SAT(5000), + .available_funds = AMOUNT_SAT(100000), + .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(980), + .policy = { + .opt = MATCH, + .mod = 100, + .min_their_funding = AMOUNT_SAT(0), + .max_their_funding = AMOUNT_SAT(10000), + .per_channel_max = AMOUNT_SAT(100000), + .per_channel_min = AMOUNT_SAT(0), + .fuzz_factor = 0, + .reserve_tank = AMOUNT_SAT(0), + .fund_probability = 100, + .leases_only = false, + }, + + .exp_our_funds = AMOUNT_SAT(980), + .expect_err = false, + }, + /* Only fund lease requests */ + { + .their_funds = AMOUNT_SAT(5000), + .available_funds = AMOUNT_SAT(100000), + .channel_max = AMOUNT_SAT(11000), + .lease_request = AMOUNT_SAT(0), + .policy = { + .opt = FIXED, + .mod = 985, + .min_their_funding = AMOUNT_SAT(0), + .max_their_funding = AMOUNT_SAT(10000), + .per_channel_max = AMOUNT_SAT(100000), + .per_channel_min = AMOUNT_SAT(0), + .fuzz_factor = 0, + .reserve_tank = AMOUNT_SAT(0), + .fund_probability = 100, + .leases_only = true, }, .exp_our_funds = AMOUNT_SAT(0), @@ -376,10 +452,11 @@ static void check_fuzzing(struct test_case fuzzcase) memset(&id, 2, sizeof(struct node_id)); for (size_t i = 0; i < 100; i++) { - calculate_our_funding(fuzzcase.policy, id, + calculate_our_funding(&fuzzcase.policy, id, fuzzcase.their_funds, fuzzcase.available_funds, fuzzcase.channel_max, + fuzzcase.lease_request, &our_funds); if (amount_sat_greater(our_funds, fuzz_max)) fuzz_max = our_funds; @@ -393,7 +470,7 @@ static void check_fuzzing(struct test_case fuzzcase) int main(int argc, const char *argv[]) { - struct funder_policy policy; + struct funder_policy *policy; struct node_id id; struct amount_sat empty = AMOUNT_SAT(0), our_funds; bool ok = true; @@ -406,25 +483,27 @@ int main(int argc, const char *argv[]) memset(&id, 2, sizeof(struct node_id)); /* Check the default funder policy, at fixed (0msat) */ - policy = default_funder_policy(FIXED, 0); + policy = default_funder_policy(tmpctx, FIXED, 0); err = calculate_our_funding(policy, id, AMOUNT_SAT(50000), AMOUNT_SAT(50000), AMOUNT_SAT(100000), + AMOUNT_SAT(0), &our_funds); assert(amount_sat_eq(empty, our_funds)); assert(!err); for (i = 0; i < ARRAY_SIZE(cases); i++) { - err = calculate_our_funding(cases[i].policy, id, + err = calculate_our_funding(&cases[i].policy, id, cases[i].their_funds, cases[i].available_funds, cases[i].channel_max, + cases[i].lease_request, &our_funds); if (!amount_sat_eq(cases[i].exp_our_funds, our_funds)) { fprintf(stderr, "FAIL policy: %s. expected %s, got %s\n", - funder_policy_desc(NULL, cases[i].policy), + funder_policy_desc(NULL, &cases[i].policy), type_to_string(NULL, struct amount_sat, &cases[i].exp_our_funds), type_to_string(NULL, struct amount_sat, @@ -434,7 +513,7 @@ int main(int argc, const char *argv[]) if (cases[i].expect_err != (err != NULL)) { fprintf(stderr, "FAIL policy: %s. expected %serr," " got %s\n", - funder_policy_desc(NULL, cases[i].policy), + funder_policy_desc(NULL, &cases[i].policy), cases[i].expect_err ? "" : "no ", err ? err : "no err"); ok = false; @@ -450,10 +529,11 @@ int main(int argc, const char *argv[]) flipcase.policy.fund_probability = flips; for (i = 0; i < 100 * flips; i++) { - calculate_our_funding(flipcase.policy, id, + calculate_our_funding(&flipcase.policy, id, flipcase.their_funds, flipcase.available_funds, flipcase.channel_max, + flipcase.lease_request, &our_funds); if (!amount_sat_eq(our_funds, AMOUNT_SAT(0))) flipcount++; diff --git a/tests/fuzz/fuzz-initial_channel.c b/tests/fuzz/fuzz-initial_channel.c index c2e690e58b59..06e1e89bdc20 100644 --- a/tests/fuzz/fuzz-initial_channel.c +++ b/tests/fuzz/fuzz-initial_channel.c @@ -34,7 +34,7 @@ void run(const uint8_t *data, size_t size) u32 funding_txout, minimum_depth; struct amount_sat funding, max; struct amount_msat local_msatoshi; - u32 feerate_per_kw; + u32 feerate_per_kw, blockheight, lease_expiry; struct channel_config local, remote; struct basepoints local_basepoints, remote_basepoints; struct pubkey local_funding_pubkey, remote_funding_pubkey; @@ -51,6 +51,8 @@ void run(const uint8_t *data, size_t size) if (amount_sat_greater(funding, max)) funding = max; feerate_per_kw = fromwire_u32(&data, &size); + blockheight = fromwire_u32(&data, &size); + lease_expiry = fromwire_u32(&data, &size); fromwire_channel_config(&data, &size, &local); fromwire_channel_config(&data, &size, &remote); fromwire_basepoints(&data, &size, &local_basepoints); @@ -66,12 +68,20 @@ void run(const uint8_t *data, size_t size) return; for (enum side opener = 0; opener < NUM_SIDES; opener++) { - channel = new_initial_channel(tmpctx, &cid, &funding_txid, funding_txout, - minimum_depth, funding, local_msatoshi, - take(new_fee_states(NULL, opener, &feerate_per_kw)), - &local, &remote, &local_basepoints, - &remote_basepoints, &local_funding_pubkey, - &remote_funding_pubkey, option_static_remotekey, + channel = new_initial_channel(tmpctx, &cid, &funding_txid, + funding_txout, minimum_depth, + take(new_height_states(NULL, opener, + &blockheight)), + lease_expiry, + funding, local_msatoshi, + take(new_fee_states(NULL, opener, + &feerate_per_kw)), + &local, &remote, + &local_basepoints, + &remote_basepoints, + &local_funding_pubkey, + &remote_funding_pubkey, + option_static_remotekey, option_anchor_outputs, opener); /* TODO: make initial_channel_tx() work with ASAN.. */ diff --git a/tests/test_closing.py b/tests/test_closing.py index cd95224c2036..20303e520a71 100644 --- a/tests/test_closing.py +++ b/tests/test_closing.py @@ -718,6 +718,353 @@ def test_penalty_outhtlc(node_factory, bitcoind, executor, chainparams): assert account_balance(l2, channel_id) == 0 +# check that the fee paid is correct +def calc_lease_fee(amt, feerate, rates): + fee = rates['lease_fee_base_msat'] + fee += amt * rates['lease_fee_basis'] // 10 + fee += rates['funding_weight'] * feerate + return fee + + +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.openchannel('v2') +@pytest.mark.slow_test +@pytest.mark.developer("requres 'dev-queryrates'") +def test_channel_lease_falls_behind(node_factory, bitcoind): + ''' + If our peer falls too far behind/doesn't send us an update for + their blockheight, the lessor fails the channel + ''' + opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100}, + {'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100}] + l1, l2, = node_factory.get_nodes(2, opts=opts) + amount = 500000 + feerate = 2000 + l1.fundwallet(20000000) + l2.fundwallet(20000000) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) + l1.daemon.wait_for_log('disconnect') + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # l1 leases a channel from l2 + l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease=rates['compact_lease']) + + # sink the funding transaction + bitcoind.generate_block(1) + + # stop l1 + l1.stop() + + # advance blockchain 1008 blocks, the lessor should drop to chain + bitcoind.generate_block(1008) + sync_blockheight(bitcoind, [l2]) + + l2.daemon.wait_for_log('Offline peer is too far behind, terminating') + + +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.openchannel('v2') +@pytest.mark.slow_test +@pytest.mark.developer("requres 'dev-queryrates'") +def test_channel_lease_closes(node_factory, bitcoind): + ''' + Check that channel leases work + + l1-l2: l1 leases funds from l2; l1 goes to chain unilaterally + l2-l3: l2 leases funds from l3; l3 goes to chain unilaterally + l4-l2: l4 leases funds from l2; mutual close at the end of the lease + ''' + + l1, l2, l3, l4 = node_factory.get_nodes(4, opts={'funder-policy': 'match', + 'funder-policy-mod': 100, + 'lease-fee-base-msat': '100sat', + 'lease-fee-basis': 100, + 'may_reconnect': True}) + + # Allow l2 some warnings + l2.allow_warning = True + + feerate = 2000 + amount = 500000 + l1.fundwallet(20000000) + l2.fundwallet(20000000) + l3.fundwallet(20000000) + l4.fundwallet(20000000) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) + l1.daemon.wait_for_log('disconnect') + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # l1 leases a channel from l2 + l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease=rates['compact_lease']) + + # free up some funds for l2 to fund a channel with l4 + bitcoind.generate_block(1, wait_for_mempool=1) + sync_blockheight(bitcoind, [l2]) + + # l4 leases a channel from l2 + l4.rpc.connect(l2.info['id'], 'localhost', l2.port) + # l4 leases a channel to l2 + l4.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease=rates['compact_lease']) + + # l2 leases a channel from l3 + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + rates = l2.rpc.dev_queryrates(l3.info['id'], amount, amount) + l3.daemon.wait_for_log('disconnect') + l2.rpc.connect(l3.info['id'], 'localhost', l3.port) + l2.rpc.fundchannel(l3.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), minconf=0, + compact_lease=rates['compact_lease']) + + est_fees = calc_lease_fee(amount, feerate, rates) + + # This should be the accepter's amount + fundings = only_one(only_one(l1.rpc.listpeers()['peers'])['channels'])['funding_msat'] + assert Millisatoshi(est_fees + amount * 1000) == Millisatoshi(fundings[l2.info['id']]) + + bitcoind.generate_block(6) + l1.daemon.wait_for_log('to CHANNELD_NORMAL') + l3.daemon.wait_for_log('to CHANNELD_NORMAL') + l4.daemon.wait_for_log('to CHANNELD_NORMAL') + + wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(l1.get_channel_scid(l2))['channels']] == [True, True]) + wait_for(lambda: [c['active'] for c in l3.rpc.listchannels(l3.get_channel_scid(l2))['channels']] == [True, True]) + wait_for(lambda: [c['active'] for c in l4.rpc.listchannels(l4.get_channel_scid(l2))['channels']] == [True, True]) + + # send some payments, mine a block or two + inv = l2.rpc.invoice(10**4, '1', 'no_1') + l1.rpc.pay(inv['bolt11']) + inv = l2.rpc.invoice(10**4, '3', 'no_3') + l3.rpc.pay(inv['bolt11']) + inv = l2.rpc.invoice(10**4, '4', 'no_4') + l4.rpc.pay(inv['bolt11']) + + # l2 attempts to close a channel that it leased, should fail + with pytest.raises(RpcError, match=r'Peer leased this channel from us'): + l2.rpc.close(l4.get_channel_scid(l2)) + + bitcoind.generate_block(6) + sync_blockheight(bitcoind, [l1, l2, l3, l4]) + # make sure we're at the right place for the csv lock + l2.daemon.wait_for_log('Blockheight: SENT_ADD_ACK_COMMIT->RCVD_ADD_ACK_REVOCATION LOCAL now 112') + l2.stop() + + # unilateral close channels l1<->l2 & l3<->l2 + l1.rpc.close(l2.info['id'], 1) + l3.rpc.close(l2.info['id'], 1, force_lease_closed=True) + + # Wait til to_self_delay expires, l1 should claim to_local back + bitcoind.generate_block(10, wait_for_mempool=1) + l1.daemon.wait_for_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') + bitcoind.generate_block(1, wait_for_mempool=1) + l1.daemon.wait_for_log('Resolved OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by our proposal OUR_DELAYED_RETURN_TO_WALLET') + assert len(l1.rpc.listfunds()['outputs']) == 2 + + l2.start() + search_start = l2.daemon.logsearch_start + log = l2.daemon.wait_for_log('adding utxo to watch .* csv 40.*') + utxo1 = re.match('.* adding utxo to watch (.*), csv .*', log).group(1) + + l2.daemon.logsearch_start = search_start + log = l2.daemon.wait_for_log('adding utxo to watch .* csv 1') + utxo3 = re.match('.* adding utxo to watch (.*), csv 1', log).group(1) + + # we *shouldn't* be able to spend it, there's a lock on it + with pytest.raises(RpcError, match='UTXO .* is csv locked'): + l2.rpc.withdraw(l2.rpc.newaddr()['bech32'], "all", utxos=[utxo1]) + + # we *can* spend the 1csv lock one + l2.rpc.withdraw(l2.rpc.newaddr()['bech32'], "all", utxos=[utxo3]) + + # We need to give l4-l2 time to update their blockheights + bitcoind.generate_block(1000) + sync_blockheight(bitcoind, [l2, l3, l4]) + l4.daemon.wait_for_log('peer_out WIRE_UPDATE_BLOCKHEIGHT') + + bitcoind.generate_block(1000) + sync_blockheight(bitcoind, [l2, l3, l4]) + l4.daemon.wait_for_log('peer_out WIRE_UPDATE_BLOCKHEIGHT') + + bitcoind.generate_block(1000) + sync_blockheight(bitcoind, [l2, l3, l4]) + l4.daemon.wait_for_log('peer_out WIRE_UPDATE_BLOCKHEIGHT') + + bitcoind.generate_block(1000) + sync_blockheight(bitcoind, [l2, l3, l4]) + l4.daemon.wait_for_log('peer_out WIRE_UPDATE_BLOCKHEIGHT') + + bitcoind.generate_block(32) + sync_blockheight(bitcoind, [l2, l3, l4]) + l4.daemon.wait_for_log('peer_out WIRE_UPDATE_BLOCKHEIGHT') + + l2.rpc.withdraw(l2.rpc.newaddr()['bech32'], "all", utxos=[utxo1]) + + # l3 cleans up their to-self after their lease expires + assert l3.daemon.is_in_log('Broadcasting OUR_DELAYED_RETURN_TO_WALLET') + + # l2<->l4 mutual close should work + chan = l4.get_channel_scid(l2) + l2.rpc.connect(l4.info['id'], 'localhost', l4.port) + l4.rpc.close(chan) + l2.daemon.wait_for_log('State changed from CLOSINGD_SIGEXCHANGE to CLOSINGD_COMPLETE') + + +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.openchannel('v2') +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") +@pytest.mark.developer("requres 'dev-queryrates'") +def test_channel_lease_lessor_cheat(node_factory, bitcoind, chainparams): + ''' + Check that lessee can recover funds if lessor cheats + ''' + opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'may_reconnect': True, 'allow_warning': True}, + {'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'may_reconnect': True, 'allow_broken_log': True}] + l1, l2, = node_factory.get_nodes(2, opts=opts) + amount = 500000 + feerate = 2000 + l1.fundwallet(20000000) + l2.fundwallet(20000000) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) + l1.daemon.wait_for_log('disconnect') + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # l1 leases a channel from l2 + l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease=rates['compact_lease']) + + bitcoind.generate_block(6) + l1.daemon.wait_for_log('to CHANNELD_NORMAL') + wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(l1.get_channel_scid(l2))['channels']] == [True, True]) + wait_for(lambda: [c['active'] for c in l2.rpc.listchannels(l2.get_channel_scid(l1))['channels']] == [True, True]) + # send some payments, mine a block or two + inv = l2.rpc.invoice(10**4, '1', 'no_1') + l1.rpc.pay(inv['bolt11']) + + bitcoind.generate_block(1) + + # make database snapshot of l2 + l2.stop() + l2_db_path = os.path.join(l2.daemon.lightning_dir, chainparams['name'], 'lightningd.sqlite3') + l2_db_path_bak = os.path.join(l2.daemon.lightning_dir, chainparams['name'], 'lightningd.sqlite3.bak') + copyfile(l2_db_path, l2_db_path_bak) + l2.start() + l2.rpc.connect(l1.info['id'], 'localhost', l1.port) + sync_blockheight(bitcoind, [l2]) + + # push some money from l2->l1, so the commit counter advances + inv = l1.rpc.invoice(10**5, '2', 'no_2') + l2.rpc.pay(inv['bolt11']) + + # stop both nodes, roll back l2's database + l2.stop() + l1.stop() + copyfile(l2_db_path_bak, l2_db_path) + + # start l2 and force close channel with l1 while l1 is still offline + l2.start() + sync_blockheight(bitcoind, [l2]) + l2.rpc.close(l1.info['id'], 1, force_lease_closed=True) + bitcoind.generate_block(1, wait_for_mempool=1) + + l1.start() + sync_blockheight(bitcoind, [l1]) + l1.daemon.wait_for_logs(['Broadcasting OUR_PENALTY_TX', + ' Propose handling THEIR_REVOKED_UNILATERAL/DELAYED_CHEAT_OUTPUT_TO_THEM by OUR_PENALTY_TX']) + + bitcoind.generate_block(1, wait_for_mempool=1) + # l2 sees that l1 has spent their coins! + l2.daemon.wait_for_log('Unknown spend of OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by') + + +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.openchannel('v2') +@unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") +@pytest.mark.developer("requres 'dev-queryrates'") +def test_channel_lease_lessee_cheat(node_factory, bitcoind, chainparams): + ''' + Check that lessor can recover funds if lessee cheats + ''' + opts = [{'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'may_reconnect': True, 'allow_broken_log': True}, + {'funder-policy': 'match', 'funder-policy-mod': 100, + 'lease-fee-base-msat': '100sat', 'lease-fee-basis': 100, + 'may_reconnect': True}] + l1, l2, = node_factory.get_nodes(2, opts=opts) + amount = 500000 + feerate = 2000 + l1.fundwallet(20000000) + l2.fundwallet(20000000) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + rates = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) + l1.daemon.wait_for_log('disconnect') + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + # l1 leases a channel from l2 + l1.rpc.fundchannel(l2.info['id'], amount, request_amt=amount, + feerate='{}perkw'.format(feerate), + compact_lease=rates['compact_lease']) + + bitcoind.generate_block(6) + l1.daemon.wait_for_log('to CHANNELD_NORMAL') + wait_for(lambda: [c['active'] for c in l1.rpc.listchannels(l1.get_channel_scid(l2))['channels']] == [True, True]) + wait_for(lambda: [c['active'] for c in l2.rpc.listchannels(l2.get_channel_scid(l1))['channels']] == [True, True]) + # send some payments, mine a block or two + inv = l2.rpc.invoice(10**4, '1', 'no_1') + l1.rpc.pay(inv['bolt11']) + + bitcoind.generate_block(1) + + # make database snapshot of l1 + l1.stop() + l1_db_path = os.path.join(l1.daemon.lightning_dir, chainparams['name'], 'lightningd.sqlite3') + l1_db_path_bak = os.path.join(l1.daemon.lightning_dir, chainparams['name'], 'lightningd.sqlite3.bak') + copyfile(l1_db_path, l1_db_path_bak) + l1.start() + l1.rpc.connect(l1.info['id'], 'localhost', l1.port) + sync_blockheight(bitcoind, [l1]) + + # push some money from l2->l1, so the commit counter advances + inv = l1.rpc.invoice(10**5, '2', 'no_2') + l2.rpc.pay(inv['bolt11']) + + # stop both nodes, roll back l1's database + l1.stop() + l2.stop() + copyfile(l1_db_path_bak, l1_db_path) + + # start l1 and force close channel with l2 while l2 is still offline + l1.start() + sync_blockheight(bitcoind, [l1]) + l1.rpc.close(l2.info['id'], 1, force_lease_closed=True) + bitcoind.generate_block(1, wait_for_mempool=1) + + l2.start() + sync_blockheight(bitcoind, [l2]) + l2.daemon.wait_for_logs(['Broadcasting OUR_PENALTY_TX', + ' Propose handling THEIR_REVOKED_UNILATERAL/DELAYED_CHEAT_OUTPUT_TO_THEM by OUR_PENALTY_TX']) + + bitcoind.generate_block(1, wait_for_mempool=1) + # l2 sees that l1 has spent their coins! + l1.daemon.wait_for_logs(['Grinding for to_remote', + 'Unknown spend of OUR_UNILATERAL/DELAYED_OUTPUT_TO_US by']) + + @pytest.mark.developer("needs DEVELOPER=1") @unittest.skipIf(os.getenv('TEST_DB_PROVIDER', 'sqlite3') != 'sqlite3', "Makes use of the sqlite3 db") @pytest.mark.slow_test diff --git a/tests/test_gossip.py b/tests/test_gossip.py index abf6841e01c6..ec0a995f9dfb 100644 --- a/tests/test_gossip.py +++ b/tests/test_gossip.py @@ -928,6 +928,85 @@ def test_gossip_addresses(node_factory, bitcoind): ] +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.developer("needs dev-fast-gossip") +@pytest.mark.openchannel('v2') +def test_gossip_lease_rates(node_factory, bitcoind): + lease_opts = {'lease-fee-basis': 50, + 'lease-fee-base-msat': '2000msat', + 'channel-fee-max-base-msat': '500sat', + 'channel-fee-max-proportional-thousandths': 200} + l1, l2 = node_factory.get_nodes(2, opts=[lease_opts, {}]) + + # These logs happen during startup, start looking from the beginning + l1.daemon.logsearch_start = 0 + l2.daemon.logsearch_start = 0 + + rates = l1.rpc.call('funderupdate') + assert rates['channel_fee_max_base_msat'] == Millisatoshi('500000msat') + assert rates['channel_fee_max_proportional_thousandths'] == 200 + assert rates['funding_weight'] == 666 # Default on regtest + assert rates['lease_fee_base_msat'] == Millisatoshi('2000msat') + assert rates['lease_fee_basis'] == 50 + + rates = l2.rpc.call('funderupdate') + assert 'channel_fee_max_base_msat' not in rates + assert 'channel_fee_max_proportional_thousandths' not in rates + assert 'funding_weight' not in rates + assert 'lease_fee_base_msat' not in rates + assert 'lease_fee_basis' not in rates + + # Open a channel, check that the node_announcements + # include offer details, as expected + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + l1.fundchannel(l2, 10**6) + + # Announce depth is ALWAYS 6 blocks + bitcoind.generate_block(5) + + l2.daemon.wait_for_log('Received node_announcement for node {}' + .format(l1.info['id'])) + l1.daemon.wait_for_log('Received node_announcement for node {}' + .format(l2.info['id'])) + + l2_nodeinfo = only_one(l1.rpc.listnodes(l2.info['id'])['nodes']) + l1_nodeinfo = only_one(l2.rpc.listnodes(l1.info['id'])['nodes']) + + assert 'option_will_fund' not in l2_nodeinfo + rates = l1_nodeinfo['option_will_fund'] + assert rates['channel_fee_max_base_msat'] == Millisatoshi('500000msat') + assert rates['channel_fee_max_proportional_thousandths'] == 200 + assert rates['funding_weight'] == 666 # Default on regtest + assert rates['lease_fee_base_msat'] == Millisatoshi('2000msat') + assert rates['lease_fee_basis'] == 50 + + # Update the node announce (set new on l2, turn off l1) + # (Turn off by setting everything to zero) + l1.rpc.call('funderupdate', {'channel_fee_max_base_msat': '0msat', + 'channel_fee_max_proportional_thousandths': 0, + 'funding_weight': 0, + 'lease_fee_base_msat': '0msat', + 'lease_fee_basis': 0}) + l2.rpc.call('funderupdate', {'channel_fee_max_base_msat': '30000msat', + 'channel_fee_max_proportional_thousandths': 100, + 'lease_fee_base_msat': '400000msat', + 'lease_fee_basis': 20}) + + l1.daemon.wait_for_log('Received node_announcement for node {}'.format(l2.info['id'])) + l2.daemon.wait_for_log('Received node_announcement for node {}'.format(l1.info['id'])) + + l2_nodeinfo = only_one(l1.rpc.listnodes(l2.info['id'])['nodes']) + l1_nodeinfo = only_one(l2.rpc.listnodes(l1.info['id'])['nodes']) + + assert 'option_will_fund' not in l1_nodeinfo + rates = l2_nodeinfo['option_will_fund'] + assert rates['channel_fee_max_base_msat'] == Millisatoshi('30000msat') + assert rates['channel_fee_max_proportional_thousandths'] == 100 + assert rates['funding_weight'] == 666 # Default on regtest + assert rates['lease_fee_base_msat'] == Millisatoshi('400000msat') + assert rates['lease_fee_basis'] == 20 + + def test_gossip_store_load(node_factory): """Make sure we can read canned gossip store""" l1 = node_factory.get_node(start=False) @@ -1084,6 +1163,28 @@ def test_node_reannounce(node_factory, bitcoind, chainparams): # Won't have queued up another one, either. assert not l1.daemon.is_in_log('node_announcement: delaying') + # Try updating the lease rates ad + ad = l1.rpc.call('setleaserates', + {'lease_fee_base_msat': '1000sat', + 'lease_fee_basis': 20, + 'funding_weight': 150, + 'channel_fee_max_base_msat': '2000msat', + 'channel_fee_max_proportional_thousandths': 22}) + + assert ad['lease_fee_base_msat'] == Millisatoshi('1000000msat') + assert ad['lease_fee_basis'] == 20 + assert ad['funding_weight'] == 150 + assert ad['channel_fee_max_base_msat'] == Millisatoshi('2000msat') + assert ad['channel_fee_max_proportional_thousandths'] == 22 + + msgs2 = l1.query_gossip('gossip_timestamp_filter', + genesis_blockhash, + '0', '0xFFFFFFFF', + # Filter out gossip_timestamp_filter, + # channel_announcement and channel_updates. + filters=['0109', '0102', '0100']) + assert msgs != msgs2 + def test_gossipwith(node_factory): l1, l2 = node_factory.line_graph(2, wait_for_announce=True) diff --git a/tests/test_misc.py b/tests/test_misc.py index c1dee2b55546..9bb5e7abb726 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -1460,6 +1460,11 @@ def test_feerates(node_factory): types = ["opening", "mutual_close", "unilateral_close", "delayed_to_us", "htlc_resolution", "penalty"] + # Try parsing the feerates, won't work because can't estimate + for t in types: + with pytest.raises(RpcError, match=r'Cannot estimate fees'): + feerate = l1.rpc.parsefeerate(t) + # Query feerates (shouldn't give any!) wait_for(lambda: len(l1.rpc.feerates('perkw')['perkw']) == 2) feerates = l1.rpc.feerates('perkw') @@ -1537,6 +1542,13 @@ def test_feerates(node_factory): htlc_feerate = feerates["perkw"]["htlc_resolution"] htlc_timeout_cost = feerates["onchain_fee_estimates"]["htlc_timeout_satoshis"] htlc_success_cost = feerates["onchain_fee_estimates"]["htlc_success_satoshis"] + + # Try parsing the feerates, won't work because can't estimate + for t in types: + feerate = l1.rpc.parsefeerate(t) + assert feerate['perkw'] + assert 'perkb' not in feerate + if EXPERIMENTAL_FEATURES: # option_anchor_outputs assert htlc_timeout_cost == htlc_feerate * 666 // 1000 diff --git a/tests/test_opening.py b/tests/test_opening.py index 7758e7da1fdb..7e81f6bf0cf4 100644 --- a/tests/test_opening.py +++ b/tests/test_opening.py @@ -1,6 +1,6 @@ from fixtures import * # noqa: F401,F403 from fixtures import TEST_NETWORK -from pyln.client import RpcError +from pyln.client import Millisatoshi, RpcError from utils import ( only_one, wait_for, sync_blockheight, first_channel_id ) @@ -15,6 +15,42 @@ def find_next_feerate(node, peer): return chan['next_feerate'] +@unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') +@pytest.mark.openchannel('v2') +@pytest.mark.developer("requres 'dev-queryrates'") +def test_queryrates(node_factory, bitcoind): + l1, l2 = node_factory.get_nodes(2) + + amount = 10 ** 6 + + l1.fundwallet(amount * 10) + l2.fundwallet(amount * 10) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + with pytest.raises(RpcError, match=r'not advertising liquidity'): + l1.rpc.dev_queryrates(l2.info['id'], amount, amount * 10) + + l2.rpc.call('funderupdate', {'policy': 'match', + 'policy_mod': 100, + 'per_channel_max': '1btc', + 'fuzz_percent': 0, + 'lease_fee_base_msat': '2sat', + 'funding_weight': 1000, + 'lease_fee_basis': 140, + 'channel_fee_max_base_msat': '3sat', + 'channel_fee_max_proportional_thousandths': 101}) + + l1.rpc.connect(l2.info['id'], 'localhost', l2.port) + result = l1.rpc.dev_queryrates(l2.info['id'], amount, amount) + assert result['our_funding_msat'] == Millisatoshi(amount * 1000) + assert result['their_funding_msat'] == Millisatoshi(amount * 1000) + assert result['funding_weight'] == 1000 + assert result['lease_fee_base_msat'] == Millisatoshi(2000) + assert result['lease_fee_basis'] == 140 + assert result['channel_fee_max_base_msat'] == Millisatoshi(3000) + assert result['channel_fee_max_proportional_thousandths'] == 101 + + @unittest.skipIf(TEST_NETWORK != 'regtest', 'elementsd doesnt yet support PSBT features we need') @pytest.mark.developer("uses dev-disconnect") @pytest.mark.openchannel('v1') # Mixed v1 + v2, v2 manually turned on @@ -979,7 +1015,7 @@ def test_funder_options(node_factory, bitcoind): assert funder_opts['per_channel_min'] == '10000000msat' assert funder_opts['per_channel_max'] == '4294967295000msat' assert funder_opts['reserve_tank'] == '0msat' - assert funder_opts['fuzz_percent'] == 5 + assert funder_opts['fuzz_percent'] == 0 assert funder_opts['fund_probability'] == 100 # l2 funds a chanenl with us. We don't contribute diff --git a/tools/generate-wire.py b/tools/generate-wire.py index 38c372d9cc4e..9845c9a0bfce 100755 --- a/tools/generate-wire.py +++ b/tools/generate-wire.py @@ -231,6 +231,7 @@ class Type(FieldSet): 'bitcoin_tx_output', 'exclude_entry', 'fee_states', + 'height_states', 'onionreply', 'feature_set', 'onionmsg_path', diff --git a/wallet/db.c b/wallet/db.c index be0fc74d42ff..a56aa37790ef 100644 --- a/wallet/db.c +++ b/wallet/db.c @@ -62,6 +62,10 @@ static void fillin_missing_local_basepoints(struct lightningd *ld, struct db *db, const struct migration_context *mc); +static void fillin_missing_channel_blockheights(struct lightningd *ld, + struct db *db, + const struct migration_context *mc); + /* Do not reorder or remove elements from this array, it is used to * migrate existing databases from a previous state, based on the * string indices */ @@ -728,6 +732,23 @@ static struct migration dbmigrations[] = { " local_static_remotekey_start = 9223372036854775807" " WHERE option_static_remotekey = 0"), NULL}, + {SQL("ALTER TABLE channel_funding_inflights ADD lease_commit_sig BLOB DEFAULT NULL"), NULL}, + {SQL("ALTER TABLE channel_funding_inflights ADD lease_chan_max_msat BIGINT DEFAULT NULL"), NULL}, + {SQL("ALTER TABLE channel_funding_inflights ADD lease_chan_max_ppt INTEGER DEFAULT NULL"), NULL}, + {SQL("ALTER TABLE channel_funding_inflights ADD lease_expiry INTEGER DEFAULT 0"), NULL}, + {SQL("ALTER TABLE channel_funding_inflights ADD lease_blockheight_start INTEGER DEFAULT 0"), NULL}, + {SQL("ALTER TABLE channels ADD lease_commit_sig BLOB DEFAULT NULL"), NULL}, + {SQL("ALTER TABLE channels ADD lease_chan_max_msat INTEGER DEFAULT NULL"), NULL}, + {SQL("ALTER TABLE channels ADD lease_chan_max_ppt INTEGER DEFAULT NULL"), NULL}, + {SQL("ALTER TABLE channels ADD lease_expiry INTEGER DEFAULT 0"), NULL}, + {SQL("CREATE TABLE channel_blockheights (" + " channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE," + " hstate INTEGER," + " blockheight INTEGER," + " UNIQUE (channel_id, hstate)" + ");"), + fillin_missing_channel_blockheights}, + {SQL("ALTER TABLE outputs ADD csv_lock INTEGER DEFAULT 1;"), NULL}, }; /* Leak tracking. */ @@ -1420,6 +1441,33 @@ static void fillin_missing_local_basepoints(struct lightningd *ld, tal_free(stmt); } +/* New 'channel_blockheights' table, every existing channel gets a + * 'initial blockheight' of 0 */ +static void fillin_missing_channel_blockheights(struct lightningd *ld, + struct db *db, + const struct migration_context *mc) +{ + struct db_stmt *stmt; + + /* Set all existing channels to 0 */ + /* If we're funder (LOCAL=0): + * Then our blockheight is set last (SENT_ADD_ACK_REVOCATION = 4) */ + stmt = db_prepare_v2(db, + SQL("INSERT INTO channel_blockheights" + " (channel_id, hstate, blockheight)" + " SELECT id, 4, 0 FROM channels" + " WHERE funder = 0;")); + db_exec_prepared_v2(take(stmt)); + /* If they're funder (REMOTE=1): + * Then their blockheight is last (RCVD_ADD_ACK_REVOCATION = 14) */ + stmt = db_prepare_v2(db, + SQL("INSERT INTO channel_blockheights" + " (channel_id, hstate, blockheight)" + " SELECT id, 14, 0 FROM channels" + " WHERE funder = 1;")); + db_exec_prepared_v2(take(stmt)); +} + void migrate_inflight_last_tx_to_psbt(struct lightningd *ld, struct db *db, const struct migration_context *mc) diff --git a/wallet/db_postgres_sqlgen.c b/wallet/db_postgres_sqlgen.c index 8a3b70c5776d..145814864d95 100644 --- a/wallet/db_postgres_sqlgen.c +++ b/wallet/db_postgres_sqlgen.c @@ -956,6 +956,72 @@ struct db_query db_postgres_queries[] = { .placeholders = 0, .readonly = false, }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_commit_sig BLOB DEFAULT NULL", + .query = "ALTER TABLE channel_funding_inflights ADD lease_commit_sig BYTEA DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_chan_max_msat BIGINT DEFAULT NULL", + .query = "ALTER TABLE channel_funding_inflights ADD lease_chan_max_msat BIGINT DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_chan_max_ppt INTEGER DEFAULT NULL", + .query = "ALTER TABLE channel_funding_inflights ADD lease_chan_max_ppt INTEGER DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_expiry INTEGER DEFAULT 0", + .query = "ALTER TABLE channel_funding_inflights ADD lease_expiry INTEGER DEFAULT 0", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_blockheight_start INTEGER DEFAULT 0", + .query = "ALTER TABLE channel_funding_inflights ADD lease_blockheight_start INTEGER DEFAULT 0", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channels ADD lease_commit_sig BLOB DEFAULT NULL", + .query = "ALTER TABLE channels ADD lease_commit_sig BYTEA DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channels ADD lease_chan_max_msat INTEGER DEFAULT NULL", + .query = "ALTER TABLE channels ADD lease_chan_max_msat INTEGER DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channels ADD lease_chan_max_ppt INTEGER DEFAULT NULL", + .query = "ALTER TABLE channels ADD lease_chan_max_ppt INTEGER DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channels ADD lease_expiry INTEGER DEFAULT 0", + .query = "ALTER TABLE channels ADD lease_expiry INTEGER DEFAULT 0", + .placeholders = 0, + .readonly = false, + }, + { + .name = "CREATE TABLE channel_blockheights ( channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, hstate INTEGER, blockheight INTEGER, UNIQUE (channel_id, hstate));", + .query = "CREATE TABLE channel_blockheights ( channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, hstate INTEGER, blockheight INTEGER, UNIQUE (channel_id, hstate));", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE outputs ADD csv_lock INTEGER DEFAULT 1;", + .query = "ALTER TABLE outputs ADD csv_lock INTEGER DEFAULT 1;", + .placeholders = 0, + .readonly = false, + }, { .name = "UPDATE vars SET intval = intval + 1 WHERE name = 'data_version' AND intval = ?", .query = "UPDATE vars SET intval = intval + 1 WHERE name = 'data_version' AND intval = $1", @@ -1052,6 +1118,18 @@ struct db_query db_postgres_queries[] = { .placeholders = 6, .readonly = false, }, + { + .name = "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 4, 0 FROM channels WHERE funder = 0;", + .query = "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 4, 0 FROM channels WHERE funder = 0;", + .placeholders = 0, + .readonly = false, + }, + { + .name = "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 14, 0 FROM channels WHERE funder = 1;", + .query = "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 14, 0 FROM channels WHERE funder = 1;", + .placeholders = 0, + .readonly = false, + }, { .name = "SELECT c.id, p.node_id, c.fundingkey_remote, inflight.last_tx, inflight.last_sig, inflight.funding_satoshi, inflight.funding_tx_id FROM channels c LEFT OUTER JOIN peers p ON p.id = c.peer_id LEFT OUTER JOIN channel_funding_inflights inflight ON c.id = inflight.channel_id WHERE inflight.last_tx IS NOT NULL;", .query = "SELECT c.id, p.node_id, c.fundingkey_remote, inflight.last_tx, inflight.last_sig, inflight.funding_satoshi, inflight.funding_tx_id FROM channels c LEFT OUTER JOIN peers p ON p.id = c.peer_id LEFT OUTER JOIN channel_funding_inflights inflight ON c.id = inflight.channel_id WHERE inflight.last_tx IS NOT NULL;", @@ -1197,26 +1275,26 @@ struct db_query db_postgres_queries[] = { .readonly = false, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs", .placeholders = 0, .readonly = true, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status= ? ", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status= $1 ", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs WHERE status= ? ", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs WHERE status= $1 ", .placeholders = 1, .readonly = true, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL", .placeholders = 0, .readonly = true, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE prev_out_tx = $1 AND prev_out_index = $2", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE prev_out_tx = $1 AND prev_out_index = $2", .placeholders = 2, .readonly = true, }, @@ -1227,11 +1305,17 @@ struct db_query db_postgres_queries[] = { .readonly = false, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status = $1 OR (status = $2 AND reserved_til <= $3)ORDER BY RANDOM();", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til, csv_lock FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til, csv_lock FROM outputs WHERE status = $1 OR (status = $2 AND reserved_til <= $3)ORDER BY RANDOM();", .placeholders = 3, .readonly = true, }, + { + .name = "INSERT INTO outputs ( prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, csv_lock) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + .query = "INSERT INTO outputs ( prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, csv_lock) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14);", + .placeholders = 14, + .readonly = false, + }, { .name = "INSERT INTO shachains (min_index, num_valid) VALUES (?, 0);", .query = "INSERT INTO shachains (min_index, num_valid) VALUES ($1, 0);", @@ -1293,9 +1377,15 @@ struct db_query db_postgres_queries[] = { .readonly = true, }, { - .name = "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);", - .query = "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9);", - .placeholders = 9, + .name = "SELECT hstate, blockheight FROM channel_blockheights WHERE channel_id = ?", + .query = "SELECT hstate, blockheight FROM channel_blockheights WHERE channel_id = $1", + .placeholders = 1, + .readonly = true, + }, + { + .name = "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_expiry, lease_blockheight_start) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + .query = "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_expiry, lease_blockheight_start) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14);", + .placeholders = 14, .readonly = false, }, { @@ -1311,8 +1401,8 @@ struct db_query db_postgres_queries[] = { .readonly = false, }, { - .name = "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate", - .query = "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received FROM channel_funding_inflights WHERE channel_id = $1 ORDER BY funding_feerate", + .name = "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_blockheight_start FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate", + .query = "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_blockheight_start FROM channel_funding_inflights WHERE channel_id = $1 ORDER BY funding_feerate", .placeholders = 1, .readonly = true, }, @@ -1323,8 +1413,8 @@ struct db_query db_postgres_queries[] = { .readonly = true, }, { - .name = "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum FROM channels WHERE state != ?;", - .query = "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum FROM channels WHERE state != $1;", + .name = "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt FROM channels WHERE state != ?;", + .query = "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt FROM channels WHERE state != $1;", .placeholders = 1, .readonly = true, }, @@ -1389,9 +1479,9 @@ struct db_query db_postgres_queries[] = { .readonly = false, }, { - .name = "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, local_static_remotekey_start=?, remote_static_remotekey_start=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=? WHERE id=?", - .query = "UPDATE channels SET shachain_remote_id=$1, short_channel_id=$2, full_channel_id=$3, state=$4, funder=$5, channel_flags=$6, minimum_depth=$7, next_index_local=$8, next_index_remote=$9, next_htlc_id=$10, funding_tx_id=$11, funding_tx_outnum=$12, funding_satoshi=$13, our_funding_satoshi=$14, funding_locked_remote=$15, push_msatoshi=$16, msatoshi_local=$17, shutdown_scriptpubkey_remote=$18, shutdown_keyidx_local=$19, channel_config_local=$20, last_tx=$21, last_sig=$22, last_was_revoke=$23, min_possible_feerate=$24, max_possible_feerate=$25, msatoshi_to_us_min=$26, msatoshi_to_us_max=$27, feerate_base=$28, feerate_ppm=$29, remote_upfront_shutdown_script=$30, local_static_remotekey_start=$31, remote_static_remotekey_start=$32, option_anchor_outputs=$33, shutdown_scriptpubkey_local=$34, closer=$35, state_change_reason=$36, shutdown_wrong_txid=$37, shutdown_wrong_outnum=$38 WHERE id=$39", - .placeholders = 39, + .name = "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, local_static_remotekey_start=?, remote_static_remotekey_start=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=?, lease_expiry=?, lease_commit_sig=?, lease_chan_max_msat=?, lease_chan_max_ppt=? WHERE id=?", + .query = "UPDATE channels SET shachain_remote_id=$1, short_channel_id=$2, full_channel_id=$3, state=$4, funder=$5, channel_flags=$6, minimum_depth=$7, next_index_local=$8, next_index_remote=$9, next_htlc_id=$10, funding_tx_id=$11, funding_tx_outnum=$12, funding_satoshi=$13, our_funding_satoshi=$14, funding_locked_remote=$15, push_msatoshi=$16, msatoshi_local=$17, shutdown_scriptpubkey_remote=$18, shutdown_keyidx_local=$19, channel_config_local=$20, last_tx=$21, last_sig=$22, last_was_revoke=$23, min_possible_feerate=$24, max_possible_feerate=$25, msatoshi_to_us_min=$26, msatoshi_to_us_max=$27, feerate_base=$28, feerate_ppm=$29, remote_upfront_shutdown_script=$30, local_static_remotekey_start=$31, remote_static_remotekey_start=$32, option_anchor_outputs=$33, shutdown_scriptpubkey_local=$34, closer=$35, state_change_reason=$36, shutdown_wrong_txid=$37, shutdown_wrong_outnum=$38, lease_expiry=$39, lease_commit_sig=$40, lease_chan_max_msat=$41, lease_chan_max_ppt=$42 WHERE id=$43", + .placeholders = 43, .readonly = false, }, { @@ -1412,6 +1502,18 @@ struct db_query db_postgres_queries[] = { .placeholders = 3, .readonly = false, }, + { + .name = "DELETE FROM channel_blockheights WHERE channel_id=?", + .query = "DELETE FROM channel_blockheights WHERE channel_id=$1", + .placeholders = 1, + .readonly = false, + }, + { + .name = "INSERT INTO channel_blockheights VALUES(?, ?, ?)", + .query = "INSERT INTO channel_blockheights VALUES($1, $2, $3)", + .placeholders = 3, + .readonly = false, + }, { .name = "UPDATE channels SET last_sent_commit=? WHERE id=?", .query = "UPDATE channels SET last_sent_commit=$1 WHERE id=$2", @@ -1918,10 +2020,10 @@ struct db_query db_postgres_queries[] = { }, }; -#define DB_POSTGRES_QUERY_COUNT 318 +#define DB_POSTGRES_QUERY_COUNT 335 #endif /* HAVE_POSTGRES */ #endif /* LIGHTNINGD_WALLET_GEN_DB_POSTGRES */ -// SHA256STAMP:1379bcdee314439910fc6b238f7ec986536543c53933883ffd1b750dfc34f9b9 +// SHA256STAMP:faf897ed266e0365697865648649c2107997bdf9923a8062b0256d314f7ff6db diff --git a/wallet/db_sqlite3_sqlgen.c b/wallet/db_sqlite3_sqlgen.c index 98ed0bf0c4b6..562cd9400820 100644 --- a/wallet/db_sqlite3_sqlgen.c +++ b/wallet/db_sqlite3_sqlgen.c @@ -956,6 +956,72 @@ struct db_query db_sqlite3_queries[] = { .placeholders = 0, .readonly = false, }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_commit_sig BLOB DEFAULT NULL", + .query = "ALTER TABLE channel_funding_inflights ADD lease_commit_sig BLOB DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_chan_max_msat BIGINT DEFAULT NULL", + .query = "ALTER TABLE channel_funding_inflights ADD lease_chan_max_msat INTEGER DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_chan_max_ppt INTEGER DEFAULT NULL", + .query = "ALTER TABLE channel_funding_inflights ADD lease_chan_max_ppt INTEGER DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_expiry INTEGER DEFAULT 0", + .query = "ALTER TABLE channel_funding_inflights ADD lease_expiry INTEGER DEFAULT 0", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channel_funding_inflights ADD lease_blockheight_start INTEGER DEFAULT 0", + .query = "ALTER TABLE channel_funding_inflights ADD lease_blockheight_start INTEGER DEFAULT 0", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channels ADD lease_commit_sig BLOB DEFAULT NULL", + .query = "ALTER TABLE channels ADD lease_commit_sig BLOB DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channels ADD lease_chan_max_msat INTEGER DEFAULT NULL", + .query = "ALTER TABLE channels ADD lease_chan_max_msat INTEGER DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channels ADD lease_chan_max_ppt INTEGER DEFAULT NULL", + .query = "ALTER TABLE channels ADD lease_chan_max_ppt INTEGER DEFAULT NULL", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE channels ADD lease_expiry INTEGER DEFAULT 0", + .query = "ALTER TABLE channels ADD lease_expiry INTEGER DEFAULT 0", + .placeholders = 0, + .readonly = false, + }, + { + .name = "CREATE TABLE channel_blockheights ( channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, hstate INTEGER, blockheight INTEGER, UNIQUE (channel_id, hstate));", + .query = "CREATE TABLE channel_blockheights ( channel_id INTEGER REFERENCES channels(id) ON DELETE CASCADE, hstate INTEGER, blockheight INTEGER, UNIQUE (channel_id, hstate));", + .placeholders = 0, + .readonly = false, + }, + { + .name = "ALTER TABLE outputs ADD csv_lock INTEGER DEFAULT 1;", + .query = "ALTER TABLE outputs ADD csv_lock INTEGER DEFAULT 1;", + .placeholders = 0, + .readonly = false, + }, { .name = "UPDATE vars SET intval = intval + 1 WHERE name = 'data_version' AND intval = ?", .query = "UPDATE vars SET intval = intval + 1 WHERE name = 'data_version' AND intval = ?", @@ -1052,6 +1118,18 @@ struct db_query db_sqlite3_queries[] = { .placeholders = 6, .readonly = false, }, + { + .name = "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 4, 0 FROM channels WHERE funder = 0;", + .query = "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 4, 0 FROM channels WHERE funder = 0;", + .placeholders = 0, + .readonly = false, + }, + { + .name = "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 14, 0 FROM channels WHERE funder = 1;", + .query = "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 14, 0 FROM channels WHERE funder = 1;", + .placeholders = 0, + .readonly = false, + }, { .name = "SELECT c.id, p.node_id, c.fundingkey_remote, inflight.last_tx, inflight.last_sig, inflight.funding_satoshi, inflight.funding_tx_id FROM channels c LEFT OUTER JOIN peers p ON p.id = c.peer_id LEFT OUTER JOIN channel_funding_inflights inflight ON c.id = inflight.channel_id WHERE inflight.last_tx IS NOT NULL;", .query = "SELECT c.id, p.node_id, c.fundingkey_remote, inflight.last_tx, inflight.last_sig, inflight.funding_satoshi, inflight.funding_tx_id FROM channels c LEFT OUTER JOIN peers p ON p.id = c.peer_id LEFT OUTER JOIN channel_funding_inflights inflight ON c.id = inflight.channel_id WHERE inflight.last_tx IS NOT NULL;", @@ -1197,26 +1275,26 @@ struct db_query db_sqlite3_queries[] = { .readonly = false, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs", .placeholders = 0, .readonly = true, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status= ? ", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status= ? ", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs WHERE status= ? ", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs WHERE status= ? ", .placeholders = 1, .readonly = true, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL", .placeholders = 0, .readonly = true, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?", .placeholders = 2, .readonly = true, }, @@ -1227,11 +1305,17 @@ struct db_query db_sqlite3_queries[] = { .readonly = false, }, { - .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();", - .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();", + .name = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til, csv_lock FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();", + .query = "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til, csv_lock FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();", .placeholders = 3, .readonly = true, }, + { + .name = "INSERT INTO outputs ( prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, csv_lock) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + .query = "INSERT INTO outputs ( prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, csv_lock) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + .placeholders = 14, + .readonly = false, + }, { .name = "INSERT INTO shachains (min_index, num_valid) VALUES (?, 0);", .query = "INSERT INTO shachains (min_index, num_valid) VALUES (?, 0);", @@ -1293,9 +1377,15 @@ struct db_query db_sqlite3_queries[] = { .readonly = true, }, { - .name = "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);", - .query = "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);", - .placeholders = 9, + .name = "SELECT hstate, blockheight FROM channel_blockheights WHERE channel_id = ?", + .query = "SELECT hstate, blockheight FROM channel_blockheights WHERE channel_id = ?", + .placeholders = 1, + .readonly = true, + }, + { + .name = "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_expiry, lease_blockheight_start) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + .query = "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_expiry, lease_blockheight_start) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);", + .placeholders = 14, .readonly = false, }, { @@ -1311,8 +1401,8 @@ struct db_query db_sqlite3_queries[] = { .readonly = false, }, { - .name = "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate", - .query = "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate", + .name = "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_blockheight_start FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate", + .query = "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_blockheight_start FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate", .placeholders = 1, .readonly = true, }, @@ -1323,8 +1413,8 @@ struct db_query db_sqlite3_queries[] = { .readonly = true, }, { - .name = "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum FROM channels WHERE state != ?;", - .query = "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum FROM channels WHERE state != ?;", + .name = "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt FROM channels WHERE state != ?;", + .query = "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt FROM channels WHERE state != ?;", .placeholders = 1, .readonly = true, }, @@ -1389,9 +1479,9 @@ struct db_query db_sqlite3_queries[] = { .readonly = false, }, { - .name = "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, local_static_remotekey_start=?, remote_static_remotekey_start=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=? WHERE id=?", - .query = "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, local_static_remotekey_start=?, remote_static_remotekey_start=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=? WHERE id=?", - .placeholders = 39, + .name = "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, local_static_remotekey_start=?, remote_static_remotekey_start=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=?, lease_expiry=?, lease_commit_sig=?, lease_chan_max_msat=?, lease_chan_max_ppt=? WHERE id=?", + .query = "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, local_static_remotekey_start=?, remote_static_remotekey_start=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=?, lease_expiry=?, lease_commit_sig=?, lease_chan_max_msat=?, lease_chan_max_ppt=? WHERE id=?", + .placeholders = 43, .readonly = false, }, { @@ -1412,6 +1502,18 @@ struct db_query db_sqlite3_queries[] = { .placeholders = 3, .readonly = false, }, + { + .name = "DELETE FROM channel_blockheights WHERE channel_id=?", + .query = "DELETE FROM channel_blockheights WHERE channel_id=?", + .placeholders = 1, + .readonly = false, + }, + { + .name = "INSERT INTO channel_blockheights VALUES(?, ?, ?)", + .query = "INSERT INTO channel_blockheights VALUES(?, ?, ?)", + .placeholders = 3, + .readonly = false, + }, { .name = "UPDATE channels SET last_sent_commit=? WHERE id=?", .query = "UPDATE channels SET last_sent_commit=? WHERE id=?", @@ -1918,10 +2020,10 @@ struct db_query db_sqlite3_queries[] = { }, }; -#define DB_SQLITE3_QUERY_COUNT 318 +#define DB_SQLITE3_QUERY_COUNT 335 #endif /* HAVE_SQLITE3 */ #endif /* LIGHTNINGD_WALLET_GEN_DB_SQLITE3 */ -// SHA256STAMP:1379bcdee314439910fc6b238f7ec986536543c53933883ffd1b750dfc34f9b9 +// SHA256STAMP:faf897ed266e0365697865648649c2107997bdf9923a8062b0256d314f7ff6db diff --git a/wallet/reservation.c b/wallet/reservation.c index 7ca74557dccb..84e5e3beef07 100644 --- a/wallet/reservation.c +++ b/wallet/reservation.c @@ -287,7 +287,7 @@ static struct wally_psbt *psbt_using_utxos(const tal_t *ctx, */ if (utxos[i]->close_info && utxos[i]->close_info->option_anchor_outputs) - this_nsequence = 1; + this_nsequence = utxos[i]->close_info->csv; else this_nsequence = nsequence; @@ -682,6 +682,15 @@ static struct command_result *json_utxopsbt(struct command *cmd, struct bitcoin_txid, &utxo->txid), utxo->outnum); + if (utxo_is_csv_locked(utxo, current_height)) + return command_fail(cmd, JSONRPC2_INVALID_PARAMS, + "UTXO %s:%u is csv locked (%u)", + type_to_string(tmpctx, + struct bitcoin_txid, + &utxo->txid), + utxo->outnum, + utxo->close_info->csv); + /* It supplies more input. */ if (!amount_sat_add(&input, input, utxo->amount)) diff --git a/wallet/statements_gettextgen.po b/wallet/statements_gettextgen.po index 27896c8b6d17..1ef224ba153b 100644 --- a/wallet/statements_gettextgen.po +++ b/wallet/statements_gettextgen.po @@ -1,712 +1,764 @@ -#: wallet/db.c:69 +#: wallet/db.c:73 msgid "CREATE TABLE version (version INTEGER)" msgstr "" -#: wallet/db.c:70 +#: wallet/db.c:74 msgid "INSERT INTO version VALUES (1)" msgstr "" -#: wallet/db.c:71 +#: wallet/db.c:75 msgid "CREATE TABLE outputs ( prev_out_tx BLOB, prev_out_index INTEGER, value BIGINT, type INTEGER, status INTEGER, keyindex INTEGER, PRIMARY KEY (prev_out_tx, prev_out_index));" msgstr "" -#: wallet/db.c:80 +#: wallet/db.c:84 msgid "CREATE TABLE vars ( name VARCHAR(32), val VARCHAR(255), PRIMARY KEY (name));" msgstr "" -#: wallet/db.c:86 +#: wallet/db.c:90 msgid "CREATE TABLE shachains ( id BIGSERIAL, min_index BIGINT, num_valid BIGINT, PRIMARY KEY (id));" msgstr "" -#: wallet/db.c:93 +#: wallet/db.c:97 msgid "CREATE TABLE shachain_known ( shachain_id BIGINT REFERENCES shachains(id) ON DELETE CASCADE, pos INTEGER, idx BIGINT, hash BLOB, PRIMARY KEY (shachain_id, pos));" msgstr "" -#: wallet/db.c:101 +#: wallet/db.c:105 msgid "CREATE TABLE peers ( id BIGSERIAL, node_id BLOB UNIQUE, address TEXT, PRIMARY KEY (id));" msgstr "" -#: wallet/db.c:108 +#: wallet/db.c:112 msgid "CREATE TABLE channels ( id BIGSERIAL, peer_id BIGINT REFERENCES peers(id) ON DELETE CASCADE, short_channel_id TEXT, channel_config_local BIGINT, channel_config_remote BIGINT, state INTEGER, funder INTEGER, channel_flags INTEGER, minimum_depth INTEGER, next_index_local BIGINT, next_index_remote BIGINT, next_htlc_id BIGINT, funding_tx_id BLOB, funding_tx_outnum INTEGER, funding_satoshi BIGINT, funding_locked_remote INTEGER, push_msatoshi BIGINT, msatoshi_local BIGINT, fundingkey_remote BLOB, revocation_basepoint_remote BLOB, payment_basepoint_remote BLOB, htlc_basepoint_remote BLOB, delayed_payment_basepoint_remote BLOB, per_commit_remote BLOB, old_per_commit_remote BLOB, local_feerate_per_kw INTEGER, remote_feerate_per_kw INTEGER, shachain_remote_id BIGINT, shutdown_scriptpubkey_remote BLOB, shutdown_keyidx_local BIGINT, last_sent_commit_state BIGINT, last_sent_commit_id INTEGER, last_tx BLOB, last_sig BLOB, closing_fee_received INTEGER, closing_sig_received BLOB, PRIMARY KEY (id));" msgstr "" -#: wallet/db.c:150 +#: wallet/db.c:154 msgid "CREATE TABLE channel_configs ( id BIGSERIAL, dust_limit_satoshis BIGINT, max_htlc_value_in_flight_msat BIGINT, channel_reserve_satoshis BIGINT, htlc_minimum_msat BIGINT, to_self_delay INTEGER, max_accepted_htlcs INTEGER, PRIMARY KEY (id));" msgstr "" -#: wallet/db.c:161 +#: wallet/db.c:165 msgid "CREATE TABLE channel_htlcs ( id BIGSERIAL, channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, channel_htlc_id BIGINT, direction INTEGER, origin_htlc BIGINT, msatoshi BIGINT, cltv_expiry INTEGER, payment_hash BLOB, payment_key BLOB, routing_onion BLOB, failuremsg BLOB, malformed_onion INTEGER, hstate INTEGER, shared_secret BLOB, PRIMARY KEY (id), UNIQUE (channel_id, channel_htlc_id, direction));" msgstr "" -#: wallet/db.c:181 +#: wallet/db.c:185 msgid "CREATE TABLE invoices ( id BIGSERIAL, state INTEGER, msatoshi BIGINT, payment_hash BLOB, payment_key BLOB, label TEXT, PRIMARY KEY (id), UNIQUE (label), UNIQUE (payment_hash));" msgstr "" -#: wallet/db.c:193 +#: wallet/db.c:197 msgid "CREATE TABLE payments ( id BIGSERIAL, timestamp INTEGER, status INTEGER, payment_hash BLOB, direction INTEGER, destination BLOB, msatoshi BIGINT, PRIMARY KEY (id), UNIQUE (payment_hash));" msgstr "" -#: wallet/db.c:206 +#: wallet/db.c:210 msgid "ALTER TABLE invoices ADD expiry_time BIGINT;" msgstr "" -#: wallet/db.c:207 +#: wallet/db.c:211 msgid "UPDATE invoices SET expiry_time=9223372036854775807;" msgstr "" -#: wallet/db.c:209 +#: wallet/db.c:213 msgid "ALTER TABLE invoices ADD pay_index BIGINT;" msgstr "" -#: wallet/db.c:210 +#: wallet/db.c:214 msgid "CREATE UNIQUE INDEX invoices_pay_index ON invoices(pay_index);" msgstr "" -#: wallet/db.c:212 +#: wallet/db.c:216 msgid "UPDATE invoices SET pay_index=id WHERE state=1;" msgstr "" -#: wallet/db.c:215 +#: wallet/db.c:219 msgid "INSERT INTO vars(name, val) VALUES('next_pay_index', COALESCE((SELECT MAX(pay_index) FROM invoices WHERE state=1), 0) + 1 );" msgstr "" -#: wallet/db.c:224 +#: wallet/db.c:228 msgid "ALTER TABLE channels ADD first_blocknum BIGINT;" msgstr "" -#: wallet/db.c:225 +#: wallet/db.c:229 msgid "UPDATE channels SET first_blocknum=1 WHERE short_channel_id IS NOT NULL;" msgstr "" -#: wallet/db.c:227 +#: wallet/db.c:231 msgid "ALTER TABLE outputs ADD COLUMN channel_id BIGINT;" msgstr "" -#: wallet/db.c:228 +#: wallet/db.c:232 msgid "ALTER TABLE outputs ADD COLUMN peer_id BLOB;" msgstr "" -#: wallet/db.c:229 +#: wallet/db.c:233 msgid "ALTER TABLE outputs ADD COLUMN commitment_point BLOB;" msgstr "" -#: wallet/db.c:230 +#: wallet/db.c:234 msgid "ALTER TABLE invoices ADD COLUMN msatoshi_received BIGINT;" msgstr "" -#: wallet/db.c:232 +#: wallet/db.c:236 msgid "UPDATE invoices SET msatoshi_received=0 WHERE state=1;" msgstr "" -#: wallet/db.c:233 +#: wallet/db.c:237 msgid "ALTER TABLE channels ADD COLUMN last_was_revoke INTEGER;" msgstr "" -#: wallet/db.c:237 wallet/db.c:530 +#: wallet/db.c:241 wallet/db.c:534 msgid "ALTER TABLE payments RENAME TO temp_payments;" msgstr "" -#: wallet/db.c:238 +#: wallet/db.c:242 msgid "CREATE TABLE payments ( id BIGSERIAL, timestamp INTEGER, status INTEGER, payment_hash BLOB, destination BLOB, msatoshi BIGINT, PRIMARY KEY (id), UNIQUE (payment_hash));" msgstr "" -#: wallet/db.c:249 +#: wallet/db.c:253 msgid "INSERT INTO payments SELECT id, timestamp, status, payment_hash, destination, msatoshi FROM temp_payments WHERE direction=1;" msgstr "" -#: wallet/db.c:252 wallet/db.c:605 +#: wallet/db.c:256 wallet/db.c:609 msgid "DROP TABLE temp_payments;" msgstr "" -#: wallet/db.c:254 +#: wallet/db.c:258 msgid "ALTER TABLE payments ADD COLUMN payment_preimage BLOB;" msgstr "" -#: wallet/db.c:256 +#: wallet/db.c:260 msgid "ALTER TABLE payments ADD COLUMN path_secrets BLOB;" msgstr "" -#: wallet/db.c:259 +#: wallet/db.c:263 msgid "ALTER TABLE invoices ADD paid_timestamp BIGINT;" msgstr "" -#: wallet/db.c:260 +#: wallet/db.c:264 msgid "UPDATE invoices SET paid_timestamp = CURRENT_TIMESTAMP() WHERE state = 1;" msgstr "" -#: wallet/db.c:268 +#: wallet/db.c:272 msgid "ALTER TABLE payments ADD COLUMN route_nodes BLOB;" msgstr "" -#: wallet/db.c:269 +#: wallet/db.c:273 msgid "ALTER TABLE payments ADD COLUMN route_channels BLOB;" msgstr "" -#: wallet/db.c:270 +#: wallet/db.c:274 msgid "CREATE TABLE htlc_sigs (channelid INTEGER REFERENCES channels(id) ON DELETE CASCADE, signature BLOB);" msgstr "" -#: wallet/db.c:273 +#: wallet/db.c:277 msgid "CREATE INDEX channel_idx ON htlc_sigs (channelid)" msgstr "" -#: wallet/db.c:275 +#: wallet/db.c:279 msgid "DELETE FROM channels WHERE state=1" msgstr "" -#: wallet/db.c:277 +#: wallet/db.c:281 msgid "CREATE TABLE db_upgrades (upgrade_from INTEGER, lightning_version TEXT);" msgstr "" -#: wallet/db.c:281 wallet/db.c:471 +#: wallet/db.c:285 wallet/db.c:475 msgid "DELETE FROM peers WHERE id NOT IN (SELECT peer_id FROM channels);" msgstr "" -#: wallet/db.c:285 +#: wallet/db.c:289 msgid "UPDATE channels SET STATE = 8 WHERE state > 8;" msgstr "" -#: wallet/db.c:287 +#: wallet/db.c:291 msgid "ALTER TABLE invoices ADD bolt11 TEXT;" msgstr "" -#: wallet/db.c:291 +#: wallet/db.c:295 msgid "CREATE TABLE blocks (height INT, hash BLOB, prev_hash BLOB, UNIQUE(height));" msgstr "" -#: wallet/db.c:301 +#: wallet/db.c:305 msgid "ALTER TABLE outputs ADD COLUMN confirmation_height INTEGER REFERENCES blocks(height) ON DELETE SET NULL;" msgstr "" -#: wallet/db.c:304 +#: wallet/db.c:308 msgid "ALTER TABLE outputs ADD COLUMN spend_height INTEGER REFERENCES blocks(height) ON DELETE SET NULL;" msgstr "" -#: wallet/db.c:308 +#: wallet/db.c:312 msgid "CREATE INDEX output_height_idx ON outputs (confirmation_height, spend_height);" msgstr "" -#: wallet/db.c:311 +#: wallet/db.c:315 msgid "CREATE TABLE utxoset ( txid BLOB, outnum INT, blockheight INT REFERENCES blocks(height) ON DELETE CASCADE, spendheight INT REFERENCES blocks(height) ON DELETE SET NULL, txindex INT, scriptpubkey BLOB, satoshis BIGINT, PRIMARY KEY(txid, outnum));" msgstr "" -#: wallet/db.c:321 +#: wallet/db.c:325 msgid "CREATE INDEX short_channel_id ON utxoset (blockheight, txindex, outnum)" msgstr "" -#: wallet/db.c:326 +#: wallet/db.c:330 msgid "CREATE INDEX utxoset_spend ON utxoset (spendheight)" msgstr "" -#: wallet/db.c:328 +#: wallet/db.c:332 msgid "UPDATE channels SET shutdown_keyidx_local=0 WHERE shutdown_keyidx_local = -1;" msgstr "" -#: wallet/db.c:334 +#: wallet/db.c:338 msgid "ALTER TABLE payments ADD failonionreply BLOB;" msgstr "" -#: wallet/db.c:336 +#: wallet/db.c:340 msgid "ALTER TABLE payments ADD faildestperm INTEGER;" msgstr "" -#: wallet/db.c:338 +#: wallet/db.c:342 msgid "ALTER TABLE payments ADD failindex INTEGER;" msgstr "" -#: wallet/db.c:340 +#: wallet/db.c:344 msgid "ALTER TABLE payments ADD failcode INTEGER;" msgstr "" -#: wallet/db.c:341 +#: wallet/db.c:345 msgid "ALTER TABLE payments ADD failnode BLOB;" msgstr "" -#: wallet/db.c:342 +#: wallet/db.c:346 msgid "ALTER TABLE payments ADD failchannel TEXT;" msgstr "" -#: wallet/db.c:344 +#: wallet/db.c:348 msgid "ALTER TABLE payments ADD failupdate BLOB;" msgstr "" -#: wallet/db.c:348 +#: wallet/db.c:352 msgid "UPDATE payments SET path_secrets = NULL , route_nodes = NULL , route_channels = NULL WHERE status <> 0;" msgstr "" -#: wallet/db.c:355 +#: wallet/db.c:359 msgid "ALTER TABLE channels ADD in_payments_offered INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:356 +#: wallet/db.c:360 msgid "ALTER TABLE channels ADD in_payments_fulfilled INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:357 +#: wallet/db.c:361 msgid "ALTER TABLE channels ADD in_msatoshi_offered BIGINT DEFAULT 0;" msgstr "" -#: wallet/db.c:358 +#: wallet/db.c:362 msgid "ALTER TABLE channels ADD in_msatoshi_fulfilled BIGINT DEFAULT 0;" msgstr "" -#: wallet/db.c:359 +#: wallet/db.c:363 msgid "ALTER TABLE channels ADD out_payments_offered INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:360 +#: wallet/db.c:364 msgid "ALTER TABLE channels ADD out_payments_fulfilled INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:361 +#: wallet/db.c:365 msgid "ALTER TABLE channels ADD out_msatoshi_offered BIGINT DEFAULT 0;" msgstr "" -#: wallet/db.c:362 +#: wallet/db.c:366 msgid "ALTER TABLE channels ADD out_msatoshi_fulfilled BIGINT DEFAULT 0;" msgstr "" -#: wallet/db.c:363 +#: wallet/db.c:367 msgid "UPDATE channels SET in_payments_offered = 0, in_payments_fulfilled = 0 , in_msatoshi_offered = 0, in_msatoshi_fulfilled = 0 , out_payments_offered = 0, out_payments_fulfilled = 0 , out_msatoshi_offered = 0, out_msatoshi_fulfilled = 0 ;" msgstr "" -#: wallet/db.c:372 +#: wallet/db.c:376 msgid "ALTER TABLE payments ADD msatoshi_sent BIGINT;" msgstr "" -#: wallet/db.c:373 +#: wallet/db.c:377 msgid "UPDATE payments SET msatoshi_sent = msatoshi;" msgstr "" -#: wallet/db.c:375 +#: wallet/db.c:379 msgid "DELETE FROM utxoset WHERE blockheight IN ( SELECT DISTINCT(blockheight) FROM utxoset LEFT OUTER JOIN blocks on (blockheight = blocks.height) WHERE blocks.hash IS NULL);" msgstr "" -#: wallet/db.c:383 +#: wallet/db.c:387 msgid "ALTER TABLE channels ADD min_possible_feerate INTEGER;" msgstr "" -#: wallet/db.c:384 +#: wallet/db.c:388 msgid "ALTER TABLE channels ADD max_possible_feerate INTEGER;" msgstr "" -#: wallet/db.c:387 +#: wallet/db.c:391 msgid "UPDATE channels SET min_possible_feerate=0, max_possible_feerate=250000;" msgstr "" -#: wallet/db.c:391 +#: wallet/db.c:395 msgid "ALTER TABLE channels ADD msatoshi_to_us_min BIGINT;" msgstr "" -#: wallet/db.c:392 +#: wallet/db.c:396 msgid "ALTER TABLE channels ADD msatoshi_to_us_max BIGINT;" msgstr "" -#: wallet/db.c:393 +#: wallet/db.c:397 msgid "UPDATE channels SET msatoshi_to_us_min = msatoshi_local , msatoshi_to_us_max = msatoshi_local ;" msgstr "" -#: wallet/db.c:402 +#: wallet/db.c:406 msgid "CREATE TABLE transactions ( id BLOB, blockheight INTEGER REFERENCES blocks(height) ON DELETE SET NULL, txindex INTEGER, rawtx BLOB, PRIMARY KEY (id));" msgstr "" -#: wallet/db.c:411 +#: wallet/db.c:415 msgid "ALTER TABLE payments ADD faildetail TEXT;" msgstr "" -#: wallet/db.c:412 +#: wallet/db.c:416 msgid "UPDATE payments SET faildetail = 'unspecified payment failure reason' WHERE status = 2;" msgstr "" -#: wallet/db.c:417 +#: wallet/db.c:421 msgid "CREATE TABLE channeltxs ( id BIGSERIAL, channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, type INTEGER, transaction_id BLOB REFERENCES transactions(id) ON DELETE CASCADE, input_num INTEGER, blockheight INTEGER REFERENCES blocks(height) ON DELETE CASCADE, PRIMARY KEY(id));" msgstr "" -#: wallet/db.c:433 +#: wallet/db.c:437 msgid "DELETE FROM blocks WHERE height > (SELECT MIN(first_blocknum) FROM channels);" msgstr "" -#: wallet/db.c:439 +#: wallet/db.c:443 msgid "INSERT INTO blocks (height) VALUES ((SELECT MIN(first_blocknum) FROM channels)) ON CONFLICT(height) DO NOTHING;" msgstr "" -#: wallet/db.c:443 +#: wallet/db.c:447 msgid "DELETE FROM blocks WHERE height IS NULL;" msgstr "" -#: wallet/db.c:445 +#: wallet/db.c:449 msgid "ALTER TABLE invoices ADD description TEXT;" msgstr "" -#: wallet/db.c:447 +#: wallet/db.c:451 msgid "ALTER TABLE payments ADD description TEXT;" msgstr "" -#: wallet/db.c:449 +#: wallet/db.c:453 msgid "ALTER TABLE channels ADD future_per_commitment_point BLOB;" msgstr "" -#: wallet/db.c:451 +#: wallet/db.c:455 msgid "ALTER TABLE channels ADD last_sent_commit BLOB;" msgstr "" -#: wallet/db.c:456 +#: wallet/db.c:460 msgid "CREATE TABLE forwarded_payments ( in_htlc_id BIGINT REFERENCES channel_htlcs(id) ON DELETE SET NULL, out_htlc_id BIGINT REFERENCES channel_htlcs(id) ON DELETE SET NULL, in_channel_scid BIGINT, out_channel_scid BIGINT, in_msatoshi BIGINT, out_msatoshi BIGINT, state INTEGER, UNIQUE(in_htlc_id, out_htlc_id));" msgstr "" -#: wallet/db.c:468 +#: wallet/db.c:472 msgid "ALTER TABLE payments ADD faildirection INTEGER;" msgstr "" -#: wallet/db.c:473 +#: wallet/db.c:477 msgid "ALTER TABLE outputs ADD scriptpubkey BLOB;" msgstr "" -#: wallet/db.c:475 +#: wallet/db.c:479 msgid "ALTER TABLE payments ADD bolt11 TEXT;" msgstr "" -#: wallet/db.c:477 +#: wallet/db.c:481 msgid "ALTER TABLE channels ADD feerate_base INTEGER;" msgstr "" -#: wallet/db.c:478 +#: wallet/db.c:482 msgid "ALTER TABLE channels ADD feerate_ppm INTEGER;" msgstr "" -#: wallet/db.c:480 +#: wallet/db.c:484 msgid "ALTER TABLE channel_htlcs ADD received_time BIGINT" msgstr "" -#: wallet/db.c:481 +#: wallet/db.c:485 msgid "ALTER TABLE forwarded_payments ADD received_time BIGINT" msgstr "" -#: wallet/db.c:482 +#: wallet/db.c:486 msgid "ALTER TABLE forwarded_payments ADD resolved_time BIGINT" msgstr "" -#: wallet/db.c:483 +#: wallet/db.c:487 msgid "ALTER TABLE channels ADD remote_upfront_shutdown_script BLOB;" msgstr "" -#: wallet/db.c:486 +#: wallet/db.c:490 msgid "ALTER TABLE forwarded_payments ADD failcode INTEGER;" msgstr "" -#: wallet/db.c:488 +#: wallet/db.c:492 msgid "ALTER TABLE channels ADD remote_ann_node_sig BLOB;" msgstr "" -#: wallet/db.c:489 +#: wallet/db.c:493 msgid "ALTER TABLE channels ADD remote_ann_bitcoin_sig BLOB;" msgstr "" -#: wallet/db.c:491 +#: wallet/db.c:495 msgid "ALTER TABLE transactions ADD type BIGINT;" msgstr "" -#: wallet/db.c:496 +#: wallet/db.c:500 msgid "ALTER TABLE transactions ADD channel_id BIGINT;" msgstr "" -#: wallet/db.c:498 +#: wallet/db.c:502 msgid "UPDATE channels SET short_channel_id = REPLACE(short_channel_id, ':', 'x') WHERE short_channel_id IS NOT NULL;" msgstr "" -#: wallet/db.c:501 +#: wallet/db.c:505 msgid "UPDATE payments SET failchannel = REPLACE(failchannel, ':', 'x') WHERE failchannel IS NOT NULL;" msgstr "" -#: wallet/db.c:504 +#: wallet/db.c:508 msgid "ALTER TABLE channels ADD COLUMN option_static_remotekey INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:506 +#: wallet/db.c:510 msgid "ALTER TABLE vars ADD COLUMN intval INTEGER" msgstr "" -#: wallet/db.c:507 +#: wallet/db.c:511 msgid "ALTER TABLE vars ADD COLUMN blobval BLOB" msgstr "" -#: wallet/db.c:508 +#: wallet/db.c:512 msgid "UPDATE vars SET intval = CAST(val AS INTEGER) WHERE name IN ('bip32_max_index', 'last_processed_block', 'next_pay_index')" msgstr "" -#: wallet/db.c:509 +#: wallet/db.c:513 msgid "UPDATE vars SET blobval = CAST(val AS BLOB) WHERE name = 'genesis_hash'" msgstr "" -#: wallet/db.c:510 +#: wallet/db.c:514 msgid "CREATE TABLE transaction_annotations ( txid BLOB, idx INTEGER, location INTEGER, type INTEGER, channel BIGINT REFERENCES channels(id), UNIQUE(txid, idx));" msgstr "" -#: wallet/db.c:522 +#: wallet/db.c:526 msgid "ALTER TABLE channels ADD shutdown_scriptpubkey_local BLOB;" msgstr "" -#: wallet/db.c:525 +#: wallet/db.c:529 msgid "UPDATE forwarded_payments SET received_time=0 WHERE received_time IS NULL;" msgstr "" -#: wallet/db.c:527 +#: wallet/db.c:531 msgid "ALTER TABLE invoices ADD COLUMN features BLOB DEFAULT '';" msgstr "" -#: wallet/db.c:531 +#: wallet/db.c:535 msgid "CREATE TABLE payments ( id BIGSERIAL, timestamp INTEGER, status INTEGER, payment_hash BLOB, destination BLOB, msatoshi BIGINT, payment_preimage BLOB, path_secrets BLOB, route_nodes BLOB, route_channels BLOB, failonionreply BLOB, faildestperm INTEGER, failindex INTEGER, failcode INTEGER, failnode BLOB, failchannel TEXT, failupdate BLOB, msatoshi_sent BIGINT, faildetail TEXT, description TEXT, faildirection INTEGER, bolt11 TEXT, total_msat BIGINT, partid BIGINT, PRIMARY KEY (id), UNIQUE (payment_hash, partid))" msgstr "" -#: wallet/db.c:558 +#: wallet/db.c:562 msgid "INSERT INTO payments (id, timestamp, status, payment_hash, destination, msatoshi, payment_preimage, path_secrets, route_nodes, route_channels, failonionreply, faildestperm, failindex, failcode, failnode, failchannel, failupdate, msatoshi_sent, faildetail, description, faildirection, bolt11)SELECT id, timestamp, status, payment_hash, destination, msatoshi, payment_preimage, path_secrets, route_nodes, route_channels, failonionreply, faildestperm, failindex, failcode, failnode, failchannel, failupdate, msatoshi_sent, faildetail, description, faildirection, bolt11 FROM temp_payments;" msgstr "" -#: wallet/db.c:603 +#: wallet/db.c:607 msgid "UPDATE payments SET total_msat = msatoshi;" msgstr "" -#: wallet/db.c:604 +#: wallet/db.c:608 msgid "UPDATE payments SET partid = 0;" msgstr "" -#: wallet/db.c:606 +#: wallet/db.c:610 msgid "ALTER TABLE channel_htlcs ADD partid BIGINT;" msgstr "" -#: wallet/db.c:607 +#: wallet/db.c:611 msgid "UPDATE channel_htlcs SET partid = 0;" msgstr "" -#: wallet/db.c:608 +#: wallet/db.c:612 msgid "CREATE TABLE channel_feerates ( channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, hstate INTEGER, feerate_per_kw INTEGER, UNIQUE (channel_id, hstate));" msgstr "" -#: wallet/db.c:619 +#: wallet/db.c:623 msgid "INSERT INTO channel_feerates(channel_id, hstate, feerate_per_kw) SELECT id, 4, local_feerate_per_kw FROM channels WHERE funder = 0;" msgstr "" -#: wallet/db.c:623 +#: wallet/db.c:627 msgid "INSERT INTO channel_feerates(channel_id, hstate, feerate_per_kw) SELECT id, 1, remote_feerate_per_kw FROM channels WHERE funder = 0 and local_feerate_per_kw != remote_feerate_per_kw;" msgstr "" -#: wallet/db.c:628 +#: wallet/db.c:632 msgid "INSERT INTO channel_feerates(channel_id, hstate, feerate_per_kw) SELECT id, 14, remote_feerate_per_kw FROM channels WHERE funder = 1;" msgstr "" -#: wallet/db.c:632 +#: wallet/db.c:636 msgid "INSERT INTO channel_feerates(channel_id, hstate, feerate_per_kw) SELECT id, 11, local_feerate_per_kw FROM channels WHERE funder = 1 and local_feerate_per_kw != remote_feerate_per_kw;" msgstr "" -#: wallet/db.c:636 +#: wallet/db.c:640 msgid "INSERT INTO vars (name, intval) VALUES ('data_version', 0);" msgstr "" -#: wallet/db.c:639 +#: wallet/db.c:643 msgid "ALTER TABLE channel_htlcs ADD localfailmsg BLOB;" msgstr "" -#: wallet/db.c:640 +#: wallet/db.c:644 msgid "UPDATE channel_htlcs SET localfailmsg=decode('2002', 'hex') WHERE malformed_onion != 0 AND direction = 1;" msgstr "" -#: wallet/db.c:641 +#: wallet/db.c:645 msgid "ALTER TABLE channels ADD our_funding_satoshi BIGINT DEFAULT 0;" msgstr "" -#: wallet/db.c:642 +#: wallet/db.c:646 msgid "CREATE TABLE penalty_bases ( channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, commitnum BIGINT, txid BLOB, outnum INTEGER, amount BIGINT, PRIMARY KEY (channel_id, commitnum));" msgstr "" -#: wallet/db.c:652 +#: wallet/db.c:656 msgid "ALTER TABLE channel_htlcs ADD we_filled INTEGER;" msgstr "" -#: wallet/db.c:654 +#: wallet/db.c:658 msgid "INSERT INTO vars (name, intval) VALUES ('coin_moves_count', 0);" msgstr "" -#: wallet/db.c:656 +#: wallet/db.c:660 msgid "ALTER TABLE outputs ADD reserved_til INTEGER DEFAULT NULL;" msgstr "" -#: wallet/db.c:659 +#: wallet/db.c:663 msgid "ALTER TABLE channels ADD COLUMN option_anchor_outputs INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:662 +#: wallet/db.c:666 msgid "ALTER TABLE outputs ADD option_anchor_outputs INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:664 +#: wallet/db.c:668 msgid "ALTER TABLE channels ADD full_channel_id BLOB DEFAULT NULL;" msgstr "" -#: wallet/db.c:665 +#: wallet/db.c:669 msgid "ALTER TABLE channels ADD funding_psbt BLOB DEFAULT NULL;" msgstr "" -#: wallet/db.c:667 +#: wallet/db.c:671 msgid "ALTER TABLE channels ADD closer INTEGER DEFAULT 2;" msgstr "" -#: wallet/db.c:668 +#: wallet/db.c:672 msgid "ALTER TABLE channels ADD state_change_reason INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:669 +#: wallet/db.c:673 msgid "CREATE TABLE channel_state_changes ( channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, timestamp BIGINT, old_state INTEGER, new_state INTEGER, cause INTEGER, message TEXT);" msgstr "" -#: wallet/db.c:677 +#: wallet/db.c:681 msgid "CREATE TABLE offers ( offer_id BLOB, bolt12 TEXT, label TEXT, status INTEGER, PRIMARY KEY (offer_id));" msgstr "" -#: wallet/db.c:685 +#: wallet/db.c:689 msgid "ALTER TABLE invoices ADD COLUMN local_offer_id BLOB DEFAULT NULL REFERENCES offers(offer_id);" msgstr "" -#: wallet/db.c:687 +#: wallet/db.c:691 msgid "ALTER TABLE payments ADD COLUMN local_offer_id BLOB DEFAULT NULL REFERENCES offers(offer_id);" msgstr "" -#: wallet/db.c:688 +#: wallet/db.c:692 msgid "ALTER TABLE channels ADD funding_tx_remote_sigs_received INTEGER DEFAULT 0;" msgstr "" -#: wallet/db.c:691 +#: wallet/db.c:695 msgid "CREATE INDEX forwarded_payments_out_htlc_id ON forwarded_payments (out_htlc_id);" msgstr "" -#: wallet/db.c:693 +#: wallet/db.c:697 msgid "UPDATE channel_htlcs SET malformed_onion = 0 WHERE malformed_onion IS NULL" msgstr "" -#: wallet/db.c:695 +#: wallet/db.c:699 msgid "CREATE INDEX forwarded_payments_state ON forwarded_payments (state)" msgstr "" -#: wallet/db.c:696 +#: wallet/db.c:700 msgid "CREATE TABLE channel_funding_inflights ( channel_id BIGSERIAL REFERENCES channels(id) ON DELETE CASCADE, funding_tx_id BLOB, funding_tx_outnum INTEGER, funding_feerate INTEGER, funding_satoshi BIGINT, our_funding_satoshi BIGINT, funding_psbt BLOB, last_tx BLOB, last_sig BLOB, funding_tx_remote_sigs_received INTEGER, PRIMARY KEY (channel_id, funding_tx_id));" msgstr "" -#: wallet/db.c:710 +#: wallet/db.c:714 msgid "ALTER TABLE channels ADD revocation_basepoint_local BLOB" msgstr "" -#: wallet/db.c:711 +#: wallet/db.c:715 msgid "ALTER TABLE channels ADD payment_basepoint_local BLOB" msgstr "" -#: wallet/db.c:712 +#: wallet/db.c:716 msgid "ALTER TABLE channels ADD htlc_basepoint_local BLOB" msgstr "" -#: wallet/db.c:713 +#: wallet/db.c:717 msgid "ALTER TABLE channels ADD delayed_payment_basepoint_local BLOB" msgstr "" -#: wallet/db.c:714 +#: wallet/db.c:718 msgid "ALTER TABLE channels ADD funding_pubkey_local BLOB" msgstr "" -#: wallet/db.c:717 +#: wallet/db.c:721 msgid "ALTER TABLE channels ADD shutdown_wrong_txid BLOB DEFAULT NULL" msgstr "" -#: wallet/db.c:718 +#: wallet/db.c:722 msgid "ALTER TABLE channels ADD shutdown_wrong_outnum INTEGER DEFAULT NULL" msgstr "" -#: wallet/db.c:721 +#: wallet/db.c:725 msgid "ALTER TABLE channels ADD local_static_remotekey_start BIGINT DEFAULT 0" msgstr "" -#: wallet/db.c:723 +#: wallet/db.c:727 msgid "ALTER TABLE channels ADD remote_static_remotekey_start BIGINT DEFAULT 0" msgstr "" -#: wallet/db.c:726 +#: wallet/db.c:730 msgid "UPDATE channels SET remote_static_remotekey_start = 9223372036854775807, local_static_remotekey_start = 9223372036854775807 WHERE option_static_remotekey = 0" msgstr "" -#: wallet/db.c:957 +#: wallet/db.c:735 +msgid "ALTER TABLE channel_funding_inflights ADD lease_commit_sig BLOB DEFAULT NULL" +msgstr "" + +#: wallet/db.c:736 +msgid "ALTER TABLE channel_funding_inflights ADD lease_chan_max_msat BIGINT DEFAULT NULL" +msgstr "" + +#: wallet/db.c:737 +msgid "ALTER TABLE channel_funding_inflights ADD lease_chan_max_ppt INTEGER DEFAULT NULL" +msgstr "" + +#: wallet/db.c:738 +msgid "ALTER TABLE channel_funding_inflights ADD lease_expiry INTEGER DEFAULT 0" +msgstr "" + +#: wallet/db.c:739 +msgid "ALTER TABLE channel_funding_inflights ADD lease_blockheight_start INTEGER DEFAULT 0" +msgstr "" + +#: wallet/db.c:740 +msgid "ALTER TABLE channels ADD lease_commit_sig BLOB DEFAULT NULL" +msgstr "" + +#: wallet/db.c:741 +msgid "ALTER TABLE channels ADD lease_chan_max_msat INTEGER DEFAULT NULL" +msgstr "" + +#: wallet/db.c:742 +msgid "ALTER TABLE channels ADD lease_chan_max_ppt INTEGER DEFAULT NULL" +msgstr "" + +#: wallet/db.c:743 +msgid "ALTER TABLE channels ADD lease_expiry INTEGER DEFAULT 0" +msgstr "" + +#: wallet/db.c:744 +msgid "CREATE TABLE channel_blockheights ( channel_id BIGINT REFERENCES channels(id) ON DELETE CASCADE, hstate INTEGER, blockheight INTEGER, UNIQUE (channel_id, hstate));" +msgstr "" + +#: wallet/db.c:751 +msgid "ALTER TABLE outputs ADD csv_lock INTEGER DEFAULT 1;" +msgstr "" + +#: wallet/db.c:979 msgid "UPDATE vars SET intval = intval + 1 WHERE name = 'data_version' AND intval = ?" msgstr "" -#: wallet/db.c:1057 +#: wallet/db.c:1079 msgid "SELECT version FROM version LIMIT 1" msgstr "" -#: wallet/db.c:1119 +#: wallet/db.c:1141 msgid "UPDATE version SET version=?;" msgstr "" -#: wallet/db.c:1127 +#: wallet/db.c:1149 msgid "INSERT INTO db_upgrades VALUES (?, ?);" msgstr "" -#: wallet/db.c:1139 +#: wallet/db.c:1161 msgid "SELECT intval FROM vars WHERE name = 'data_version'" msgstr "" -#: wallet/db.c:1166 +#: wallet/db.c:1188 msgid "SELECT intval FROM vars WHERE name= ? LIMIT 1" msgstr "" -#: wallet/db.c:1182 +#: wallet/db.c:1204 msgid "UPDATE vars SET intval=? WHERE name=?;" msgstr "" -#: wallet/db.c:1191 +#: wallet/db.c:1213 msgid "INSERT INTO vars (name, intval) VALUES (?, ?);" msgstr "" -#: wallet/db.c:1205 +#: wallet/db.c:1227 msgid "UPDATE channels SET feerate_base = ?, feerate_ppm = ?;" msgstr "" -#: wallet/db.c:1226 +#: wallet/db.c:1248 msgid "UPDATE channels SET our_funding_satoshi = funding_satoshi WHERE funder = 0;" msgstr "" -#: wallet/db.c:1242 +#: wallet/db.c:1264 msgid "SELECT type, keyindex, prev_out_tx, prev_out_index, channel_id, peer_id, commitment_point FROM outputs WHERE scriptpubkey IS NULL;" msgstr "" -#: wallet/db.c:1304 +#: wallet/db.c:1326 msgid "UPDATE outputs SET scriptpubkey = ? WHERE prev_out_tx = ? AND prev_out_index = ?" msgstr "" -#: wallet/db.c:1329 +#: wallet/db.c:1351 msgid "SELECT id, funding_tx_id, funding_tx_outnum FROM channels;" msgstr "" -#: wallet/db.c:1348 +#: wallet/db.c:1370 msgid "UPDATE channels SET full_channel_id = ? WHERE id = ?;" msgstr "" -#: wallet/db.c:1369 +#: wallet/db.c:1391 msgid "SELECT channels.id, peers.node_id FROM channels JOIN peers ON (peers.id = channels.peer_id)" msgstr "" -#: wallet/db.c:1402 +#: wallet/db.c:1424 msgid "UPDATE channels SET revocation_basepoint_local = ?, payment_basepoint_local = ?, htlc_basepoint_local = ?, delayed_payment_basepoint_local = ?, funding_pubkey_local = ? WHERE id = ?;" msgstr "" -#: wallet/db.c:1428 +#: wallet/db.c:1457 +msgid "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 4, 0 FROM channels WHERE funder = 0;" +msgstr "" + +#: wallet/db.c:1465 +msgid "INSERT INTO channel_blockheights (channel_id, hstate, blockheight) SELECT id, 14, 0 FROM channels WHERE funder = 1;" +msgstr "" + +#: wallet/db.c:1477 msgid "SELECT c.id, p.node_id, c.fundingkey_remote, inflight.last_tx, inflight.last_sig, inflight.funding_satoshi, inflight.funding_tx_id FROM channels c LEFT OUTER JOIN peers p ON p.id = c.peer_id LEFT OUTER JOIN channel_funding_inflights inflight ON c.id = inflight.channel_id WHERE inflight.last_tx IS NOT NULL;" msgstr "" -#: wallet/db.c:1495 +#: wallet/db.c:1544 msgid "UPDATE channel_funding_inflights SET last_tx = ? WHERE channel_id = ? AND funding_tx_id = ?;" msgstr "" -#: wallet/db.c:1519 +#: wallet/db.c:1568 msgid "SELECT c.id, p.node_id, c.last_tx, c.funding_satoshi, c.fundingkey_remote, c.last_sig FROM channels c LEFT OUTER JOIN peers p ON p.id = c.peer_id;" msgstr "" -#: wallet/db.c:1586 +#: wallet/db.c:1635 msgid "UPDATE channels SET last_tx = ? WHERE id = ?;" msgstr "" @@ -770,487 +822,503 @@ msgstr "" msgid "SELECT state, payment_key, payment_hash, label, msatoshi, expiry_time, pay_index, msatoshi_received, paid_timestamp, bolt11, description, features, local_offer_id FROM invoices WHERE id = ?;" msgstr "" -#: wallet/wallet.c:65 +#: wallet/wallet.c:66 msgid "SELECT txid, outnum FROM utxoset WHERE spendheight is NULL" msgstr "" -#: wallet/wallet.c:106 wallet/wallet.c:588 +#: wallet/wallet.c:107 wallet/wallet.c:605 msgid "SELECT * from outputs WHERE prev_out_tx=? AND prev_out_index=?" msgstr "" -#: wallet/wallet.c:120 wallet/wallet.c:602 +#: wallet/wallet.c:121 msgid "INSERT INTO outputs ( prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:237 +#: wallet/wallet.c:239 msgid "UPDATE outputs SET status=? WHERE status=? AND prev_out_tx=? AND prev_out_index=?" msgstr "" -#: wallet/wallet.c:245 +#: wallet/wallet.c:247 msgid "UPDATE outputs SET status=? WHERE prev_out_tx=? AND prev_out_index=?" msgstr "" -#: wallet/wallet.c:264 -msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs" +#: wallet/wallet.c:266 +msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs" msgstr "" -#: wallet/wallet.c:281 -msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status= ? " +#: wallet/wallet.c:284 +msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til , csv_lock FROM outputs WHERE status= ? " msgstr "" -#: wallet/wallet.c:319 -msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL" +#: wallet/wallet.c:323 +msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE channel_id IS NOT NULL AND confirmation_height IS NULL" msgstr "" -#: wallet/wallet.c:356 -msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?" +#: wallet/wallet.c:361 +msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, reserved_til, csv_lock FROM outputs WHERE prev_out_tx = ? AND prev_out_index = ?" msgstr "" -#: wallet/wallet.c:448 +#: wallet/wallet.c:454 msgid "UPDATE outputs SET status=?, reserved_til=? WHERE prev_out_tx=? AND prev_out_index=?" msgstr "" -#: wallet/wallet.c:537 -msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();" +#: wallet/wallet.c:551 +msgid "SELECT prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey , reserved_til, csv_lock FROM outputs WHERE status = ? OR (status = ? AND reserved_til <= ?)ORDER BY RANDOM();" msgstr "" -#: wallet/wallet.c:706 +#: wallet/wallet.c:619 +msgid "INSERT INTO outputs ( prev_out_tx, prev_out_index, value, type, status, keyindex, channel_id, peer_id, commitment_point, option_anchor_outputs, confirmation_height, spend_height, scriptpubkey, csv_lock) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" +msgstr "" + +#: wallet/wallet.c:726 msgid "INSERT INTO shachains (min_index, num_valid) VALUES (?, 0);" msgstr "" -#: wallet/wallet.c:750 +#: wallet/wallet.c:770 msgid "UPDATE shachains SET num_valid=?, min_index=? WHERE id=?" msgstr "" -#: wallet/wallet.c:757 +#: wallet/wallet.c:777 msgid "UPDATE shachain_known SET idx=?, hash=? WHERE shachain_id=? AND pos=?" msgstr "" -#: wallet/wallet.c:769 +#: wallet/wallet.c:789 msgid "INSERT INTO shachain_known (shachain_id, pos, idx, hash) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:791 +#: wallet/wallet.c:811 msgid "SELECT min_index, num_valid FROM shachains WHERE id=?" msgstr "" -#: wallet/wallet.c:806 +#: wallet/wallet.c:826 msgid "SELECT idx, hash, pos FROM shachain_known WHERE shachain_id=?" msgstr "" -#: wallet/wallet.c:829 +#: wallet/wallet.c:849 msgid "SELECT id, node_id, address FROM peers WHERE id=?;" msgstr "" -#: wallet/wallet.c:862 +#: wallet/wallet.c:882 msgid "SELECT signature FROM htlc_sigs WHERE channelid = ?" msgstr "" -#: wallet/wallet.c:896 +#: wallet/wallet.c:916 msgid "SELECT remote_ann_node_sig, remote_ann_bitcoin_sig FROM channels WHERE id = ?" msgstr "" -#: wallet/wallet.c:940 +#: wallet/wallet.c:960 msgid "SELECT hstate, feerate_per_kw FROM channel_feerates WHERE channel_id = ?" msgstr "" -#: wallet/wallet.c:973 -msgid "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);" +#: wallet/wallet.c:996 +msgid "SELECT hstate, blockheight FROM channel_blockheights WHERE channel_id = ?" +msgstr "" + +#: wallet/wallet.c:1029 +msgid "INSERT INTO channel_funding_inflights ( channel_id, funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_expiry, lease_blockheight_start) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:1008 +#: wallet/wallet.c:1085 msgid "UPDATE channel_funding_inflights SET funding_psbt=?, funding_tx_remote_sigs_received=? WHERE channel_id=? AND funding_tx_id=? AND funding_tx_outnum=?" msgstr "" -#: wallet/wallet.c:1031 +#: wallet/wallet.c:1108 msgid "DELETE FROM channel_funding_inflights WHERE channel_id = ?" msgstr "" -#: wallet/wallet.c:1079 -msgid "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate" +#: wallet/wallet.c:1178 +msgid "SELECT funding_tx_id, funding_tx_outnum, funding_feerate, funding_satoshi, our_funding_satoshi, funding_psbt, last_tx, last_sig, funding_tx_remote_sigs_received, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt, lease_blockheight_start FROM channel_funding_inflights WHERE channel_id = ? ORDER BY funding_feerate" msgstr "" -#: wallet/wallet.c:1309 +#: wallet/wallet.c:1446 msgid "SELECT id FROM channels ORDER BY id DESC LIMIT 1;" msgstr "" -#: wallet/wallet.c:1326 -msgid "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum FROM channels WHERE state != ?;" +#: wallet/wallet.c:1463 +msgid "SELECT id, peer_id, short_channel_id, full_channel_id, channel_config_local, channel_config_remote, state, funder, channel_flags, minimum_depth, next_index_local, next_index_remote, next_htlc_id, funding_tx_id, funding_tx_outnum, funding_satoshi, our_funding_satoshi, funding_locked_remote, push_msatoshi, msatoshi_local, fundingkey_remote, revocation_basepoint_remote, payment_basepoint_remote, htlc_basepoint_remote, delayed_payment_basepoint_remote, per_commit_remote, old_per_commit_remote, local_feerate_per_kw, remote_feerate_per_kw, shachain_remote_id, shutdown_scriptpubkey_remote, shutdown_keyidx_local, last_sent_commit_state, last_sent_commit_id, last_tx, last_sig, last_was_revoke, first_blocknum, min_possible_feerate, max_possible_feerate, msatoshi_to_us_min, msatoshi_to_us_max, future_per_commitment_point, last_sent_commit, feerate_base, feerate_ppm, remote_upfront_shutdown_script, local_static_remotekey_start, remote_static_remotekey_start, option_anchor_outputs, shutdown_scriptpubkey_local, closer, state_change_reason, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local, shutdown_wrong_txid, shutdown_wrong_outnum, lease_expiry, lease_commit_sig, lease_chan_max_msat, lease_chan_max_ppt FROM channels WHERE state != ?;" msgstr "" -#: wallet/wallet.c:1434 +#: wallet/wallet.c:1575 msgid "UPDATE channels SET in_payments_offered = COALESCE(in_payments_offered, 0) + 1 , in_msatoshi_offered = COALESCE(in_msatoshi_offered, 0) + ? WHERE id = ?;" msgstr "" -#: wallet/wallet.c:1440 +#: wallet/wallet.c:1581 msgid "UPDATE channels SET in_payments_fulfilled = COALESCE(in_payments_fulfilled, 0) + 1 , in_msatoshi_fulfilled = COALESCE(in_msatoshi_fulfilled, 0) + ? WHERE id = ?;" msgstr "" -#: wallet/wallet.c:1446 +#: wallet/wallet.c:1587 msgid "UPDATE channels SET out_payments_offered = COALESCE(out_payments_offered, 0) + 1 , out_msatoshi_offered = COALESCE(out_msatoshi_offered, 0) + ? WHERE id = ?;" msgstr "" -#: wallet/wallet.c:1452 +#: wallet/wallet.c:1593 msgid "UPDATE channels SET out_payments_fulfilled = COALESCE(out_payments_fulfilled, 0) + 1 , out_msatoshi_fulfilled = COALESCE(out_msatoshi_fulfilled, 0) + ? WHERE id = ?;" msgstr "" -#: wallet/wallet.c:1497 +#: wallet/wallet.c:1638 msgid "SELECT in_payments_offered, in_payments_fulfilled, in_msatoshi_offered, in_msatoshi_fulfilled, out_payments_offered, out_payments_fulfilled, out_msatoshi_offered, out_msatoshi_fulfilled FROM channels WHERE id = ?" msgstr "" -#: wallet/wallet.c:1526 +#: wallet/wallet.c:1667 msgid "SELECT MIN(height), MAX(height) FROM blocks;" msgstr "" -#: wallet/wallet.c:1548 +#: wallet/wallet.c:1689 msgid "INSERT INTO channel_configs DEFAULT VALUES;" msgstr "" -#: wallet/wallet.c:1560 +#: wallet/wallet.c:1701 msgid "UPDATE channel_configs SET dust_limit_satoshis=?, max_htlc_value_in_flight_msat=?, channel_reserve_satoshis=?, htlc_minimum_msat=?, to_self_delay=?, max_accepted_htlcs=? WHERE id=?;" msgstr "" -#: wallet/wallet.c:1584 +#: wallet/wallet.c:1725 msgid "SELECT id, dust_limit_satoshis, max_htlc_value_in_flight_msat, channel_reserve_satoshis, htlc_minimum_msat, to_self_delay, max_accepted_htlcs FROM channel_configs WHERE id= ? ;" msgstr "" -#: wallet/wallet.c:1618 +#: wallet/wallet.c:1759 msgid "UPDATE channels SET remote_ann_node_sig=?, remote_ann_bitcoin_sig=? WHERE id=?" msgstr "" -#: wallet/wallet.c:1637 -msgid "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, local_static_remotekey_start=?, remote_static_remotekey_start=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=? WHERE id=?" +#: wallet/wallet.c:1778 +msgid "UPDATE channels SET shachain_remote_id=?, short_channel_id=?, full_channel_id=?, state=?, funder=?, channel_flags=?, minimum_depth=?, next_index_local=?, next_index_remote=?, next_htlc_id=?, funding_tx_id=?, funding_tx_outnum=?, funding_satoshi=?, our_funding_satoshi=?, funding_locked_remote=?, push_msatoshi=?, msatoshi_local=?, shutdown_scriptpubkey_remote=?, shutdown_keyidx_local=?, channel_config_local=?, last_tx=?, last_sig=?, last_was_revoke=?, min_possible_feerate=?, max_possible_feerate=?, msatoshi_to_us_min=?, msatoshi_to_us_max=?, feerate_base=?, feerate_ppm=?, remote_upfront_shutdown_script=?, local_static_remotekey_start=?, remote_static_remotekey_start=?, option_anchor_outputs=?, shutdown_scriptpubkey_local=?, closer=?, state_change_reason=?, shutdown_wrong_txid=?, shutdown_wrong_outnum=?, lease_expiry=?, lease_commit_sig=?, lease_chan_max_msat=?, lease_chan_max_ppt=? WHERE id=?" msgstr "" -#: wallet/wallet.c:1731 +#: wallet/wallet.c:1887 msgid "UPDATE channels SET fundingkey_remote=?, revocation_basepoint_remote=?, payment_basepoint_remote=?, htlc_basepoint_remote=?, delayed_payment_basepoint_remote=?, per_commit_remote=?, old_per_commit_remote=?, channel_config_remote=?, future_per_commitment_point=? WHERE id=?" msgstr "" -#: wallet/wallet.c:1758 +#: wallet/wallet.c:1914 msgid "DELETE FROM channel_feerates WHERE channel_id=?" msgstr "" -#: wallet/wallet.c:1768 +#: wallet/wallet.c:1924 msgid "INSERT INTO channel_feerates VALUES(?, ?, ?)" msgstr "" -#: wallet/wallet.c:1785 +#: wallet/wallet.c:1933 +msgid "DELETE FROM channel_blockheights WHERE channel_id=?" +msgstr "" + +#: wallet/wallet.c:1943 +msgid "INSERT INTO channel_blockheights VALUES(?, ?, ?)" +msgstr "" + +#: wallet/wallet.c:1960 msgid "UPDATE channels SET last_sent_commit=? WHERE id=?" msgstr "" -#: wallet/wallet.c:1808 +#: wallet/wallet.c:1983 msgid "INSERT INTO channel_state_changes ( channel_id, timestamp, old_state, new_state, cause, message) VALUES (?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:1836 +#: wallet/wallet.c:2011 msgid "SELECT timestamp, old_state, new_state, cause, message FROM channel_state_changes WHERE channel_id = ? ORDER BY timestamp ASC;" msgstr "" -#: wallet/wallet.c:1865 +#: wallet/wallet.c:2040 msgid "SELECT id FROM peers WHERE node_id = ?" msgstr "" -#: wallet/wallet.c:1877 +#: wallet/wallet.c:2052 msgid "UPDATE peers SET address = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:1886 +#: wallet/wallet.c:2061 msgid "INSERT INTO peers (node_id, address) VALUES (?, ?);" msgstr "" -#: wallet/wallet.c:1907 +#: wallet/wallet.c:2082 msgid "INSERT INTO channels ( peer_id, first_blocknum, id, revocation_basepoint_local, payment_basepoint_local, htlc_basepoint_local, delayed_payment_basepoint_local, funding_pubkey_local) VALUES (?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:1948 +#: wallet/wallet.c:2123 msgid "DELETE FROM channel_htlcs WHERE channel_id=?" msgstr "" -#: wallet/wallet.c:1954 +#: wallet/wallet.c:2129 msgid "DELETE FROM htlc_sigs WHERE channelid=?" msgstr "" -#: wallet/wallet.c:1960 +#: wallet/wallet.c:2135 msgid "DELETE FROM channeltxs WHERE channel_id=?" msgstr "" -#: wallet/wallet.c:1967 +#: wallet/wallet.c:2142 msgid "DELETE FROM channel_funding_inflights WHERE channel_id=?" msgstr "" -#: wallet/wallet.c:1973 +#: wallet/wallet.c:2148 msgid "DELETE FROM shachains WHERE id IN ( SELECT shachain_remote_id FROM channels WHERE channels.id=?)" msgstr "" -#: wallet/wallet.c:1983 +#: wallet/wallet.c:2158 msgid "UPDATE channels SET state=?, peer_id=? WHERE channels.id=?" msgstr "" -#: wallet/wallet.c:1997 +#: wallet/wallet.c:2172 msgid "SELECT * FROM channels WHERE peer_id = ?;" msgstr "" -#: wallet/wallet.c:2005 +#: wallet/wallet.c:2180 msgid "DELETE FROM peers WHERE id=?" msgstr "" -#: wallet/wallet.c:2016 +#: wallet/wallet.c:2191 msgid "UPDATE outputs SET confirmation_height = ? WHERE prev_out_tx = ?" msgstr "" -#: wallet/wallet.c:2119 +#: wallet/wallet.c:2294 msgid "INSERT INTO channel_htlcs ( channel_id, channel_htlc_id, direction, msatoshi, cltv_expiry, payment_hash, payment_key, hstate, shared_secret, routing_onion, received_time) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:2172 +#: wallet/wallet.c:2347 msgid "INSERT INTO channel_htlcs ( channel_id, channel_htlc_id, direction, origin_htlc, msatoshi, cltv_expiry, payment_hash, payment_key, hstate, routing_onion, malformed_onion, partid) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, ?);" msgstr "" -#: wallet/wallet.c:2233 +#: wallet/wallet.c:2408 msgid "UPDATE channel_htlcs SET hstate=?, payment_key=?, malformed_onion=?, failuremsg=?, localfailmsg=?, we_filled=? WHERE id=?" msgstr "" -#: wallet/wallet.c:2450 +#: wallet/wallet.c:2625 msgid "SELECT id, channel_htlc_id, msatoshi, cltv_expiry, hstate, payment_hash, payment_key, routing_onion, failuremsg, malformed_onion, origin_htlc, shared_secret, received_time, we_filled FROM channel_htlcs WHERE direction= ? AND channel_id= ? AND hstate != ?" msgstr "" -#: wallet/wallet.c:2497 +#: wallet/wallet.c:2672 msgid "SELECT id, channel_htlc_id, msatoshi, cltv_expiry, hstate, payment_hash, payment_key, routing_onion, failuremsg, malformed_onion, origin_htlc, shared_secret, received_time, partid, localfailmsg FROM channel_htlcs WHERE direction = ? AND channel_id = ? AND hstate != ?" msgstr "" -#: wallet/wallet.c:2628 +#: wallet/wallet.c:2803 msgid "SELECT channel_id, direction, cltv_expiry, channel_htlc_id, payment_hash FROM channel_htlcs WHERE channel_id = ?;" msgstr "" -#: wallet/wallet.c:2662 +#: wallet/wallet.c:2837 msgid "DELETE FROM channel_htlcs WHERE direction = ? AND origin_htlc = ? AND payment_hash = ? AND partid = ?;" msgstr "" -#: wallet/wallet.c:2715 +#: wallet/wallet.c:2890 msgid "SELECT status FROM payments WHERE payment_hash=? AND partid = ?;" msgstr "" -#: wallet/wallet.c:2733 +#: wallet/wallet.c:2908 msgid "INSERT INTO payments ( status, payment_hash, destination, msatoshi, timestamp, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, total_msat, partid, local_offer_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:2822 +#: wallet/wallet.c:2997 msgid "DELETE FROM payments WHERE payment_hash = ? AND partid = ?" msgstr "" -#: wallet/wallet.c:2836 +#: wallet/wallet.c:3011 msgid "DELETE FROM payments WHERE payment_hash = ?" msgstr "" -#: wallet/wallet.c:2937 +#: wallet/wallet.c:3112 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ? AND partid = ?" msgstr "" -#: wallet/wallet.c:2987 +#: wallet/wallet.c:3162 msgid "UPDATE payments SET status=? WHERE payment_hash=? AND partid=?" msgstr "" -#: wallet/wallet.c:2997 +#: wallet/wallet.c:3172 msgid "UPDATE payments SET payment_preimage=? WHERE payment_hash=? AND partid=?" msgstr "" -#: wallet/wallet.c:3007 +#: wallet/wallet.c:3182 msgid "UPDATE payments SET path_secrets = NULL , route_nodes = NULL , route_channels = NULL WHERE payment_hash = ? AND partid = ?;" msgstr "" -#: wallet/wallet.c:3039 +#: wallet/wallet.c:3214 msgid "SELECT failonionreply, faildestperm, failindex, failcode, failnode, failchannel, failupdate, faildetail, faildirection FROM payments WHERE payment_hash=? AND partid=?;" msgstr "" -#: wallet/wallet.c:3106 +#: wallet/wallet.c:3281 msgid "UPDATE payments SET failonionreply=? , faildestperm=? , failindex=? , failcode=? , failnode=? , failchannel=? , failupdate=? , faildetail=? , faildirection=? WHERE payment_hash=? AND partid=?;" msgstr "" -#: wallet/wallet.c:3165 +#: wallet/wallet.c:3340 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE payment_hash = ? ORDER BY id;" msgstr "" -#: wallet/wallet.c:3188 +#: wallet/wallet.c:3363 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments ORDER BY id;" msgstr "" -#: wallet/wallet.c:3239 +#: wallet/wallet.c:3414 msgid "SELECT id, status, destination, msatoshi, payment_hash, timestamp, payment_preimage, path_secrets, route_nodes, route_channels, msatoshi_sent, description, bolt11, failonionreply, total_msat, partid, local_offer_id FROM payments WHERE local_offer_id = ?;" msgstr "" -#: wallet/wallet.c:3284 +#: wallet/wallet.c:3459 msgid "DELETE FROM htlc_sigs WHERE channelid = ?" msgstr "" -#: wallet/wallet.c:3291 +#: wallet/wallet.c:3466 msgid "INSERT INTO htlc_sigs (channelid, signature) VALUES (?, ?)" msgstr "" -#: wallet/wallet.c:3303 +#: wallet/wallet.c:3478 msgid "SELECT blobval FROM vars WHERE name='genesis_hash'" msgstr "" -#: wallet/wallet.c:3327 +#: wallet/wallet.c:3502 msgid "INSERT INTO vars (name, blobval) VALUES ('genesis_hash', ?);" msgstr "" -#: wallet/wallet.c:3345 +#: wallet/wallet.c:3520 msgid "SELECT txid, outnum FROM utxoset WHERE spendheight < ?" msgstr "" -#: wallet/wallet.c:3357 +#: wallet/wallet.c:3532 msgid "DELETE FROM utxoset WHERE spendheight < ?" msgstr "" -#: wallet/wallet.c:3365 wallet/wallet.c:3479 +#: wallet/wallet.c:3540 wallet/wallet.c:3654 msgid "INSERT INTO blocks (height, hash, prev_hash) VALUES (?, ?, ?);" msgstr "" -#: wallet/wallet.c:3384 +#: wallet/wallet.c:3559 msgid "DELETE FROM blocks WHERE hash = ?" msgstr "" -#: wallet/wallet.c:3390 +#: wallet/wallet.c:3565 msgid "SELECT * FROM blocks WHERE height >= ?;" msgstr "" -#: wallet/wallet.c:3399 +#: wallet/wallet.c:3574 msgid "DELETE FROM blocks WHERE height > ?" msgstr "" -#: wallet/wallet.c:3411 +#: wallet/wallet.c:3586 msgid "UPDATE outputs SET spend_height = ?, status = ? WHERE prev_out_tx = ? AND prev_out_index = ?" msgstr "" -#: wallet/wallet.c:3429 +#: wallet/wallet.c:3604 msgid "UPDATE utxoset SET spendheight = ? WHERE txid = ? AND outnum = ?" msgstr "" -#: wallet/wallet.c:3452 wallet/wallet.c:3490 +#: wallet/wallet.c:3627 wallet/wallet.c:3665 msgid "INSERT INTO utxoset ( txid, outnum, blockheight, spendheight, txindex, scriptpubkey, satoshis) VALUES(?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3516 +#: wallet/wallet.c:3691 msgid "SELECT height FROM blocks WHERE height = ?" msgstr "" -#: wallet/wallet.c:3529 +#: wallet/wallet.c:3704 msgid "SELECT txid, spendheight, scriptpubkey, satoshis FROM utxoset WHERE blockheight = ? AND txindex = ? AND outnum = ? AND spendheight IS NULL" msgstr "" -#: wallet/wallet.c:3571 +#: wallet/wallet.c:3746 msgid "SELECT blockheight, txindex, outnum FROM utxoset WHERE spendheight = ?" msgstr "" -#: wallet/wallet.c:3602 wallet/wallet.c:3762 +#: wallet/wallet.c:3777 wallet/wallet.c:3937 msgid "SELECT blockheight FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3612 +#: wallet/wallet.c:3787 msgid "INSERT INTO transactions ( id, blockheight, txindex, rawtx) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3633 +#: wallet/wallet.c:3808 msgid "UPDATE transactions SET blockheight = ?, txindex = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:3650 +#: wallet/wallet.c:3825 msgid "INSERT INTO transaction_annotations (txid, idx, location, type, channel) VALUES (?, ?, ?, ?, ?) ON CONFLICT(txid,idx) DO NOTHING;" msgstr "" -#: wallet/wallet.c:3682 +#: wallet/wallet.c:3857 msgid "SELECT type, channel_id FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3698 +#: wallet/wallet.c:3873 msgid "UPDATE transactions SET type = ?, channel_id = ? WHERE id = ?" msgstr "" -#: wallet/wallet.c:3717 +#: wallet/wallet.c:3892 msgid "SELECT type FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3740 +#: wallet/wallet.c:3915 msgid "SELECT rawtx FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3786 +#: wallet/wallet.c:3961 msgid "SELECT blockheight, txindex FROM transactions WHERE id=?" msgstr "" -#: wallet/wallet.c:3814 +#: wallet/wallet.c:3989 msgid "SELECT id FROM transactions WHERE blockheight=?" msgstr "" -#: wallet/wallet.c:3833 +#: wallet/wallet.c:4008 msgid "INSERT INTO channeltxs ( channel_id, type, transaction_id, input_num, blockheight) VALUES (?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:3857 +#: wallet/wallet.c:4032 msgid "SELECT DISTINCT(channel_id) FROM channeltxs WHERE type = ?;" msgstr "" -#: wallet/wallet.c:3878 +#: wallet/wallet.c:4053 msgid "SELECT c.type, c.blockheight, t.rawtx, c.input_num, c.blockheight - t.blockheight + 1 AS depth, t.id as txid FROM channeltxs c JOIN transactions t ON t.id = c.transaction_id WHERE c.channel_id = ? ORDER BY c.id ASC;" msgstr "" -#: wallet/wallet.c:3923 +#: wallet/wallet.c:4098 msgid "UPDATE forwarded_payments SET in_msatoshi=?, out_msatoshi=?, state=?, resolved_time=?, failcode=? WHERE in_htlc_id=?" msgstr "" -#: wallet/wallet.c:3981 +#: wallet/wallet.c:4156 msgid "INSERT INTO forwarded_payments ( in_htlc_id, out_htlc_id, in_channel_scid, out_channel_scid, in_msatoshi, out_msatoshi, state, received_time, resolved_time, failcode) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4040 +#: wallet/wallet.c:4215 msgid "SELECT CAST(COALESCE(SUM(in_msatoshi - out_msatoshi), 0) AS BIGINT)FROM forwarded_payments WHERE state = ?;" msgstr "" -#: wallet/wallet.c:4089 +#: wallet/wallet.c:4264 msgid "SELECT f.state, in_msatoshi, out_msatoshi, hin.payment_hash as payment_hash, in_channel_scid, out_channel_scid, f.received_time, f.resolved_time, f.failcode FROM forwarded_payments f LEFT JOIN channel_htlcs hin ON (f.in_htlc_id = hin.id) WHERE (1 = ? OR f.state = ?) AND (1 = ? OR f.in_channel_scid = ?) AND (1 = ? OR f.out_channel_scid = ?)" msgstr "" -#: wallet/wallet.c:4211 +#: wallet/wallet.c:4386 msgid "SELECT t.id, t.rawtx, t.blockheight, t.txindex, t.type as txtype, c2.short_channel_id as txchan, a.location, a.idx as ann_idx, a.type as annotation_type, c.short_channel_id FROM transactions t LEFT JOIN transaction_annotations a ON (a.txid = t.id) LEFT JOIN channels c ON (a.channel = c.id) LEFT JOIN channels c2 ON (t.channel_id = c2.id) ORDER BY t.blockheight, t.txindex ASC" msgstr "" -#: wallet/wallet.c:4305 +#: wallet/wallet.c:4480 msgid "INSERT INTO penalty_bases ( channel_id, commitnum, txid, outnum, amount) VALUES (?, ?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4330 +#: wallet/wallet.c:4505 msgid "SELECT commitnum, txid, outnum, amount FROM penalty_bases WHERE channel_id = ?" msgstr "" -#: wallet/wallet.c:4354 +#: wallet/wallet.c:4529 msgid "DELETE FROM penalty_bases WHERE channel_id = ? AND commitnum = ?" msgstr "" -#: wallet/wallet.c:4372 +#: wallet/wallet.c:4547 msgid "SELECT 1 FROM offers WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4385 +#: wallet/wallet.c:4560 msgid "INSERT INTO offers ( offer_id, bolt12, label, status) VALUES (?, ?, ?, ?);" msgstr "" -#: wallet/wallet.c:4412 +#: wallet/wallet.c:4587 msgid "SELECT bolt12, label, status FROM offers WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4440 +#: wallet/wallet.c:4615 msgid "SELECT offer_id FROM offers;" msgstr "" -#: wallet/wallet.c:4466 +#: wallet/wallet.c:4641 msgid "UPDATE offers SET status=? WHERE offer_id = ?;" msgstr "" -#: wallet/wallet.c:4477 +#: wallet/wallet.c:4652 msgid "UPDATE invoices SET state=? WHERE state=? AND local_offer_id = ?;" msgstr "" -#: wallet/wallet.c:4505 +#: wallet/wallet.c:4680 msgid "SELECT status FROM offers WHERE offer_id = ?;" msgstr "" @@ -1262,11 +1330,11 @@ msgstr "" msgid "not a valid SQL statement" msgstr "" -#: wallet/test/run-wallet.c:1451 +#: wallet/test/run-wallet.c:1483 msgid "SELECT COUNT(1) FROM channel_funding_inflights WHERE channel_id = ?;" msgstr "" -#: wallet/test/run-wallet.c:1649 +#: wallet/test/run-wallet.c:1696 msgid "INSERT INTO channels (id) VALUES (1);" msgstr "" -# SHA256STAMP:51de0bd1efd4b12ec550d3faf934a32f745018a46e61b50cc242c2d5bae09470 +# SHA256STAMP:28f0ccfe94b0a41dd9f7053d1593beba6a833c6de80e510dbe7e26aca8f98ccf diff --git a/wallet/test/Makefile b/wallet/test/Makefile index 8c60d6c8e5d8..7526ec2321e6 100644 --- a/wallet/test/Makefile +++ b/wallet/test/Makefile @@ -8,6 +8,7 @@ ALL_TEST_PROGRAMS += $(WALLET_TEST_PROGRAMS) WALLET_TEST_COMMON_OBJS := \ common/amount.o \ common/base32.o \ + common/blockheight_states.o \ common/derive_basepoints.o \ common/htlc_state.o \ common/htlc_wire.o \ diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index ede16ddd6795..744649c57ccf 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -132,16 +132,16 @@ bool feature_offered(const u8 *features UNNEEDED, size_t f UNNEEDED) bool fromwire_channeld_dev_memleak_reply(const void *p UNNEEDED, bool *leak UNNEEDED) { fprintf(stderr, "fromwire_channeld_dev_memleak_reply called!\n"); abort(); } /* Generated stub for fromwire_channeld_got_commitsig */ -bool fromwire_channeld_got_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct fee_states **fee_states UNNEEDED, struct bitcoin_signature *signature UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, struct added_htlc **added UNNEEDED, struct fulfilled_htlc **fulfilled UNNEEDED, struct failed_htlc ***failed UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_tx **tx UNNEEDED) +bool fromwire_channeld_got_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct bitcoin_signature *signature UNNEEDED, struct bitcoin_signature **htlc_signature UNNEEDED, struct added_htlc **added UNNEEDED, struct fulfilled_htlc **fulfilled UNNEEDED, struct failed_htlc ***failed UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_tx **tx UNNEEDED) { fprintf(stderr, "fromwire_channeld_got_commitsig called!\n"); abort(); } /* Generated stub for fromwire_channeld_got_revoke */ -bool fromwire_channeld_got_revoke(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *revokenum UNNEEDED, struct secret *per_commitment_secret UNNEEDED, struct pubkey *next_per_commit_point UNNEEDED, struct fee_states **fee_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct penalty_base **pbase UNNEEDED, struct bitcoin_tx **penalty_tx UNNEEDED) +bool fromwire_channeld_got_revoke(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *revokenum UNNEEDED, struct secret *per_commitment_secret UNNEEDED, struct pubkey *next_per_commit_point UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct penalty_base **pbase UNNEEDED, struct bitcoin_tx **penalty_tx UNNEEDED) { fprintf(stderr, "fromwire_channeld_got_revoke called!\n"); abort(); } /* Generated stub for fromwire_channeld_offer_htlc_reply */ bool fromwire_channeld_offer_htlc_reply(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *id UNNEEDED, u8 **failuremsg UNNEEDED, wirestring **failurestr UNNEEDED) { fprintf(stderr, "fromwire_channeld_offer_htlc_reply called!\n"); abort(); } /* Generated stub for fromwire_channeld_sending_commitsig */ -bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct penalty_base **pbase UNNEEDED, struct fee_states **fee_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_signature *commit_sig UNNEEDED, struct bitcoin_signature **htlc_sigs UNNEEDED) +bool fromwire_channeld_sending_commitsig(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, u64 *commitnum UNNEEDED, struct penalty_base **pbase UNNEEDED, struct fee_states **fee_states UNNEEDED, struct height_states **blockheight_states UNNEEDED, struct changed_htlc **changed UNNEEDED, struct bitcoin_signature *commit_sig UNNEEDED, struct bitcoin_signature **htlc_sigs UNNEEDED) { fprintf(stderr, "fromwire_channeld_sending_commitsig called!\n"); abort(); } /* Generated stub for fromwire_connectd_peer_connected */ bool fromwire_connectd_peer_connected(const tal_t *ctx UNNEEDED, const void *p UNNEEDED, struct node_id *id UNNEEDED, struct wireaddr_internal *addr UNNEEDED, bool *incoming UNNEEDED, struct per_peer_state **pps UNNEEDED, u8 **features UNNEEDED) @@ -1184,6 +1184,15 @@ static bool channel_inflightseq(struct channel_inflight *i1, &i2->last_sig, sizeof(i2->last_sig))); CHECK(bitcoin_tx_eq(i1->last_tx, i2->last_tx)); + CHECK(!i1->lease_commit_sig == !i2->lease_commit_sig); + if (i1->lease_commit_sig) + CHECK(memeq(i1->lease_commit_sig, sizeof(*i1->lease_commit_sig), + i2->lease_commit_sig, sizeof(*i2->lease_commit_sig))); + CHECK(i1->lease_expiry == i2->lease_expiry); + CHECK(i1->lease_chan_max_msat == i2->lease_chan_max_msat); + CHECK(i1->lease_chan_max_ppt == i2->lease_chan_max_ppt); + CHECK(i1->lease_blockheight_start == i2->lease_blockheight_start); + return true; } @@ -1254,6 +1263,27 @@ static bool channelseq(struct channel *c1, struct channel *c2) CHECK(c1->last_was_revoke == c2->last_was_revoke); + CHECK(!c1->lease_commit_sig == !c2->lease_commit_sig); + if (c1->lease_commit_sig) + CHECK(memeq(c1->lease_commit_sig, sizeof(*c1->lease_commit_sig), + c2->lease_commit_sig, sizeof(*c2->lease_commit_sig))); + + CHECK(c1->lease_chan_max_msat == c2->lease_chan_max_msat); + CHECK(c1->lease_chan_max_ppt == c2->lease_chan_max_ppt); + CHECK(c1->lease_expiry == c2->lease_expiry); + + CHECK(height_states_valid(c1->blockheight_states, c1->opener)); + CHECK(height_states_valid(c2->blockheight_states, c2->opener)); + for (enum htlc_state i = 0; i < ARRAY_SIZE(c1->blockheight_states->height); + i++) { + if (c1->blockheight_states->height[i] == NULL) { + CHECK(c2->blockheight_states->height[i] == NULL); + } else { + CHECK(*c1->blockheight_states->height[i] + == *c2->blockheight_states->height[i]); + } + } + i1 = list_top(&c1->inflights, struct channel_inflight, list); i2 = list_top(&c2->inflights, struct channel_inflight, list); CHECK((i1 != NULL) == (i2 != NULL)); @@ -1304,7 +1334,7 @@ static bool test_channel_crud(struct lightningd *ld, const tal_t *ctx) secp256k1_ecdsa_signature *node_sig1 = tal(w, secp256k1_ecdsa_signature); secp256k1_ecdsa_signature *bitcoin_sig1 = tal(w, secp256k1_ecdsa_signature); secp256k1_ecdsa_signature *node_sig2, *bitcoin_sig2; - u32 feerate; + u32 feerate, blockheight; bool load; memset(&c1, 0, sizeof(c1)); @@ -1320,6 +1350,8 @@ static bool test_channel_crud(struct lightningd *ld, const tal_t *ctx) node_id_from_pubkey(&id, &pk); feerate = 31337; c1.fee_states = new_fee_states(w, c1.opener, &feerate); + blockheight = 10010; + c1.blockheight_states = new_height_states(w, c1.opener, &blockheight); mempat(scriptpubkey, tal_count(scriptpubkey)); c1.first_blocknum = 1; parse_wireaddr_internal("localhost:1234", &addr, 0, false, false, false, @@ -1478,7 +1510,8 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) struct wally_psbt *funding_psbt; struct channel_info *channel_info = tal(w, struct channel_info); struct basepoints basepoints; - u32 feerate; + secp256k1_ecdsa_signature *lease_commit_sig; + u32 feerate, lease_blockheight_start; u64 dbid; pubkey_from_der(tal_hexdata(w, "02a1633cafcc01ebfb6d78e39f687a1f0995c62fc95f51ead10a02ee0be551b5dc", 66), 33, &pk); @@ -1493,12 +1526,17 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) our_sats = AMOUNT_SAT(3333333); mempat(&sig.s, sizeof(sig.s)); mempat(&cid, sizeof(struct channel_id)); + + lease_commit_sig = tal(w, secp256k1_ecdsa_signature); + mempat(lease_commit_sig, sizeof(*lease_commit_sig)); + sig.sighash_type = SIGHASH_ALL; /* last_tx taken from BOLT #3 */ last_tx = bitcoin_tx_from_hex(w, "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8003a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de843110ae8f6a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e040047304402206a2679efa3c7aaffd2a447fd0df7aba8792858b589750f6a1203f9259173198a022008d52a0e77a99ab533c36206cb15ad7aeb2aa72b93d4b571e728cb5ec2f6fe260147304402206d6cb93969d39177a09d5d45b583f34966195b77c7e585cf47ac5cce0c90cefb022031d71ae4e33a4e80df7f981d696fbdee517337806a3c7138b7491e2cbb077a0e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220", strlen("02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8003a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de843110ae8f6a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e040047304402206a2679efa3c7aaffd2a447fd0df7aba8792858b589750f6a1203f9259173198a022008d52a0e77a99ab533c36206cb15ad7aeb2aa72b93d4b571e728cb5ec2f6fe260147304402206d6cb93969d39177a09d5d45b583f34966195b77c7e585cf47ac5cce0c90cefb022031d71ae4e33a4e80df7f981d696fbdee517337806a3c7138b7491e2cbb077a0e01475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220")); funding_psbt = psbt_from_b64(w, "cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA", strlen("cHNidP8BAKACAAAAAqsJSaCMWvfEm4IS9Bfi8Vqz9cM9zxU4IagTn4d6W3vkAAAAAAD+////qwlJoIxa98SbghL0F+LxWrP1wz3PFTghqBOfh3pbe+QBAAAAAP7///8CYDvqCwAAAAAZdqkUdopAu9dAy+gdmI5x3ipNXHE5ax2IrI4kAAAAAAAAGXapFG9GILVT+glechue4O/p+gOcykWXiKwAAAAAAAEHakcwRAIgR1lmF5fAGwNrJZKJSGhiGDR9iYZLcZ4ff89X0eURZYcCIFMJ6r9Wqk2Ikf/REf3xM286KdqGbX+EhtdVRs7tr5MZASEDXNxh/HupccC1AaZGoqg7ECy0OIEhfKaC3Ibi1z+ogpIAAQEgAOH1BQAAAAAXqRQ1RebjO4MsRwUPJNPuuTycA5SLx4cBBBYAFIXRNTfy4mVAWjTbr6nj3aAfuCMIAAAA")); feerate = 192838; + lease_blockheight_start = 101010; memset(&our_config, 1, sizeof(struct channel_config)); our_config.id = 0; memset(&txid, 1, sizeof(txid)); @@ -1541,7 +1579,13 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) &pk, NULL, 1000, 100, NULL, 0, 0, true, - LOCAL, REASON_UNKNOWN, NULL); + LOCAL, REASON_UNKNOWN, + NULL, + new_height_states(w, LOCAL, + &lease_blockheight_start), + 100, + lease_commit_sig, + 7777, 22); db_begin_transaction(w->db); CHECK(!wallet_err); wallet_channel_insert(w, chan); @@ -1557,7 +1601,8 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) our_sats, funding_psbt, last_tx, - sig); + sig, + 1, lease_commit_sig, 2, 4, 22); /* do inflights get correctly added to the channel? */ wallet_inflight_add(w, inflight); @@ -1578,12 +1623,14 @@ static bool test_channel_inflight_crud(struct lightningd *ld, const tal_t *ctx) our_sats, funding_psbt, last_tx, - sig); + sig, + 0, NULL, 0, 0, 0); wallet_inflight_add(w, inflight); CHECK_MSG(c2 = wallet_channel_load(w, chan->dbid), tal_fmt(w, "Load from DB")); CHECK_MSG(channelseq(chan, c2), "Compare loaded with saved (v2)"); CHECK_MSG(count_inflights(w, chan->dbid) == 2, "inflights exist"); + tal_free(c2); /* Update the PSBT for both inflights, check that are updated diff --git a/wallet/wallet.c b/wallet/wallet.c index d53652fece4c..e9a26b611cee 100644 --- a/wallet/wallet.c +++ b/wallet/wallet.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -196,6 +197,7 @@ static struct utxo *wallet_stmt2output(const tal_t *ctx, struct db_stmt *stmt) utxo->close_info->commitment_point = NULL; utxo->close_info->option_anchor_outputs = db_column_int(stmt, 9); + utxo->close_info->csv = db_column_int(stmt, 14); } else { utxo->close_info = NULL; } @@ -276,6 +278,7 @@ struct utxo **wallet_get_utxos(const tal_t *ctx, struct wallet *w, const enum ou ", spend_height" ", scriptpubkey " ", reserved_til " + ", csv_lock " "FROM outputs")); } else { stmt = db_prepare_v2(w->db, SQL("SELECT" @@ -293,6 +296,7 @@ struct utxo **wallet_get_utxos(const tal_t *ctx, struct wallet *w, const enum ou ", spend_height" ", scriptpubkey " ", reserved_til " + ", csv_lock " "FROM outputs " "WHERE status= ? ")); db_bind_int(stmt, 0, output_status_in_db(state)); @@ -331,6 +335,7 @@ struct utxo **wallet_get_unconfirmed_closeinfo_utxos(const tal_t *ctx, ", spend_height" ", scriptpubkey" ", reserved_til" + ", csv_lock" " FROM outputs" " WHERE channel_id IS NOT NULL AND " "confirmation_height IS NULL")); @@ -368,6 +373,7 @@ struct utxo *wallet_utxo_get(const tal_t *ctx, struct wallet *w, ", spend_height" ", scriptpubkey" ", reserved_til" + ", csv_lock" " FROM outputs" " WHERE prev_out_tx = ?" " AND prev_out_index = ?")); @@ -510,7 +516,8 @@ static bool excluded(const struct utxo **excludes, return false; } -static bool deep_enough(u32 maxheight, const struct utxo *utxo) +static bool deep_enough(u32 maxheight, const struct utxo *utxo, + u32 current_blockheight) { /* If we require confirmations check that we have a * confirmation height and that it is below the required @@ -519,6 +526,13 @@ static bool deep_enough(u32 maxheight, const struct utxo *utxo) return true; if (!utxo->blockheight) return false; + /* Check that CSV-lock is now free! */ + if (utxo->close_info) { + u32 csv_free = *utxo->blockheight + utxo->close_info->csv; + assert(csv_free > *utxo->blockheight); + if (csv_free < current_blockheight) + return false; + } return *utxo->blockheight <= maxheight; } @@ -549,6 +563,7 @@ struct utxo *wallet_find_utxo(const tal_t *ctx, struct wallet *w, ", spend_height" ", scriptpubkey " ", reserved_til" + ", csv_lock" " FROM outputs" " WHERE status = ?" " OR (status = ? AND reserved_til <= ?)" @@ -565,7 +580,8 @@ struct utxo *wallet_find_utxo(const tal_t *ctx, struct wallet *w, utxo = NULL; while (!utxo && db_step(stmt)) { utxo = wallet_stmt2output(ctx, stmt); - if (excluded(excludes, utxo) || !deep_enough(maxheight, utxo)) + if (excluded(excludes, utxo) + || !deep_enough(maxheight, utxo, current_blockheight)) utxo = tal_free(utxo); } @@ -581,7 +597,8 @@ bool wallet_add_onchaind_utxo(struct wallet *w, struct amount_sat amount, const struct channel *channel, /* NULL if option_static_remotekey */ - const struct pubkey *commitment_point) + const struct pubkey *commitment_point, + u32 csv_lock) { struct db_stmt *stmt; @@ -613,7 +630,8 @@ bool wallet_add_onchaind_utxo(struct wallet *w, ", confirmation_height" ", spend_height" ", scriptpubkey" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); + ", csv_lock" + ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_txid(stmt, 0, txid); db_bind_int(stmt, 1, outnum); db_bind_amount_sat(stmt, 2, &amount); @@ -634,6 +652,8 @@ bool wallet_add_onchaind_utxo(struct wallet *w, db_bind_null(stmt, 11); db_bind_blob(stmt, 12, scriptpubkey, tal_bytelen(scriptpubkey)); + db_bind_int(stmt, 13, csv_lock); + db_exec_prepared_v2(take(stmt)); return true; } @@ -966,21 +986,63 @@ static struct fee_states *wallet_channel_fee_states_load(struct wallet *w, return fee_states; } +static struct height_states *wallet_channel_height_states_load(struct wallet *w, + const u64 id, + enum side opener) +{ + struct height_states *states; + struct db_stmt *stmt; + + stmt = db_prepare_v2(w->db, SQL("SELECT hstate, blockheight FROM channel_blockheights WHERE channel_id = ?")); + db_bind_u64(stmt, 0, id); + db_query_prepared(stmt); + + /* Start with blank slate. */ + states = new_height_states(w, opener, NULL); + while (db_step(stmt)) { + enum htlc_state hstate = db_column_int(stmt, 0); + u32 blockheight = db_column_int(stmt, 1); + + if (states->height[hstate] != NULL) { + log_broken(w->log, + "duplicate channel_blockheights for %s id %"PRIu64, + htlc_state_name(hstate), id); + states = tal_free(states); + break; + } + states->height[hstate] = tal_dup(states, u32, &blockheight); + } + tal_free(stmt); + + if (states && !height_states_valid(states, opener)) { + log_broken(w->log, + "invalid channel_blockheight for id %"PRIu64, id); + states = tal_free(states); + } + return states; +} + void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) { struct db_stmt *stmt; stmt = db_prepare_v2(w->db, SQL("INSERT INTO channel_funding_inflights (" - " channel_id" - ", funding_tx_id" - ", funding_tx_outnum" - ", funding_feerate" - ", funding_satoshi" - ", our_funding_satoshi" - ", funding_psbt" - ", last_tx" - ", last_sig" - ") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);")); + " channel_id" // 0 + ", funding_tx_id" // 1 + ", funding_tx_outnum" // 2 + ", funding_feerate" // 3 + ", funding_satoshi" // 4 + ", our_funding_satoshi" // 5 + ", funding_psbt" // 6 + ", last_tx" // 7 + ", last_sig" // 8 + ", lease_commit_sig" // 9 + ", lease_chan_max_msat" // 10 + ", lease_chan_max_ppt" // 11 + ", lease_expiry" // 12 + ", lease_blockheight_start" // 13 + ") VALUES (" + "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);")); db_bind_u64(stmt, 0, inflight->channel->dbid); db_bind_txid(stmt, 1, &inflight->funding->txid); @@ -991,6 +1053,21 @@ void wallet_inflight_add(struct wallet *w, struct channel_inflight *inflight) db_bind_psbt(stmt, 6, inflight->funding_psbt); db_bind_psbt(stmt, 7, inflight->last_tx->psbt); db_bind_signature(stmt, 8, &inflight->last_sig.s); + + if (inflight->lease_expiry != 0) { + db_bind_signature(stmt, 9, inflight->lease_commit_sig); + db_bind_int(stmt, 10, inflight->lease_chan_max_msat); + db_bind_int(stmt, 11, inflight->lease_chan_max_ppt); + db_bind_int(stmt, 12, inflight->lease_expiry); + db_bind_int(stmt, 13, inflight->lease_blockheight_start); + } else { + db_bind_null(stmt, 9); + db_bind_null(stmt, 10); + db_bind_null(stmt, 11); + db_bind_int(stmt, 12, 0); + db_bind_null(stmt, 13); + } + db_exec_prepared_v2(stmt); assert(!stmt->error); tal_free(stmt); @@ -1048,6 +1125,10 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, struct bitcoin_signature last_sig; struct channel_inflight *inflight; + secp256k1_ecdsa_signature *lease_commit_sig; + u32 lease_chan_max_msat, lease_blockheight_start; + u16 lease_chan_max_ppt; + db_column_txid(stmt, 0, &funding_txid); db_column_amount_sat(stmt, 3, &funding_sat); db_column_amount_sat(stmt, 4, &our_funding_sat); @@ -1056,6 +1137,19 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, last_sig.sighash_type = SIGHASH_ALL; + if (!db_column_is_null(stmt, 10)) { + lease_commit_sig = tal(tmpctx, secp256k1_ecdsa_signature); + db_column_signature(stmt, 10, lease_commit_sig); + lease_chan_max_msat = db_column_int(stmt, 11); + lease_chan_max_ppt = db_column_int(stmt, 12); + lease_blockheight_start = db_column_int(stmt, 13); + } else { + lease_commit_sig = NULL; + lease_chan_max_msat = 0; + lease_chan_max_ppt = 0; + lease_blockheight_start = 0; + } + inflight = new_inflight(chan, funding_txid, db_column_int(stmt, 1), db_column_int(stmt, 2), @@ -1063,7 +1157,12 @@ wallet_stmt2inflight(struct wallet *w, struct db_stmt *stmt, our_funding_sat, db_column_psbt(tmpctx, stmt, 5), db_column_psbt_to_tx(tmpctx, stmt, 6), - last_sig); + last_sig, + db_column_int(stmt, 9), + lease_commit_sig, + lease_chan_max_msat, + lease_chan_max_ppt, + lease_blockheight_start); /* Pull out the serialized tx-sigs-received-ness */ inflight->remote_tx_sigs = db_column_int(stmt, 8); @@ -1086,6 +1185,11 @@ static bool wallet_channel_load_inflights(struct wallet *w, ", last_tx" // 6 ", last_sig" // 7 ", funding_tx_remote_sigs_received" //8 + ", lease_expiry" // 9 + ", lease_commit_sig" // 10 + ", lease_chan_max_msat" // 11 + ", lease_chan_max_ppt" // 12 + ", lease_blockheight_start" // 13 " FROM channel_funding_inflights" " WHERE channel_id = ?" // ?0 " ORDER BY funding_feerate")); @@ -1114,6 +1218,7 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm bool ok = true; struct channel_info channel_info; struct fee_states *fee_states; + struct height_states *height_states; struct short_channel_id *scid; struct channel_id cid; struct channel *chan; @@ -1133,6 +1238,9 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm struct pubkey *future_per_commitment_point; struct amount_sat funding_sat, our_funding_sat; struct amount_msat push_msat, our_msat, msat_to_us_min, msat_to_us_max; + secp256k1_ecdsa_signature *lease_commit_sig; + u32 lease_chan_max_msat; + u16 lease_chan_max_ppt; peer_dbid = db_column_u64(stmt, 1); peer = find_peer_by_dbid(w->ld, peer_dbid); @@ -1215,6 +1323,19 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm return NULL; } + /* Blockheight states for the channel! */ + height_states + = wallet_channel_height_states_load(w, + db_column_u64(stmt, 0), + db_column_int(stmt, 7)); + if (!height_states) + ok = false; + + if (!ok) { + tal_free(height_states); + return NULL; + } + final_key_idx = db_column_u64(stmt, 31); if (final_key_idx < 0) { tal_free(fee_states); @@ -1242,6 +1363,17 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm db_column_amount_msat(stmt, 40, &msat_to_us_min); db_column_amount_msat(stmt, 41, &msat_to_us_max); + if (!db_column_is_null(stmt, 61)) { + lease_commit_sig = tal(w, secp256k1_ecdsa_signature); + db_column_signature(stmt, 61, lease_commit_sig); + lease_chan_max_msat = db_column_int(stmt, 62); + lease_chan_max_ppt = db_column_int(stmt, 63); + } else { + lease_commit_sig = NULL; + lease_chan_max_msat = 0; + lease_chan_max_ppt = 0; + } + chan = new_channel(peer, db_column_u64(stmt, 0), &wshachain, db_column_int(stmt, 6), @@ -1292,7 +1424,12 @@ static struct channel *wallet_stmt2channel(struct wallet *w, struct db_stmt *stm db_column_int(stmt, 49), db_column_int(stmt, 51), db_column_int(stmt, 52), - shutdown_wrong_funding); + shutdown_wrong_funding, + take(height_states), + db_column_int(stmt, 60), + lease_commit_sig, + lease_chan_max_msat, + lease_chan_max_ppt); if (!wallet_channel_load_inflights(w, chan)) { tal_free(chan); @@ -1384,6 +1521,10 @@ static bool wallet_channels_load_active(struct wallet *w) ", funding_pubkey_local" // 57 ", shutdown_wrong_txid" // 58 ", shutdown_wrong_outnum" // 59 + ", lease_expiry" // 60 + ", lease_commit_sig" // 61 + ", lease_chan_max_msat" // 62 + ", lease_chan_max_ppt" // 63 " FROM channels" " WHERE state != ?;")); //? 0 db_bind_int(stmt, 0, CLOSED); @@ -1671,8 +1812,12 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) " closer=?," // 34 " state_change_reason=?," // 35 " shutdown_wrong_txid=?," // 36 - " shutdown_wrong_outnum=?" // 37 - " WHERE id=?")); // 38 + " shutdown_wrong_outnum=?," // 37 + " lease_expiry=?," // 38 + " lease_commit_sig=?," // 39 + " lease_chan_max_msat=?," // 40 + " lease_chan_max_ppt=?" // 41 + " WHERE id=?")); // 42 db_bind_u64(stmt, 0, chan->their_shachain.id); if (chan->scid) db_bind_short_channel_id(stmt, 1, chan->scid); @@ -1724,7 +1869,18 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_bind_null(stmt, 36); db_bind_null(stmt, 37); } - db_bind_u64(stmt, 38, chan->dbid); + + db_bind_int(stmt, 38, chan->lease_expiry); + if (chan->lease_commit_sig) { + db_bind_signature(stmt, 39, chan->lease_commit_sig); + db_bind_int(stmt, 40, chan->lease_chan_max_msat); + db_bind_int(stmt, 41, chan->lease_chan_max_ppt); + } else { + db_bind_null(stmt, 39); + db_bind_null(stmt, 40); + db_bind_null(stmt, 41); + } + db_bind_u64(stmt, 42, chan->dbid); db_exec_prepared_v2(take(stmt)); wallet_channel_config_save(w, &chan->channel_info.their_config); @@ -1773,6 +1929,25 @@ void wallet_channel_save(struct wallet *w, struct channel *chan) db_exec_prepared_v2(take(stmt)); } + /* FIXME: Updates channel_blockheights by discarding and rewriting. */ + stmt = db_prepare_v2(w->db, SQL("DELETE FROM channel_blockheights " + "WHERE channel_id=?")); + db_bind_u64(stmt, 0, chan->dbid); + db_exec_prepared_v2(take(stmt)); + + for (enum htlc_state i = 0; + i < ARRAY_SIZE(chan->blockheight_states->height); + i++) { + if (!chan->blockheight_states->height[i]) + continue; + stmt = db_prepare_v2(w->db, SQL("INSERT INTO channel_blockheights " + " VALUES(?, ?, ?)")); + db_bind_u64(stmt, 0, chan->dbid); + db_bind_int(stmt, 1, i); + db_bind_int(stmt, 2, *chan->blockheight_states->height[i]); + db_exec_prepared_v2(take(stmt)); + } + /* If we have a last_sent_commit, store it */ last_sent_commit = tal_arr(tmpctx, u8, 0); for (size_t i = 0; i < tal_count(chan->last_sent_commit); i++) diff --git a/wallet/wallet.h b/wallet/wallet.h index d8b399385477..f25881696806 100644 --- a/wallet/wallet.h +++ b/wallet/wallet.h @@ -400,7 +400,9 @@ bool wallet_add_onchaind_utxo(struct wallet *w, struct amount_sat amount, const struct channel *chan, /* NULL if option_static_remotekey */ - const struct pubkey *commitment_point); + const struct pubkey *commitment_point, + /* option_will_fund makes the csv_lock variable */ + u32 csv_lock); /** * wallet_reserve_utxo - set a reservation on a UTXO. diff --git a/wallet/walletrpc.c b/wallet/walletrpc.c index 8bf0f6ccea0c..08abb1b125d3 100644 --- a/wallet/walletrpc.c +++ b/wallet/walletrpc.c @@ -291,6 +291,14 @@ static void json_add_utxo(struct json_stream *response, if (reserved) json_add_num(response, "reserved_to_block", utxo->reserved_til); + if (utxo->close_info && utxo->close_info->csv > 1) { + json_add_num(response, "csv_lock", utxo->close_info->csv); + + if (utxo->blockheight) + json_add_u32(response, "spendable_at", + *utxo->blockheight + utxo->close_info->csv); + } + json_object_end(response); } diff --git a/wire/common_wiregen.c b/wire/common_wiregen.c index 1355150fd480..cca2e92611da 100644 --- a/wire/common_wiregen.c +++ b/wire/common_wiregen.c @@ -100,4 +100,4 @@ bool fromwire_custommsg_out(const tal_t *ctx, const void *p, u8 **msg) fromwire_u8_array(&cursor, &plen, *msg, msg_len); return cursor != NULL; } -// SHA256STAMP:a747ee0bc8a91c00e719bae883b505d6e7c85b33165a9156a571a0aa171a7256 +// SHA256STAMP:dae50cbf444bf43c76720df0cf0a89b4360ff576307f88d54e55696c034c1266 diff --git a/wire/common_wiregen.h b/wire/common_wiregen.h index 9efd995e40c3..5cd2b3cc572f 100644 --- a/wire/common_wiregen.h +++ b/wire/common_wiregen.h @@ -41,4 +41,4 @@ bool fromwire_custommsg_out(const tal_t *ctx, const void *p, u8 **msg); #endif /* LIGHTNING_WIRE_COMMON_WIREGEN_H */ -// SHA256STAMP:a747ee0bc8a91c00e719bae883b505d6e7c85b33165a9156a571a0aa171a7256 +// SHA256STAMP:dae50cbf444bf43c76720df0cf0a89b4360ff576307f88d54e55696c034c1266 diff --git a/wire/extracted_peer_04_opt_will_fund.patch b/wire/extracted_peer_04_opt_will_fund.patch new file mode 100644 index 000000000000..c8c279161a23 --- /dev/null +++ b/wire/extracted_peer_04_opt_will_fund.patch @@ -0,0 +1,48 @@ +--- wire/peer_wire.csv 2021-06-10 12:47:17.225844741 -0500 ++++ - 2021-06-10 12:47:40.960373156 -0500 +@@ -143,6 +139,9 @@ + tlvtype,opening_tlvs,option_upfront_shutdown_script,1 + tlvdata,opening_tlvs,option_upfront_shutdown_script,shutdown_len,u16, + tlvdata,opening_tlvs,option_upfront_shutdown_script,shutdown_scriptpubkey,byte,shutdown_len ++tlvtype,opening_tlvs,request_funds,3 ++tlvdata,opening_tlvs,request_funds,requested_sats,u64, ++tlvdata,opening_tlvs,request_funds,blockheight,u32, + msgtype,accept_channel2,65 + msgdata,accept_channel2,channel_id,channel_id, + msgdata,accept_channel2,funding_satoshis,u64, +@@ -162,6 +161,15 @@ + tlvtype,accept_tlvs,option_upfront_shutdown_script,1 + tlvdata,accept_tlvs,option_upfront_shutdown_script,shutdown_len,u16, + tlvdata,accept_tlvs,option_upfront_shutdown_script,shutdown_scriptpubkey,byte,shutdown_len ++tlvtype,accept_tlvs,will_fund,2 ++tlvdata,accept_tlvs,will_fund,signature,signature, ++tlvdata,accept_tlvs,will_fund,lease_rates,lease_rates, ++subtype,lease_rates ++subtypedata,lease_rates,channel_fee_max_base_msat,u32, ++subtypedata,lease_rates,channel_fee_max_proportional_thousandths,u16, ++subtypedata,lease_rates,funding_weight,u16, ++subtypedata,lease_rates,lease_fee_base_sat,u32, ++subtypedata,lease_rates,lease_fee_basis,tu32, + msgtype,init_rbf,72 + msgdata,init_rbf,channel_id,channel_id, + msgdata,init_rbf,funding_satoshis,u64, +@@ -215,6 +219,9 @@ + msgtype,update_fee,134 + msgdata,update_fee,channel_id,channel_id, + msgdata,update_fee,feerate_per_kw,u32, ++msgtype,update_blockheight,137 ++msgdata,update_blockheight,channel_id,channel_id, ++msgdata,update_blockheight,blockheight,u32, + msgtype,channel_reestablish,136 + msgdata,channel_reestablish,channel_id,channel_id, + msgdata,channel_reestablish,next_commitment_number,u64, +@@ -249,6 +256,9 @@ + msgdata,node_announcement,alias,byte,32 + msgdata,node_announcement,addrlen,u16, + msgdata,node_announcement,addresses,byte,addrlen ++msgdata,node_announcement,tlvs,node_ann_tlvs, ++tlvtype,node_ann_tlvs,option_will_fund,1 ++tlvdata,node_ann_tlvs,option_will_fund,lease_rates,lease_rates, + msgtype,channel_update,258 + msgdata,channel_update,signature,signature, + msgdata,channel_update,chain_hash,chain_hash, diff --git a/wire/peer_printgen.c b/wire/peer_printgen.c index b2ae43f11b83..73fe3a7d6efc 100644 --- a/wire/peer_printgen.c +++ b/wire/peer_printgen.c @@ -128,6 +128,10 @@ void printpeer_wire_message(const u8 *msg) printf("WIRE_UPDATE_FEE:\n"); printwire_update_fee("update_fee", msg); return; + case WIRE_UPDATE_BLOCKHEIGHT: + printf("WIRE_UPDATE_BLOCKHEIGHT:\n"); + printwire_update_blockheight("update_blockheight", msg); + return; case WIRE_CHANNEL_REESTABLISH: printf("WIRE_CHANNEL_REESTABLISH:\n"); printwire_channel_reestablish("channel_reestablish", msg); @@ -196,6 +200,52 @@ void printwire_witness_element(const char *fieldname, const u8 **cursor, size_t } +void printwire_lease_rates(const char *fieldname, const u8 **cursor, size_t *plen) +{ + + printf("funding_weight="); + u16 funding_weight = fromwire_u16(cursor, plen); + + printwire_u16(tal_fmt(NULL, "%s.funding_weight", fieldname), &funding_weight); + if (!*cursor) { + printf("**TRUNCATED**\n"); + return; + } + printf("lease_fee_basis="); + u16 lease_fee_basis = fromwire_u16(cursor, plen); + + printwire_u16(tal_fmt(NULL, "%s.lease_fee_basis", fieldname), &lease_fee_basis); + if (!*cursor) { + printf("**TRUNCATED**\n"); + return; + } + printf("channel_fee_max_proportional_thousandths="); + u16 channel_fee_max_proportional_thousandths = fromwire_u16(cursor, plen); + + printwire_u16(tal_fmt(NULL, "%s.channel_fee_max_proportional_thousandths", fieldname), &channel_fee_max_proportional_thousandths); + if (!*cursor) { + printf("**TRUNCATED**\n"); + return; + } + printf("lease_fee_base_sat="); + u32 lease_fee_base_sat = fromwire_u32(cursor, plen); + + printwire_u32(tal_fmt(NULL, "%s.lease_fee_base_sat", fieldname), &lease_fee_base_sat); + if (!*cursor) { + printf("**TRUNCATED**\n"); + return; + } + printf("channel_fee_max_base_msat="); + u32 channel_fee_max_base_msat = fromwire_tu32(cursor, plen); + + printwire_u32(tal_fmt(NULL, "%s.channel_fee_max_base_msat", fieldname), &channel_fee_max_base_msat); + if (!*cursor) { + printf("**TRUNCATED**\n"); + return; + } + +} + void printwire_channel_update_checksums(const char *fieldname, const u8 **cursor, size_t *plen) { @@ -461,10 +511,33 @@ static void printwire_tlv_opening_tlvs_option_upfront_shutdown_script(const char return; } +} +static void printwire_tlv_opening_tlvs_request_funds(const char *fieldname, const u8 **cursor, size_t *plen) +{ + printf("(msg_name=%s)\n", "request_funds"); + + printf("requested_sats="); + u64 requested_sats = fromwire_u64(cursor, plen); + + printwire_u64(tal_fmt(NULL, "%s.requested_sats", fieldname), &requested_sats); + if (!*cursor) { + printf("**TRUNCATED**\n"); + return; + } + printf("blockheight="); + u32 blockheight = fromwire_u32(cursor, plen); + + printwire_u32(tal_fmt(NULL, "%s.blockheight", fieldname), &blockheight); + if (!*cursor) { + printf("**TRUNCATED**\n"); + return; + } + } static const struct tlv_print_record_type print_tlvs_opening_tlvs[] = { { 1, printwire_tlv_opening_tlvs_option_upfront_shutdown_script }, + { 3, printwire_tlv_opening_tlvs_request_funds }, }; static void printwire_tlv_accept_tlvs_option_upfront_shutdown_script(const char *fieldname, const u8 **cursor, size_t *plen) @@ -484,10 +557,30 @@ static void printwire_tlv_accept_tlvs_option_upfront_shutdown_script(const char return; } +} +static void printwire_tlv_accept_tlvs_will_fund(const char *fieldname, const u8 **cursor, size_t *plen) +{ + printf("(msg_name=%s)\n", "will_fund"); + + printf("signature="); + secp256k1_ecdsa_signature signature; + fromwire_secp256k1_ecdsa_signature(cursor, plen, &signature); + + printwire_secp256k1_ecdsa_signature(tal_fmt(NULL, "%s.signature", fieldname), &signature); + if (!*cursor) { + printf("**TRUNCATED**\n"); + return; + } + printf("lease_rates="); + printf("{\n"); + printwire_lease_rates(tal_fmt(NULL, "%s.lease_rates", fieldname), cursor, plen); + printf("}\n"); + } static const struct tlv_print_record_type print_tlvs_accept_tlvs[] = { { 1, printwire_tlv_accept_tlvs_option_upfront_shutdown_script }, + { 2, printwire_tlv_accept_tlvs_will_fund }, }; static void printwire_tlv_shutdown_tlvs_wrong_funding(const char *fieldname, const u8 **cursor, size_t *plen) @@ -518,6 +611,21 @@ static const struct tlv_print_record_type print_tlvs_shutdown_tlvs[] = { { 100, printwire_tlv_shutdown_tlvs_wrong_funding }, }; +static void printwire_tlv_node_ann_tlvs_option_will_fund(const char *fieldname, const u8 **cursor, size_t *plen) +{ + printf("(msg_name=%s)\n", "option_will_fund"); + + printf("lease_rates="); + printf("{\n"); + printwire_lease_rates(tal_fmt(NULL, "%s.lease_rates", fieldname), cursor, plen); + printf("}\n"); + +} + +static const struct tlv_print_record_type print_tlvs_node_ann_tlvs[] = { + { 1, printwire_tlv_node_ann_tlvs_option_will_fund }, +}; + static void printwire_tlv_query_short_channel_ids_tlvs_query_flags(const char *fieldname, const u8 **cursor, size_t *plen) { printf("(msg_name=%s)\n", "query_flags"); @@ -2246,6 +2354,37 @@ void printwire_update_fee(const char *fieldname, const u8 *cursor) } + if (plen != 0) + printf("EXTRA: %s\n", tal_hexstr(NULL, cursor, plen)); +} +void printwire_update_blockheight(const char *fieldname, const u8 *cursor) +{ + + size_t plen = tal_count(cursor); + if (fromwire_u16(&cursor, &plen) != WIRE_UPDATE_BLOCKHEIGHT) { + printf("WRONG TYPE?!\n"); + return; + } + + printf("channel_id="); + struct channel_id channel_id; + fromwire_channel_id(&cursor, &plen, &channel_id); + + printwire_channel_id(tal_fmt(NULL, "%s.channel_id", fieldname), &channel_id); + if (!cursor) { + printf("**TRUNCATED**\n"); + return; + } + printf("blockheight="); + u32 blockheight = fromwire_u32(&cursor, &plen); + + printwire_u32(tal_fmt(NULL, "%s.blockheight", fieldname), &blockheight); + if (!cursor) { + printf("**TRUNCATED**\n"); + return; + } + + if (plen != 0) printf("EXTRA: %s\n", tal_hexstr(NULL, cursor, plen)); } @@ -2545,6 +2684,8 @@ void printwire_node_announcement(const char *fieldname, const u8 *cursor) printf("**TRUNCATED**\n"); return; } + printf("tlvs="); + printwire_tlvs(tal_fmt(NULL, "%s.tlvs", fieldname), &cursor, &plen, print_tlvs_node_ann_tlvs, ARRAY_SIZE(print_tlvs_node_ann_tlvs)); if (plen != 0) @@ -2922,6 +3063,9 @@ void printpeer_wire_tlv_message(const char *tlv_name, const u8 *msg) { if (strcmp(tlv_name, "shutdown_tlvs") == 0) { printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_shutdown_tlvs, ARRAY_SIZE(print_tlvs_shutdown_tlvs)); } + if (strcmp(tlv_name, "node_ann_tlvs") == 0) { + printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_node_ann_tlvs, ARRAY_SIZE(print_tlvs_node_ann_tlvs)); + } if (strcmp(tlv_name, "query_short_channel_ids_tlvs") == 0) { printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_query_short_channel_ids_tlvs, ARRAY_SIZE(print_tlvs_query_short_channel_ids_tlvs)); } @@ -2935,4 +3079,4 @@ void printpeer_wire_tlv_message(const char *tlv_name, const u8 *msg) { printwire_tlvs(tlv_name, &msg, &plen, print_tlvs_onion_message_tlvs, ARRAY_SIZE(print_tlvs_onion_message_tlvs)); } } -// SHA256STAMP:3ecafff6be37e4049f121dcd6816aada2818fc7c02099372d1358d1b5b9da1ca +// SHA256STAMP:df85cfbd9424cced7ea68df1088da25cfdf548a856c0a189677b4a52428c1914 diff --git a/wire/peer_printgen.h b/wire/peer_printgen.h index 7e494852b018..a0e81f0009e9 100644 --- a/wire/peer_printgen.h +++ b/wire/peer_printgen.h @@ -68,6 +68,8 @@ void printwire_revoke_and_ack(const char *fieldname, const u8 *cursor); void printwire_update_fee(const char *fieldname, const u8 *cursor); +void printwire_update_blockheight(const char *fieldname, const u8 *cursor); + void printwire_channel_reestablish(const char *fieldname, const u8 *cursor); void printwire_announcement_signatures(const char *fieldname, const u8 *cursor); @@ -92,8 +94,9 @@ void printwire_onion_message(const char *fieldname, const u8 *cursor); void printwire_witness_element(const char *fieldname, const u8 **cursor, size_t *plen); +void printwire_lease_rates(const char *fieldname, const u8 **cursor, size_t *plen); void printwire_channel_update_checksums(const char *fieldname, const u8 **cursor, size_t *plen); void printwire_channel_update_timestamps(const char *fieldname, const u8 **cursor, size_t *plen); void printwire_witness_stack(const char *fieldname, const u8 **cursor, size_t *plen); #endif /* LIGHTNING_WIRE_PEER_PRINTGEN_H */ -// SHA256STAMP:3ecafff6be37e4049f121dcd6816aada2818fc7c02099372d1358d1b5b9da1ca +// SHA256STAMP:df85cfbd9424cced7ea68df1088da25cfdf548a856c0a189677b4a52428c1914 diff --git a/wire/peer_wire.c b/wire/peer_wire.c index afc60a2203bb..21a6f6e52d6d 100644 --- a/wire/peer_wire.c +++ b/wire/peer_wire.c @@ -21,6 +21,7 @@ static bool unknown_type(enum peer_wire t) case WIRE_COMMITMENT_SIGNED: case WIRE_REVOKE_AND_ACK: case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_CHANNEL_ANNOUNCEMENT: @@ -83,6 +84,7 @@ bool is_msg_for_gossipd(const u8 *cursor) case WIRE_COMMITMENT_SIGNED: case WIRE_REVOKE_AND_ACK: case WIRE_UPDATE_FEE: + case WIRE_UPDATE_BLOCKHEIGHT: case WIRE_CHANNEL_REESTABLISH: case WIRE_ANNOUNCEMENT_SIGNATURES: case WIRE_GOSSIP_TIMESTAMP_FILTER: diff --git a/wire/peer_wire.csv b/wire/peer_wire.csv index 40b78f1751c4..7e0777f1be93 100644 --- a/wire/peer_wire.csv +++ b/wire/peer_wire.csv @@ -143,6 +143,9 @@ msgdata,open_channel2,tlvs,opening_tlvs, tlvtype,opening_tlvs,option_upfront_shutdown_script,1 tlvdata,opening_tlvs,option_upfront_shutdown_script,shutdown_len,u16, tlvdata,opening_tlvs,option_upfront_shutdown_script,shutdown_scriptpubkey,byte,shutdown_len +tlvtype,opening_tlvs,request_funds,3 +tlvdata,opening_tlvs,request_funds,requested_sats,u64, +tlvdata,opening_tlvs,request_funds,blockheight,u32, msgtype,accept_channel2,65 msgdata,accept_channel2,channel_id,channel_id, msgdata,accept_channel2,funding_satoshis,u64, @@ -162,6 +165,15 @@ msgdata,accept_channel2,tlvs,accept_tlvs, tlvtype,accept_tlvs,option_upfront_shutdown_script,1 tlvdata,accept_tlvs,option_upfront_shutdown_script,shutdown_len,u16, tlvdata,accept_tlvs,option_upfront_shutdown_script,shutdown_scriptpubkey,byte,shutdown_len +tlvtype,accept_tlvs,will_fund,2 +tlvdata,accept_tlvs,will_fund,signature,signature, +tlvdata,accept_tlvs,will_fund,lease_rates,lease_rates, +subtype,lease_rates +subtypedata,lease_rates,funding_weight,u16, +subtypedata,lease_rates,lease_fee_basis,u16, +subtypedata,lease_rates,channel_fee_max_proportional_thousandths,u16, +subtypedata,lease_rates,lease_fee_base_sat,u32, +subtypedata,lease_rates,channel_fee_max_base_msat,tu32, msgtype,init_rbf,72 msgdata,init_rbf,channel_id,channel_id, msgdata,init_rbf,funding_satoshis,u64, @@ -215,6 +227,9 @@ msgdata,revoke_and_ack,next_per_commitment_point,point, msgtype,update_fee,134 msgdata,update_fee,channel_id,channel_id, msgdata,update_fee,feerate_per_kw,u32, +msgtype,update_blockheight,137 +msgdata,update_blockheight,channel_id,channel_id, +msgdata,update_blockheight,blockheight,u32, msgtype,channel_reestablish,136 msgdata,channel_reestablish,channel_id,channel_id, msgdata,channel_reestablish,next_commitment_number,u64, @@ -249,6 +264,9 @@ msgdata,node_announcement,rgb_color,byte,3 msgdata,node_announcement,alias,byte,32 msgdata,node_announcement,addrlen,u16, msgdata,node_announcement,addresses,byte,addrlen +msgdata,node_announcement,tlvs,node_ann_tlvs, +tlvtype,node_ann_tlvs,option_will_fund,1 +tlvdata,node_ann_tlvs,option_will_fund,lease_rates,lease_rates, msgtype,channel_update,258 msgdata,channel_update,signature,signature, msgdata,channel_update,chain_hash,chain_hash, diff --git a/wire/peer_wiregen.c b/wire/peer_wiregen.c index d025048e5afb..2c03846c86ed 100644 --- a/wire/peer_wiregen.c +++ b/wire/peer_wiregen.c @@ -49,6 +49,7 @@ const char *peer_wire_name(int e) case WIRE_COMMITMENT_SIGNED: return "WIRE_COMMITMENT_SIGNED"; case WIRE_REVOKE_AND_ACK: return "WIRE_REVOKE_AND_ACK"; case WIRE_UPDATE_FEE: return "WIRE_UPDATE_FEE"; + case WIRE_UPDATE_BLOCKHEIGHT: return "WIRE_UPDATE_BLOCKHEIGHT"; case WIRE_CHANNEL_REESTABLISH: return "WIRE_CHANNEL_REESTABLISH"; case WIRE_ANNOUNCEMENT_SIGNATURES: return "WIRE_ANNOUNCEMENT_SIGNATURES"; case WIRE_CHANNEL_ANNOUNCEMENT: return "WIRE_CHANNEL_ANNOUNCEMENT"; @@ -98,6 +99,7 @@ bool peer_wire_is_defined(u16 type) case WIRE_COMMITMENT_SIGNED:; case WIRE_REVOKE_AND_ACK:; case WIRE_UPDATE_FEE:; + case WIRE_UPDATE_BLOCKHEIGHT:; case WIRE_CHANNEL_REESTABLISH:; case WIRE_ANNOUNCEMENT_SIGNATURES:; case WIRE_CHANNEL_ANNOUNCEMENT:; @@ -138,6 +140,26 @@ fromwire_u8_array(cursor, plen, witness_element->witness, len); return witness_element; } +/* SUBTYPE: LEASE_RATES */ +void towire_lease_rates(u8 **p, const struct lease_rates *lease_rates) +{ + + towire_u16(p, lease_rates->funding_weight); + towire_u16(p, lease_rates->lease_fee_basis); + towire_u16(p, lease_rates->channel_fee_max_proportional_thousandths); + towire_u32(p, lease_rates->lease_fee_base_sat); + towire_tu32(p, lease_rates->channel_fee_max_base_msat); +} +void fromwire_lease_rates(const u8 **cursor, size_t *plen, struct lease_rates *lease_rates) +{ + + lease_rates->funding_weight = fromwire_u16(cursor, plen); + lease_rates->lease_fee_basis = fromwire_u16(cursor, plen); + lease_rates->channel_fee_max_proportional_thousandths = fromwire_u16(cursor, plen); + lease_rates->lease_fee_base_sat = fromwire_u32(cursor, plen); + lease_rates->channel_fee_max_base_msat = fromwire_tu32(cursor, plen); +} + /* SUBTYPE: CHANNEL_UPDATE_CHECKSUMS */ void towire_channel_update_checksums(u8 **p, const struct channel_update_checksums *channel_update_checksums) { @@ -609,20 +631,46 @@ static void fromwire_tlv_opening_tlvs_option_upfront_shutdown_script(const u8 ** r->option_upfront_shutdown_script->shutdown_scriptpubkey = shutdown_len ? tal_arr(r->option_upfront_shutdown_script, u8, shutdown_len) : NULL; fromwire_u8_array(cursor, plen, r->option_upfront_shutdown_script->shutdown_scriptpubkey, shutdown_len); } +/* OPENING_TLVS MSG: request_funds */ +static u8 *towire_tlv_opening_tlvs_request_funds(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_opening_tlvs *r = vrecord; + u8 *ptr; + + if (!r->request_funds) + return NULL; + + + ptr = tal_arr(ctx, u8, 0); + + towire_u64(&ptr, r->request_funds->requested_sats); + + towire_u32(&ptr, r->request_funds->blockheight); + return ptr; +} +static void fromwire_tlv_opening_tlvs_request_funds(const u8 **cursor, size_t *plen, void *vrecord) +{ + struct tlv_opening_tlvs *r = vrecord; + + r->request_funds = tal(r, struct tlv_opening_tlvs_request_funds); + r->request_funds->requested_sats = fromwire_u64(cursor, plen); + r->request_funds->blockheight = fromwire_u32(cursor, plen); +} static const struct tlv_record_type tlvs_opening_tlvs[] = { { 1, towire_tlv_opening_tlvs_option_upfront_shutdown_script, fromwire_tlv_opening_tlvs_option_upfront_shutdown_script }, + { 3, towire_tlv_opening_tlvs_request_funds, fromwire_tlv_opening_tlvs_request_funds }, }; void towire_opening_tlvs(u8 **pptr, const struct tlv_opening_tlvs *record) { - towire_tlv(pptr, tlvs_opening_tlvs, 1, record); + towire_tlv(pptr, tlvs_opening_tlvs, 2, record); } bool fromwire_opening_tlvs(const u8 **cursor, size_t *max, struct tlv_opening_tlvs *record) { - return fromwire_tlv(cursor, max, tlvs_opening_tlvs, 1, record, &record->fields); + return fromwire_tlv(cursor, max, tlvs_opening_tlvs, 2, record, &record->fields); } bool opening_tlvs_is_valid(const struct tlv_opening_tlvs *record, size_t *err_index) @@ -669,20 +717,46 @@ static void fromwire_tlv_accept_tlvs_option_upfront_shutdown_script(const u8 **c r->option_upfront_shutdown_script->shutdown_scriptpubkey = shutdown_len ? tal_arr(r->option_upfront_shutdown_script, u8, shutdown_len) : NULL; fromwire_u8_array(cursor, plen, r->option_upfront_shutdown_script->shutdown_scriptpubkey, shutdown_len); } +/* ACCEPT_TLVS MSG: will_fund */ +static u8 *towire_tlv_accept_tlvs_will_fund(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_accept_tlvs *r = vrecord; + u8 *ptr; + + if (!r->will_fund) + return NULL; + + + ptr = tal_arr(ctx, u8, 0); + + towire_secp256k1_ecdsa_signature(&ptr, &r->will_fund->signature); + + towire_lease_rates(&ptr, &r->will_fund->lease_rates); + return ptr; +} +static void fromwire_tlv_accept_tlvs_will_fund(const u8 **cursor, size_t *plen, void *vrecord) +{ + struct tlv_accept_tlvs *r = vrecord; + + r->will_fund = tal(r, struct tlv_accept_tlvs_will_fund); + fromwire_secp256k1_ecdsa_signature(cursor, plen, &r->will_fund->signature); + fromwire_lease_rates(cursor, plen, &r->will_fund->lease_rates); +} static const struct tlv_record_type tlvs_accept_tlvs[] = { { 1, towire_tlv_accept_tlvs_option_upfront_shutdown_script, fromwire_tlv_accept_tlvs_option_upfront_shutdown_script }, + { 2, towire_tlv_accept_tlvs_will_fund, fromwire_tlv_accept_tlvs_will_fund }, }; void towire_accept_tlvs(u8 **pptr, const struct tlv_accept_tlvs *record) { - towire_tlv(pptr, tlvs_accept_tlvs, 1, record); + towire_tlv(pptr, tlvs_accept_tlvs, 2, record); } bool fromwire_accept_tlvs(const u8 **cursor, size_t *max, struct tlv_accept_tlvs *record) { - return fromwire_tlv(cursor, max, tlvs_accept_tlvs, 1, record, &record->fields); + return fromwire_tlv(cursor, max, tlvs_accept_tlvs, 2, record, &record->fields); } bool accept_tlvs_is_valid(const struct tlv_accept_tlvs *record, size_t *err_index) @@ -748,6 +822,61 @@ bool shutdown_tlvs_is_valid(const struct tlv_shutdown_tlvs *record, size_t *err_ } +struct tlv_node_ann_tlvs *tlv_node_ann_tlvs_new(const tal_t *ctx) +{ + /* Initialize everything to NULL. (Quiet, C pedants!) */ + struct tlv_node_ann_tlvs *inst = talz(ctx, struct tlv_node_ann_tlvs); + + /* Initialized the fields to an empty array. */ + inst->fields = tal_arr(inst, struct tlv_field, 0); + return inst; +} + +/* NODE_ANN_TLVS MSG: option_will_fund */ +static u8 *towire_tlv_node_ann_tlvs_option_will_fund(const tal_t *ctx, const void *vrecord) +{ + const struct tlv_node_ann_tlvs *r = vrecord; + u8 *ptr; + + if (!r->option_will_fund) + return NULL; + + + ptr = tal_arr(ctx, u8, 0); + + towire_lease_rates(&ptr, r->option_will_fund); + return ptr; +} +static void fromwire_tlv_node_ann_tlvs_option_will_fund(const u8 **cursor, size_t *plen, void *vrecord) +{ + struct tlv_node_ann_tlvs *r = vrecord; + + r->option_will_fund = tal(r, struct lease_rates); + +fromwire_lease_rates(cursor, plen, &*r->option_will_fund); +} + +static const struct tlv_record_type tlvs_node_ann_tlvs[] = { + { 1, towire_tlv_node_ann_tlvs_option_will_fund, fromwire_tlv_node_ann_tlvs_option_will_fund }, +}; + +void towire_node_ann_tlvs(u8 **pptr, const struct tlv_node_ann_tlvs *record) +{ + towire_tlv(pptr, tlvs_node_ann_tlvs, 1, record); +} + + +bool fromwire_node_ann_tlvs(const u8 **cursor, size_t *max, struct tlv_node_ann_tlvs *record) +{ + return fromwire_tlv(cursor, max, tlvs_node_ann_tlvs, 1, record, &record->fields); +} + +bool node_ann_tlvs_is_valid(const struct tlv_node_ann_tlvs *record, size_t *err_index) +{ + return tlv_fields_valid(record->fields, err_index); +} + + struct tlv_query_short_channel_ids_tlvs *tlv_query_short_channel_ids_tlvs_new(const tal_t *ctx) { /* Initialize everything to NULL. (Quiet, C pedants!) */ @@ -1926,6 +2055,29 @@ bool fromwire_update_fee(const void *p, struct channel_id *channel_id, u32 *feer return cursor != NULL; } +/* WIRE: UPDATE_BLOCKHEIGHT */ +u8 *towire_update_blockheight(const tal_t *ctx, const struct channel_id *channel_id, u32 blockheight) +{ + u8 *p = tal_arr(ctx, u8, 0); + + towire_u16(&p, WIRE_UPDATE_BLOCKHEIGHT); + towire_channel_id(&p, channel_id); + towire_u32(&p, blockheight); + + return memcheck(p, tal_count(p)); +} +bool fromwire_update_blockheight(const void *p, struct channel_id *channel_id, u32 *blockheight) +{ + const u8 *cursor = p; + size_t plen = tal_count(p); + + if (fromwire_u16(&cursor, &plen) != WIRE_UPDATE_BLOCKHEIGHT) + return false; + fromwire_channel_id(&cursor, &plen, channel_id); + *blockheight = fromwire_u32(&cursor, &plen); + return cursor != NULL; +} + /* WIRE: CHANNEL_REESTABLISH */ u8 *towire_channel_reestablish(const tal_t *ctx, const struct channel_id *channel_id, u64 next_commitment_number, u64 next_revocation_number, const struct secret *your_last_per_commitment_secret, const struct pubkey *my_current_per_commitment_point) { @@ -2031,7 +2183,7 @@ bool fromwire_channel_announcement(const tal_t *ctx, const void *p, secp256k1_ec } /* WIRE: NODE_ANNOUNCEMENT */ -u8 *towire_node_announcement(const tal_t *ctx, const secp256k1_ecdsa_signature *signature, const u8 *features, u32 timestamp, const struct node_id *node_id, const u8 rgb_color[3], const u8 alias[32], const u8 *addresses) +u8 *towire_node_announcement(const tal_t *ctx, const secp256k1_ecdsa_signature *signature, const u8 *features, u32 timestamp, const struct node_id *node_id, const u8 rgb_color[3], const u8 alias[32], const u8 *addresses, const struct tlv_node_ann_tlvs *tlvs) { u16 flen = tal_count(features); u16 addrlen = tal_count(addresses); @@ -2047,10 +2199,11 @@ u8 *towire_node_announcement(const tal_t *ctx, const secp256k1_ecdsa_signature * towire_u8_array(&p, alias, 32); towire_u16(&p, addrlen); towire_u8_array(&p, addresses, addrlen); + towire_node_ann_tlvs(&p, tlvs); return memcheck(p, tal_count(p)); } -bool fromwire_node_announcement(const tal_t *ctx, const void *p, secp256k1_ecdsa_signature *signature, u8 **features, u32 *timestamp, struct node_id *node_id, u8 rgb_color[3], u8 alias[32], u8 **addresses) +bool fromwire_node_announcement(const tal_t *ctx, const void *p, secp256k1_ecdsa_signature *signature, u8 **features, u32 *timestamp, struct node_id *node_id, u8 rgb_color[3], u8 alias[32], u8 **addresses, struct tlv_node_ann_tlvs *tlvs) { u16 flen; u16 addrlen; @@ -2073,6 +2226,7 @@ bool fromwire_node_announcement(const tal_t *ctx, const void *p, secp256k1_ecdsa // 2nd case addresses *addresses = addrlen ? tal_arr(ctx, u8, addrlen) : NULL; fromwire_u8_array(&cursor, &plen, *addresses, addrlen); + fromwire_node_ann_tlvs(&cursor, &plen, tlvs); return cursor != NULL; } @@ -2330,4 +2484,4 @@ bool fromwire_channel_update_option_channel_htlc_max(const void *p, secp256k1_ec *htlc_maximum_msat = fromwire_amount_msat(&cursor, &plen); return cursor != NULL; } -// SHA256STAMP:3ecafff6be37e4049f121dcd6816aada2818fc7c02099372d1358d1b5b9da1ca +// SHA256STAMP:df85cfbd9424cced7ea68df1088da25cfdf548a856c0a189677b4a52428c1914 diff --git a/wire/peer_wiregen.h b/wire/peer_wiregen.h index f4b5b7722b43..0b764169dcf0 100644 --- a/wire/peer_wiregen.h +++ b/wire/peer_wiregen.h @@ -46,6 +46,7 @@ enum peer_wire { WIRE_COMMITMENT_SIGNED = 132, WIRE_REVOKE_AND_ACK = 133, WIRE_UPDATE_FEE = 134, + WIRE_UPDATE_BLOCKHEIGHT = 137, WIRE_CHANNEL_REESTABLISH = 136, WIRE_ANNOUNCEMENT_SIGNATURES = 259, WIRE_CHANNEL_ANNOUNCEMENT = 256, @@ -73,6 +74,13 @@ bool peer_wire_is_defined(u16 type); struct witness_element { u8 *witness; }; +struct lease_rates { + u16 funding_weight; + u16 lease_fee_basis; + u16 channel_fee_max_proportional_thousandths; + u32 lease_fee_base_sat; + u32 channel_fee_max_base_msat; +}; struct channel_update_checksums { u32 checksum_node_id_1; u32 checksum_node_id_2; @@ -92,9 +100,17 @@ struct tlv_n1_tlv3 { struct tlv_opening_tlvs_option_upfront_shutdown_script { u8 *shutdown_scriptpubkey; }; +struct tlv_opening_tlvs_request_funds { + u64 requested_sats; + u32 blockheight; +}; struct tlv_accept_tlvs_option_upfront_shutdown_script { u8 *shutdown_scriptpubkey; }; +struct tlv_accept_tlvs_will_fund { + secp256k1_ecdsa_signature signature; + struct lease_rates lease_rates; +}; struct tlv_shutdown_tlvs_wrong_funding { struct bitcoin_txid txid; u32 outnum; @@ -158,6 +174,7 @@ struct tlv_opening_tlvs { /* TODO The following explicit fields could just point into the * tlv_field entries above to save on memory. */ struct tlv_opening_tlvs_option_upfront_shutdown_script *option_upfront_shutdown_script; + struct tlv_opening_tlvs_request_funds *request_funds; }; struct tlv_accept_tlvs { /* Raw fields including unknown ones. */ @@ -166,6 +183,7 @@ struct tlv_accept_tlvs { /* TODO The following explicit fields could just point into the * tlv_field entries above to save on memory. */ struct tlv_accept_tlvs_option_upfront_shutdown_script *option_upfront_shutdown_script; + struct tlv_accept_tlvs_will_fund *will_fund; }; struct tlv_shutdown_tlvs { /* Raw fields including unknown ones. */ @@ -175,6 +193,14 @@ struct tlv_shutdown_tlvs { * tlv_field entries above to save on memory. */ struct tlv_shutdown_tlvs_wrong_funding *wrong_funding; }; +struct tlv_node_ann_tlvs { + /* Raw fields including unknown ones. */ + struct tlv_field *fields; + + /* TODO The following explicit fields could just point into the + * tlv_field entries above to save on memory. */ + struct lease_rates *option_will_fund; +}; struct tlv_query_short_channel_ids_tlvs { /* Raw fields including unknown ones. */ struct tlv_field *fields; @@ -529,6 +555,43 @@ void towire_shutdown_tlvs(u8 **pptr, const struct tlv_shutdown_tlvs *record); bool shutdown_tlvs_is_valid(const struct tlv_shutdown_tlvs *record, size_t *err_index); +struct tlv_node_ann_tlvs *tlv_node_ann_tlvs_new(const tal_t *ctx); + +/** + * Deserialize a TLV stream for the node_ann_tlvs namespace. + * + * This function will parse any TLV stream, as long as the type, length and + * value fields are formatted correctly. Fields that are not known in the + * current namespace are stored in the `fields` member. Validity can be + * checked using node_ann_tlvs_is_valid. + */ +bool fromwire_node_ann_tlvs(const u8 **cursor, size_t *max, + struct tlv_node_ann_tlvs * record); + +/** + * Serialize a TLV stream for the node_ann_tlvs namespace. + * + * This function only considers known fields from the node_ann_tlvs namespace, + * and will ignore any fields that may be stored in the `fields` member. This + * ensures that the resulting stream is valid according to + * `node_ann_tlvs_is_valid`. + */ +void towire_node_ann_tlvs(u8 **pptr, const struct tlv_node_ann_tlvs *record); + +/** + * Check that the TLV stream is valid. + * + * Enforces the followin validity rules: + * - Types must be in monotonic non-repeating order + * - We must understand all even types + * + * Returns false if an error was detected, otherwise returns true. If err_index + * is non-null and we detect an error it is set to the index of the first error + * detected. + */ +bool node_ann_tlvs_is_valid(const struct tlv_node_ann_tlvs *record, + size_t *err_index); + struct tlv_query_short_channel_ids_tlvs *tlv_query_short_channel_ids_tlvs_new(const tal_t *ctx); /** @@ -681,6 +744,10 @@ bool onion_message_tlvs_is_valid(const struct tlv_onion_message_tlvs *record, void towire_witness_element(u8 **p, const struct witness_element *witness_element); struct witness_element *fromwire_witness_element(const tal_t *ctx, const u8 **cursor, size_t *plen); +/* SUBTYPE: LEASE_RATES */ +void towire_lease_rates(u8 **p, const struct lease_rates *lease_rates); +void fromwire_lease_rates(const u8 **cursor, size_t *plen, struct lease_rates *lease_rates); + /* SUBTYPE: CHANNEL_UPDATE_CHECKSUMS */ void towire_channel_update_checksums(u8 **p, const struct channel_update_checksums *channel_update_checksums); void fromwire_channel_update_checksums(const u8 **cursor, size_t *plen, struct channel_update_checksums *channel_update_checksums); @@ -809,6 +876,10 @@ bool fromwire_revoke_and_ack(const void *p, struct channel_id *channel_id, struc u8 *towire_update_fee(const tal_t *ctx, const struct channel_id *channel_id, u32 feerate_per_kw); bool fromwire_update_fee(const void *p, struct channel_id *channel_id, u32 *feerate_per_kw); +/* WIRE: UPDATE_BLOCKHEIGHT */ +u8 *towire_update_blockheight(const tal_t *ctx, const struct channel_id *channel_id, u32 blockheight); +bool fromwire_update_blockheight(const void *p, struct channel_id *channel_id, u32 *blockheight); + /* WIRE: CHANNEL_REESTABLISH */ u8 *towire_channel_reestablish(const tal_t *ctx, const struct channel_id *channel_id, u64 next_commitment_number, u64 next_revocation_number, const struct secret *your_last_per_commitment_secret, const struct pubkey *my_current_per_commitment_point); bool fromwire_channel_reestablish(const void *p, struct channel_id *channel_id, u64 *next_commitment_number, u64 *next_revocation_number, struct secret *your_last_per_commitment_secret, struct pubkey *my_current_per_commitment_point); @@ -822,8 +893,8 @@ u8 *towire_channel_announcement(const tal_t *ctx, const secp256k1_ecdsa_signatur bool fromwire_channel_announcement(const tal_t *ctx, const void *p, secp256k1_ecdsa_signature *node_signature_1, secp256k1_ecdsa_signature *node_signature_2, secp256k1_ecdsa_signature *bitcoin_signature_1, secp256k1_ecdsa_signature *bitcoin_signature_2, u8 **features, struct bitcoin_blkid *chain_hash, struct short_channel_id *short_channel_id, struct node_id *node_id_1, struct node_id *node_id_2, struct pubkey *bitcoin_key_1, struct pubkey *bitcoin_key_2); /* WIRE: NODE_ANNOUNCEMENT */ -u8 *towire_node_announcement(const tal_t *ctx, const secp256k1_ecdsa_signature *signature, const u8 *features, u32 timestamp, const struct node_id *node_id, const u8 rgb_color[3], const u8 alias[32], const u8 *addresses); -bool fromwire_node_announcement(const tal_t *ctx, const void *p, secp256k1_ecdsa_signature *signature, u8 **features, u32 *timestamp, struct node_id *node_id, u8 rgb_color[3], u8 alias[32], u8 **addresses); +u8 *towire_node_announcement(const tal_t *ctx, const secp256k1_ecdsa_signature *signature, const u8 *features, u32 timestamp, const struct node_id *node_id, const u8 rgb_color[3], const u8 alias[32], const u8 *addresses, const struct tlv_node_ann_tlvs *tlvs); +bool fromwire_node_announcement(const tal_t *ctx, const void *p, secp256k1_ecdsa_signature *signature, u8 **features, u32 *timestamp, struct node_id *node_id, u8 rgb_color[3], u8 alias[32], u8 **addresses, struct tlv_node_ann_tlvs *tlvs); /* WIRE: CHANNEL_UPDATE */ u8 *towire_channel_update(const tal_t *ctx, const secp256k1_ecdsa_signature *signature, const struct bitcoin_blkid *chain_hash, const struct short_channel_id *short_channel_id, u32 timestamp, u8 message_flags, u8 channel_flags, u16 cltv_expiry_delta, struct amount_msat htlc_minimum_msat, u32 fee_base_msat, u32 fee_proportional_millionths); @@ -859,4 +930,4 @@ bool fromwire_channel_update_option_channel_htlc_max(const void *p, secp256k1_ec #endif /* LIGHTNING_WIRE_PEER_WIREGEN_H */ -// SHA256STAMP:3ecafff6be37e4049f121dcd6816aada2818fc7c02099372d1358d1b5b9da1ca +// SHA256STAMP:df85cfbd9424cced7ea68df1088da25cfdf548a856c0a189677b4a52428c1914 diff --git a/wire/test/run-peer-wire.c b/wire/test/run-peer-wire.c index 160a889bbf35..63cff64940bf 100644 --- a/wire/test/run-peer-wire.c +++ b/wire/test/run-peer-wire.c @@ -167,6 +167,7 @@ struct msg_node_announcement { u8 alias[32]; u8 *features; u8 *addresses; + struct tlv_node_ann_tlvs *tlvs; }; struct msg_open_channel { struct bitcoin_blkid chain_hash; @@ -371,12 +372,14 @@ static void *towire_struct_node_announcement(const tal_t *ctx, &s->node_id, s->rgb_color, s->alias, - s->addresses); + s->addresses, + s->tlvs); } static struct msg_node_announcement *fromwire_struct_node_announcement(const tal_t *ctx, const void *p) { struct msg_node_announcement *s = tal(ctx, struct msg_node_announcement); + s->tlvs = tlv_node_ann_tlvs_new(s); if (!fromwire_node_announcement(s, p, &s->signature, &s->features, @@ -384,7 +387,8 @@ static struct msg_node_announcement *fromwire_struct_node_announcement(const tal &s->node_id, s->rgb_color, s->alias, - &s->addresses)) + &s->addresses, + s->tlvs)) return tal_free(s); return s; } @@ -925,9 +929,40 @@ static bool update_add_htlc_eq(const struct msg_update_add_htlc *a, return eq_with(a, b, onion_routing_packet); } +static bool +lease_rates_eq(const struct lease_rates *a, + const struct lease_rates *b) +{ + return eq_field(a, b, channel_fee_max_base_msat) + && eq_field(a, b, channel_fee_max_proportional_thousandths) + && eq_field(a, b, funding_weight) + && eq_field(a, b, lease_fee_base_sat) + && eq_field(a, b, lease_fee_basis); +} + static bool node_announcement_eq(const struct msg_node_announcement *a, const struct msg_node_announcement *b) { + /* Both or neither */ + if (!a->tlvs != !b->tlvs) { + abort(); + return false; + } + + if (!a->tlvs) + goto body_check; + + /* Both or neither */ + if (!a->tlvs->option_will_fund != !b->tlvs->option_will_fund) { + return false; + } + + if (a->tlvs->option_will_fund + && !lease_rates_eq(a->tlvs->option_will_fund, + b->tlvs->option_will_fund)) + return false; + +body_check: return eq_with(a, b, node_id) && eq_field(a, b, rgb_color) && eq_field(a, b, alias) @@ -1165,11 +1200,15 @@ int main(int argc, char *argv[]) memset(na.features, 2, 2); na.addresses = tal_arr(ctx, u8, 2); memset(na.addresses, 2, 2); + na.tlvs= tlv_node_ann_tlvs_new(ctx); + na.tlvs->option_will_fund = tal(ctx, struct lease_rates); + memset(na.tlvs->option_will_fund, 2, + sizeof(*na.tlvs->option_will_fund)); msg = towire_struct_node_announcement(ctx, &na); na2 = fromwire_struct_node_announcement(ctx, msg); assert(node_announcement_eq(&na, na2)); - test_corruption(&na, na2, node_announcement); + test_corruption_tlv(&na, na2, node_announcement); tal_free(ctx); common_shutdown();