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

Conversation

niftynei
Copy link
Contributor

@niftynei niftynei commented Apr 22, 2021

Plugin to democratize a peer's ability to contribute funds to incoming openchannel request.

Current knobs include:

  • three kinds of policy: match (% of their funding), available (% of your available funds), and fixed (sat value)
  • minimum and maximum funding from peer
  • minimum and maximum that you'll put in a channel
  • fuzzing (defaults to 5%)
  • fund_probability. only funds X% of incoming requests
  • reserve-tank. never* spends available funds beneath this amount

*assuming races never happen

Future work: we don't take feerates into account right now we calculating the total available funds to add.

When an RPC originates from a plugin, and there's no associated command,
it's useful to be able to signal that you're finished.
@niftynei niftynei added this to the v0.10.1 milestone Apr 22, 2021
@niftynei niftynei requested a review from ZmnSCPxj April 22, 2021 23:05
@niftynei niftynei requested a review from cdecker as a code owner April 22, 2021 23:05
/* Set the channel_id back to what they originally sent us,
* in open_channel2, since they won't know the combined
* channel_id yet for errors */
state->channel_id = tmp_chan_id;
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems like we should only be updating the channel_id field when we send the reply?

@@ -244,6 +244,10 @@ struct openchannel2_payload {
/* What's the maximum amount of funding
* this channel can hold */
struct amount_sat channel_max;
/* Snapshot of our current utxo set, available */
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure this is actually that useful: it's more accurate for them to count utxos themselves and take into account the feerate. It's a little more work, but I think the plug-in should listutxos itself...

@rustyrussell
Copy link
Contributor

Here's proposed patch for the tmp_channel_id issue: spec should probably be updated too?

dualopend: don't use final channel_id for accepter_start2

The other side doesn't know it until *after* it parses this msg.  We
add a quick hack to still allow old nodes to work (for now!).

This also fixes a bug (spotted by @niftynei) where any errors we sent
before accepter_start2 would have the new (unknowable!) channel_id
rather than the temp one.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>

diff --git a/openingd/dualopend.c b/openingd/dualopend.c
index ad8d5420e..d753dff8e 100644
--- a/openingd/dualopend.c
+++ b/openingd/dualopend.c
@@ -1897,7 +1897,6 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
 	struct bitcoin_blkid chain_hash;
 	struct tlv_opening_tlvs *open_tlv;
 	char *err_reason;
-	struct channel_id tmp_chan_id;
 	u8 *msg;
 	struct amount_sat total;
 	enum dualopend_wire msg_type;
@@ -1939,7 +1938,7 @@ 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,
 			      &state->their_points.revocation);
 	if (!channel_id_eq(&state->channel_id, &tmp_chan_id))
 		negotiation_failed(state, "open_channel2 channel_id incorrect."
@@ -1949,12 +1948,6 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
 				   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);
-
-
 	/* Save feerate on the state as well */
 	state->feerate_per_kw_funding = tx_state->feerate_per_kw_funding;
 
@@ -2105,6 +2098,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");
 
@@ -2479,6 +2477,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,
@@ -2492,14 +2507,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))

We removed/changed the fields on the openchannel2 hook but never
updated this test (which doesn't run with the CI for #reasons)
@niftynei niftynei force-pushed the nifty/accept-plugin branch 3 times, most recently from 69aca6a to f773987 Compare April 25, 2021 14:51
The other side doesn't know it until *after* it parses this msg.  We
add a quick hack to still allow old nodes to work (for now!).

This also fixes a bug (spotted by @niftynei) where any errors we sent
before accepter_start2 would have the new (unknowable!) channel_id
rather than the temp one.

Authored-by: Rusty Russell <rusty@rustcorp.com.au>
@niftynei niftynei force-pushed the nifty/accept-plugin branch from f773987 to fc77f51 Compare April 26, 2021 16:07
@niftynei
Copy link
Contributor Author

Ok, patches have been applied (and already folded in mea culpa).

Copy link
Contributor

@rustyrussell rustyrussell left a comment

Choose a reason for hiding this comment

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

Minor fixes only...

/* Signals that we've completed a command. Useful for when
* there's no `cmd` present */
struct command_result *command_done(void);

Copy link
Contributor

Choose a reason for hiding this comment

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

Drop this commit. The one place you end up using it, you do indeed have a command. You just don't use it!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't want to actually do anything with the command though. There's no response to send on our side when this completes, it's a 'silent no-op'?

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, this is because the "cmd" is actually a notification, which doesn't want a response. It does, however, want to be freed!

I'll submit a followup PR which cleans this up everywhere...

plugins/funder.c Outdated
err, json_tok_full_len(result),
json_tok_full(buf, result));

/* we skip resrved funds */
Copy link
Contributor

Choose a reason for hiding this comment

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

Need to buy a vwl?

@@ -89,6 +89,32 @@ default_funder_policy(enum funder_opt policy,
100);
}

char *funder_check_policy(struct funder_policy *policy)
Copy link
Contributor

Choose a reason for hiding this comment

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

const struct funder_policy?

err = funder_check_policy(&current_policy);
if (err)
return command_done_err(cmd, JSONRPC2_INVALID_PARAMS,
err, NULL);
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you need to modify a copy, then assign it back to current_policy at the end.

Otherwise, setting a bad value causes an error, but sticks around...

plugins/funder.c Outdated
return command_finished(cmd, res);
}

const struct plugin_command commands[] = { {
Copy link
Contributor

Choose a reason for hiding this comment

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

static const struct plugin_command commands?

doc/PLUGINS.md Outdated
@@ -1157,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": "200000msat",
Copy link
Contributor

Choose a reason for hiding this comment

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

It will never be this value though, so it's misleading.

@@ -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",
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.

@niftynei niftynei force-pushed the nifty/accept-plugin branch from fc77f51 to de7bbf0 Compare April 28, 2021 22:26
niftynei added 13 commits April 28, 2021 17:40
Changelog-Added: Plugins: add a `channel_max_msat` value to the `openchannel2` hook. Tells you the total max funding this channel is allowed to have.
For dual-funding's accepter plugin, we only want to send
psbts that need to be signed to `signpsbt`; this lets us quickly check
if they're "signable"
You gotta send over an amount if you send a psbt!
This is set by the peer and is non-negotiable. We're not even going to
check if you got it right. You were told about it via `openchannel2`.

It is what it is.
For scaling/multiplying sats
Behold! An immaculately concepted plugin for configuring your node to do
amazing things*

*fund channel open requests

Changelog-Added:  Plugins: Add `funder` plugin, which allows you to setup a policy for funding v2 channel open requests. Requres --experimental-dual-fund option
Compute available_funds locally, instead of getting it from the
openchannel2 hook payload.

Suggested-By: Rusty Russell @rustyrussell
If you're using the regtest node, turn on dual funding and
automatically attempt to dual fund at a 100% match for every channel
open that you do.
Error out if we've got the wrong info
Changelog-Added: Plugins: `funder` plugin now has new command `funderupdate` which will show current funding configuration and allow you to modify them
Changelog-Added: Plugins: `rbf_channel` hook has `channel_max_msat` parameter
Fund an RBF same as you would an open. We dont' do anything fancy with
our inputs -- we dont' have a copy of the last PSBT that was sent.
Dual-funded channels won't match the old amount check. Good news is
we're already returning the outnum so we just use that.
@niftynei niftynei force-pushed the nifty/accept-plugin branch from de7bbf0 to de9102a Compare April 29, 2021 16:31
@niftynei
Copy link
Contributor Author

Updated, as per comments.

@rustyrussell
Copy link
Contributor

Ack de9102a

@rustyrussell rustyrussell merged commit 29155c2 into ElementsProject:master May 3, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants