Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add a funder plugin #4489

Merged
merged 20 commits into from
May 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5065f41
libplugin: add no-op command complete function
niftynei Apr 19, 2021
2001b1f
tests: remove EXPERIMENTAL_FEATURE flag from openchannel2 hooks
niftynei Apr 20, 2021
536d032
plugin-tests: cleanup outdated openchannel2 hook fields
niftynei Apr 20, 2021
a7ef21e
dualopend: don't use final channel_id for accepter_start2
niftynei Apr 20, 2021
5d47c67
openchannel2: add channel_max_msat to openchannel2 hook payload
niftynei Apr 20, 2021
dea5f2d
psbt-open: method to quickly check if has our input
niftynei Apr 21, 2021
e489b66
openchannel2/rbf hooks: reject if response malformed
niftynei Apr 21, 2021
c88f307
dualfund: set the locktime for the user-provided PSBT
niftynei Apr 21, 2021
890ac99
openchannel: add missing string args to format string
niftynei Apr 21, 2021
d8534e7
amount: `amount_sat_scale` method
niftynei Apr 23, 2021
e4eef8e
funder: add a plugin, `funder`. policies for dual-funding
niftynei Apr 22, 2021
e13b310
funder: use listfunds to fetch utxo data
niftynei Apr 23, 2021
86eb1e0
funder-test: tests for our policy configurations
niftynei Apr 22, 2021
45e87a9
contrib: if you're in dev mode, use dual-funding (with matching)
niftynei Apr 23, 2021
ec846cd
funder: sanitize inputs
niftynei Apr 22, 2021
9795006
funder: `funderupdate` command to see/set configs
niftynei Apr 22, 2021
d97ef4d
rbf_channel hook: add channel_max_msat parameter
niftynei Apr 23, 2021
1e0f05f
funder: handle RBF callback
niftynei Apr 23, 2021
884f1d4
pyln-testing: use provided outnum instead of trying to find it
niftynei Apr 29, 2021
de9102a
tests: add test for funder options
niftynei Apr 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions common/amount.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,10 +325,10 @@ WARN_UNUSED_RESULT bool amount_msat_add_sat(struct amount_msat *val,
}

WARN_UNUSED_RESULT bool amount_msat_scale(struct amount_msat *val,
struct amount_msat sat,
struct amount_msat msat,
double scale)
{
double scaled = sat.millisatoshis * scale;
double scaled = msat.millisatoshis * scale;

/* If mantissa is < 64 bits, a naive "if (scaled >
* UINT64_MAX)" doesn't work. Stick to powers of 2. */
Expand All @@ -338,6 +338,20 @@ WARN_UNUSED_RESULT bool amount_msat_scale(struct amount_msat *val,
return true;
}

WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val,
struct amount_sat sat,
double scale)
{
double scaled = sat.satoshis * scale;

/* If mantissa is < 64 bits, a naive "if (scaled >
* UINT64_MAX)" doesn't work. Stick to powers of 2. */
if (scaled >= (double)((u64)1 << 63) * 2)
return false;
val->satoshis = scaled;
return true;
}

bool amount_sat_eq(struct amount_sat a, struct amount_sat b)
{
return a.satoshis == b.satoshis;
Expand Down
3 changes: 3 additions & 0 deletions common/amount.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ WARN_UNUSED_RESULT bool amount_sat_sub_msat(struct amount_msat *val,
WARN_UNUSED_RESULT bool amount_msat_scale(struct amount_msat *val,
struct amount_msat msat,
double scale);
WARN_UNUSED_RESULT bool amount_sat_scale(struct amount_sat *val,
struct amount_sat sat,
double scale);

struct amount_msat amount_msat_div(struct amount_msat msat, u64 div);
struct amount_sat amount_sat_div(struct amount_sat sat, u64 div);
Expand Down
10 changes: 10 additions & 0 deletions common/psbt_open.c
Original file line number Diff line number Diff line change
Expand Up @@ -481,3 +481,13 @@ bool psbt_input_is_ours(const struct wally_psbt_input *input)
PSBT_TYPE_INPUT_MARKER, &unused);
return !(!result);
}

bool psbt_has_our_input(const struct wally_psbt *psbt)
{
for (size_t i = 0; i < psbt->num_inputs; i++) {
if (psbt_input_is_ours(&psbt->inputs[i]))
return true;
}

return false;
}
4 changes: 4 additions & 0 deletions common/psbt_open.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,8 @@ void psbt_input_mark_ours(const tal_t *ctx,
*/
bool psbt_input_is_ours(const struct wally_psbt_input *input);

/* psbt_has_our_input - Returns true if this psbt contains
* any input that is ours
*/
bool psbt_has_our_input(const struct wally_psbt *psbt);
#endif /* LIGHTNING_COMMON_PSBT_OPEN_H */
3 changes: 1 addition & 2 deletions contrib/pyln-testing/pyln/testing/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -864,8 +864,7 @@ def has_funds_on_addr(addr):
txnum = i

scid = "{}x{}x{}".format(self.bitcoin.rpc.getblockcount(),
txnum,
get_tx_p2wsh_outnum(self.bitcoin, res['tx'], amount))
txnum, res['outnum'])

if wait_for_active:
self.wait_channel_active(scid)
Expand Down
12 changes: 10 additions & 2 deletions contrib/startup_regtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,16 @@ start_nodes() {

# If we've configured to use developer, add dev options
if $LIGHTNINGD --help | grep -q dev-fast-gossip; then
echo "dev-fast-gossip" >> "/tmp/l$i-$network/config"
echo "dev-bitcoind-poll=5" >> "/tmp/l$i-$network/config"
cat <<- EOF >> "/tmp/l$i-$network/config"
dev-fast-gossip
dev-bitcoind-poll=5
experimental-dual-fund
funder-policy=match
funder-policy-mod=1000
funder-min-their-funding=10000
funder-per-channel-max=100000
funder-fuzz-percent=0
EOF
fi


Expand Down
4 changes: 3 additions & 1 deletion doc/PLUGINS.md
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,7 @@ the v2 protocol, and it has passed basic sanity checks:
"max_accepted_htlcs": 483,
"channel_flags": 1
"locktime": 2453,
"channel_max_msat": "16777215000msat"
}
}
```
Expand Down Expand Up @@ -1156,7 +1157,8 @@ requests an RBF for a channel funding transaction.
"funding_feerate_per_kw": 7500,
"feerate_our_max": 10000,
"feerate_our_min": 253,
"locktime": 2453,
"channel_max_msat": "16777215000msat",
"locktime": 2453
}
}
```
Expand Down
34 changes: 32 additions & 2 deletions lightningd/dual_open_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ struct rbf_channel_payload {
/* General info */
u32 feerate_our_max;
u32 feerate_our_min;
/* What's the maximum amount of funding
* this channel can hold */
struct amount_sat channel_max;

/* Returned from hook */
struct amount_sat our_funding;
Expand All @@ -206,6 +209,8 @@ rbf_channel_hook_serialize(struct rbf_channel_payload *payload,
payload->feerate_our_min);
json_add_num(stream, "funding_feerate_per_kw",
payload->funding_feerate_per_kw);
json_add_amount_sat_only(stream, "channel_max_msat",
payload->channel_max);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this converts to msat, and that fails for UINT64_MAX, this channel_max will actually only appear for non-wumbo channels. Which is probably OK, but should be documented.

Field should also be called "channel_max_msat".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK. also updated the channel_max field that exist on openchannel2 hook.

json_object_end(stream);
}

Expand Down Expand Up @@ -241,6 +246,9 @@ struct openchannel2_payload {
u8 channel_flags;
u32 locktime;
u8 *shutdown_scriptpubkey;
/* What's the maximum amount of funding
* this channel can hold */
struct amount_sat channel_max;

struct amount_sat accepter_funding;
struct wally_psbt *psbt;
Expand Down Expand Up @@ -278,6 +286,8 @@ openchannel2_hook_serialize(struct openchannel2_payload *payload,
if (tal_bytelen(payload->shutdown_scriptpubkey) != 0)
json_add_hex_talarr(stream, "shutdown_scriptpubkey",
payload->shutdown_scriptpubkey);
json_add_amount_sat_only(stream, "channel_max_msat",
payload->channel_max);
json_object_end(stream);
}

Expand Down Expand Up @@ -617,6 +627,10 @@ rbf_channel_hook_deserialize(struct rbf_channel_payload *payload,
"our_funding_msat", &payload->our_funding))
fatal("Plugin failed to supply our_funding_msat field");

if (payload->psbt
&& amount_sat_eq(payload->our_funding, AMOUNT_SAT(0)))
fatal("Plugin failed to supply our_funding_msat field");

if (!payload->psbt &&
!amount_sat_eq(payload->our_funding, AMOUNT_SAT(0))) {

Expand Down Expand Up @@ -777,8 +791,12 @@ openchannel2_hook_deserialize(struct openchannel2_payload *payload,
&payload->accepter_funding))
fatal("Plugin failed to supply our_funding_msat field");

if (!payload->psbt &&
!amount_sat_eq(payload->accepter_funding, AMOUNT_SAT(0))) {
if (payload->psbt
&& amount_sat_eq(payload->accepter_funding, AMOUNT_SAT(0)))
fatal("Plugin failed to supply our_funding_msat field");

if (!payload->psbt
&& !amount_sat_eq(payload->accepter_funding, AMOUNT_SAT(0))) {
/* 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";
Expand Down Expand Up @@ -1636,6 +1654,12 @@ static void rbf_got_offer(struct subd *dualopend, const u8 *msg)
/* No error message known (yet) */
payload->err_msg = NULL;

payload->channel_max = chainparams->max_funding;
if (feature_negotiated(dualopend->ld->our_features,
channel->peer->their_features,
OPT_LARGE_CHANNELS))
payload->channel_max = AMOUNT_SAT(UINT_MAX);

tal_add_destructor2(dualopend, rbf_channel_remove_dualopend, payload);
plugin_hook_call_rbf_channel(dualopend->ld, payload);
}
Expand Down Expand Up @@ -1695,6 +1719,12 @@ static void accepter_got_offer(struct subd *dualopend,
payload->feerate_our_min = feerate_min(dualopend->ld, NULL);
payload->feerate_our_max = feerate_max(dualopend->ld, NULL);

payload->channel_max = chainparams->max_funding;
if (feature_negotiated(dualopend->ld->our_features,
channel->peer->their_features,
OPT_LARGE_CHANNELS))
payload->channel_max = AMOUNT_SAT(UINT64_MAX);

tal_add_destructor2(dualopend, openchannel2_remove_dualopend, payload);
plugin_hook_call_openchannel2(dualopend->ld, payload);
}
Expand Down
58 changes: 36 additions & 22 deletions openingd/dualopend.c
Original file line number Diff line number Diff line change
Expand Up @@ -1896,8 +1896,8 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
{
struct bitcoin_blkid chain_hash;
struct tlv_opening_tlvs *open_tlv;
struct channel_id cid, full_cid;
char *err_reason;
struct channel_id tmp_chan_id;
u8 *msg;
struct amount_sat total;
enum dualopend_wire msg_type;
Expand All @@ -1907,7 +1907,7 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
open_tlv = tlv_opening_tlvs_new(tmpctx);

if (!fromwire_open_channel2(oc2_msg, &chain_hash,
&state->channel_id, /* Temporary! */
&cid,
&tx_state->feerate_per_kw_funding,
&state->feerate_per_kw_commitment,
&tx_state->opener_funding,
Expand Down Expand Up @@ -1939,21 +1939,15 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
* `open_channel2`), a temporary `channel_id` should be found
* by using a zeroed out basepoint for the unknown peer.
*/
derive_tmp_channel_id(&tmp_chan_id,
derive_tmp_channel_id(&state->channel_id, /* Temporary! */
&state->their_points.revocation);
if (!channel_id_eq(&state->channel_id, &tmp_chan_id))
if (!channel_id_eq(&state->channel_id, &cid))
negotiation_failed(state, "open_channel2 channel_id incorrect."
" Expected %s, received %s",
type_to_string(tmpctx, struct channel_id,
&tmp_chan_id),
&state->channel_id),
type_to_string(tmpctx, struct channel_id,
&state->channel_id));

/* Everything's ok. Let's figure out the actual channel_id now */
derive_channel_id_v2(&state->channel_id,
&state->our_points.revocation,
&state->their_points.revocation);

&cid));

/* Save feerate on the state as well */
state->feerate_per_kw_funding = tx_state->feerate_per_kw_funding;
Expand Down Expand Up @@ -1990,8 +1984,12 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
return;
}

/* We send the 'real' channel id over to lightningd */
derive_channel_id_v2(&full_cid,
&state->our_points.revocation,
&state->their_points.revocation);
msg = towire_dualopend_got_offer(NULL,
&state->channel_id,
&full_cid,
tx_state->opener_funding,
tx_state->remoteconf.dust_limit,
tx_state->remoteconf.max_htlc_value_in_flight,
Expand All @@ -2010,7 +2008,6 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
if ((msg_type = fromwire_peektype(msg)) == WIRE_DUALOPEND_FAIL) {
if (!fromwire_dualopend_fail(msg, msg, &err_reason))
master_badmsg(msg_type, msg);

open_err_warn(state, "%s", err_reason);
return;
}
Expand All @@ -2024,6 +2021,9 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
if (!tx_state->psbt)
tx_state->psbt = create_psbt(tx_state, 0, 0,
tx_state->tx_locktime);
else
/* Locktimes must match! */
tx_state->psbt->tx->locktime = tx_state->tx_locktime;

/* Check that total funding doesn't overflow */
if (!amount_sat_add(&total, tx_state->opener_funding,
Expand Down Expand Up @@ -2105,6 +2105,11 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
&state->first_per_commitment_point[LOCAL],
a_tlv);

/* Everything's ok. Let's figure out the actual channel_id now */
derive_channel_id_v2(&state->channel_id,
&state->our_points.revocation,
&state->their_points.revocation);

sync_crypto_write(state->pps, msg);
peer_billboard(false, "channel open: accept sent, waiting for reply");

Expand Down Expand Up @@ -2479,6 +2484,23 @@ static void opener_start(struct state *state, u8 *msg)
open_err_fatal(state, "Parsing accept_channel2 %s",
tal_hex(msg, msg));

if (!channel_id_eq(&cid, &state->channel_id)) {
struct channel_id future_chan_id;
/* FIXME: v0.10.0 actually replied with the complete channel id here,
* so we need to accept it for now */
derive_channel_id_v2(&future_chan_id,
&state->our_points.revocation,
&state->their_points.revocation);
if (!channel_id_eq(&cid, &future_chan_id)) {
peer_failed_err(state->pps, &cid,
"accept_channel2 ids don't match: "
"expected %s, got %s",
type_to_string(msg, struct channel_id,
&state->channel_id),
type_to_string(msg, struct channel_id, &cid));
}
}

if (a_tlv->option_upfront_shutdown_script) {
state->upfront_shutdown_script[REMOTE]
= tal_steal(state,
Expand All @@ -2492,14 +2514,6 @@ static void opener_start(struct state *state, u8 *msg)
&state->our_points.revocation,
&state->their_points.revocation);

if (!channel_id_eq(&cid, &state->channel_id))
peer_failed_err(state->pps, &cid,
"accept_channel2 ids don't match: "
"expected %s, got %s",
type_to_string(msg, struct channel_id,
&state->channel_id),
type_to_string(msg, struct channel_id, &cid));

/* Check that total funding doesn't overflow */
if (!amount_sat_add(&total, tx_state->opener_funding,
tx_state->accepter_funding))
Expand Down
1 change: 1 addition & 0 deletions plugins/.gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
autoclean
bcli
funder
pay
spenderp
multifundchannel
Expand Down
15 changes: 15 additions & 0 deletions plugins/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,18 @@ PLUGIN_SPENDER_HEADER := \
plugins/spender/openchannel.h
PLUGIN_SPENDER_OBJS := $(PLUGIN_SPENDER_SRC:.c=.o)

PLUGIN_FUNDER_SRC := \
plugins/funder.c \
plugins/funder_policy.c
PLUGIN_FUNDER_HEADER := \
plugins/funder_policy.h
PLUGIN_FUNDER_OBJS := $(PLUGIN_FUNDER_SRC:.c=.o)

PLUGIN_ALL_SRC := \
$(PLUGIN_AUTOCLEAN_SRC) \
$(PLUGIN_BCLI_SRC) \
$(PLUGIN_FETCHINVOICE_SRC) \
$(PLUGIN_FUNDER_SRC) \
$(PLUGIN_KEYSEND_SRC) \
$(PLUGIN_TXPREPARE_SRC) \
$(PLUGIN_LIB_SRC) \
Expand All @@ -57,6 +65,7 @@ PLUGIN_ALL_SRC := \

PLUGIN_ALL_HEADER := \
$(PLUGIN_LIB_HEADER) \
$(PLUGIN_FUNDER_HEADER) \
$(PLUGIN_PAY_LIB_HEADER) \
$(PLUGIN_OFFERS_HEADER) \
$(PLUGIN_SPENDER_HEADER)
Expand All @@ -66,6 +75,7 @@ PLUGINS := \
plugins/autoclean \
plugins/bcli \
plugins/fetchinvoice \
plugins/funder \
plugins/keysend \
plugins/offers \
plugins/pay \
Expand Down Expand Up @@ -106,6 +116,7 @@ PLUGIN_COMMON_OBJS := \
common/memleak.o \
common/node_id.o \
common/param.o \
common/psbt_open.o \
common/pseudorand.o \
common/random_select.o \
common/setup.o \
Expand Down Expand Up @@ -137,9 +148,13 @@ plugins/offers: bitcoin/chainparams.o $(PLUGIN_OFFERS_OBJS) $(PLUGIN_LIB_OBJS) $

plugins/fetchinvoice: bitcoin/chainparams.o $(PLUGIN_FETCHINVOICE_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) common/bolt12.o common/bolt12_merkle.o common/iso4217.o $(WIRE_OBJS) bitcoin/block.o common/channel_id.o bitcoin/preimage.o $(JSMN_OBJS) $(CCAN_OBJS) common/gossmap.o common/fp16.o common/dijkstra.o common/route.o common/blindedpath.o common/hmac.o common/blinding.o

plugins/funder: bitcoin/chainparams.o bitcoin/psbt.o common/psbt_open.o $(PLUGIN_FUNDER_OBJS) $(PLUGIN_LIB_OBJS) $(PLUGIN_COMMON_OBJS) $(JSMN_OBJS) $(CCAN_OBJS)

$(PLUGIN_ALL_OBJS): $(PLUGIN_LIB_HEADER)

# Generated from PLUGINS definition in plugins/Makefile
ALL_C_HEADERS += plugins/list_of_builtin_plugins_gen.h
plugins/list_of_builtin_plugins_gen.h: plugins/Makefile Makefile
@$(call VERBOSE,GEN $@,echo "static const char *list_of_builtin_plugins[] = { $(foreach d,$(notdir $(PLUGINS)),\"$d\",) NULL };" > $@)

include plugins/test/Makefile
Loading