rpc: add queryrates

Undocumented RPC call for asking a peer what their rates are
This commit is contained in:
niftynei 2021-06-11 16:32:27 -05:00 committed by neil saitug
parent d7f76a6e1f
commit 89f210fcd6
6 changed files with 279 additions and 9 deletions

View File

@ -1542,6 +1542,43 @@ 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);
was_pending(command_success(cmd, response));
}
static void handle_peer_locked(struct subd *dualopend, const u8 *msg)
{
struct pubkey remote_per_commit;
@ -2358,6 +2395,105 @@ static struct command_result *init_set_feerate(struct command *cmd,
return NULL;
}
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);
subd_send_msg(channel->owner, take(msg));
return command_still_pending(cmd);
}
static struct command_result *json_openchannel_init(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
@ -2507,7 +2643,8 @@ static struct command_result *json_openchannel_init(struct command *cmd,
*feerate_per_kw_funding,
channel->channel_flags,
*request_amt,
get_block_height(cmd->ld->topology));
get_block_height(cmd->ld->topology),
false);
subd_send_msg(channel->owner, take(msg));
return command_still_pending(cmd);
@ -2801,6 +2938,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;
@ -2851,6 +2991,14 @@ static unsigned int dual_opend_msg(struct subd *dualopend,
return 0;
}
static const struct json_command queryrates_command = {
"queryrates",
"channels",
json_queryrates,
"Ask a peer what their contribution and liquidity rates are"
" for the given {amount} and {requested_amt}"
};
static const struct json_command openchannel_init_command = {
"openchannel_init",
"channels",
@ -2889,6 +3037,7 @@ static const struct json_command openchannel_abort_command = {
"Abort {channel_id}'s open. Usable while `commitment_signed=false`."
};
AUTODATA(json_command, &queryrates_command);
AUTODATA(json_command, &openchannel_init_command);
AUTODATA(json_command, &openchannel_update_command);
AUTODATA(json_command, &openchannel_signed_command);

View File

@ -2004,7 +2004,6 @@ static void accepter_start(struct state *state, const u8 *oc2_msg)
lease_blockheight_start = 0;
}
/* BOLT-* #2
* If the peer's revocation basepoint is unknown (e.g.
* `open_channel2`), a temporary `channel_id` should be found
@ -2492,6 +2491,7 @@ static void opener_start(struct state *state, u8 *msg)
char *err_reason;
struct amount_sat total, requested_sats;
u32 current_blockheight;
bool dry_run;
struct tx_state *tx_state = state->tx_state;
if (!fromwire_dualopend_opener_init(state, msg,
@ -2502,7 +2502,8 @@ static void opener_start(struct state *state, u8 *msg)
&tx_state->feerate_per_kw_funding,
&state->channel_flags,
&requested_sats,
&current_blockheight))
&current_blockheight,
&dry_run))
master_badmsg(WIRE_DUALOPEND_OPENER_INIT, msg);
state->our_role = TX_INITIATOR;
@ -2620,6 +2621,31 @@ 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");
}
/* FIXME: BOLT QUOTE */
if (open_tlv->request_funds && a_tlv->will_fund) {
/* OK! lease mode activated */
}
/* Check that total funding doesn't overflow */
if (!amount_sat_add(&total, tx_state->opener_funding,
tx_state->accepter_funding))
@ -3387,6 +3413,7 @@ 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:
break;
}

View File

@ -167,6 +167,7 @@ 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,
# dualopend->master received tx_sigs from peer
msgtype,dualopend_funding_sigs,7010
@ -213,3 +214,10 @@ 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,
msgdata,dualopend_dry_run,lease_rates,?lease_rates,

Can't render this file because it has a wrong number of fields in line 14.

View File

@ -46,6 +46,7 @@ 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";
}
snprintf(invalidbuf, sizeof(invalidbuf), "INVALID %i", e);
@ -81,6 +82,7 @@ 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:;
return true;
}
return false;
@ -621,7 +623,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, struct amount_sat requested_sats, u32 blockheight)
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)
{
u16 local_shutdown_len = tal_count(local_shutdown_scriptpubkey);
u8 *p = tal_arr(ctx, u8, 0);
@ -636,10 +638,11 @@ u8 *towire_dualopend_opener_init(const tal_t *ctx, const struct wally_psbt *psbt
towire_u8(&p, channel_flags);
towire_amount_sat(&p, requested_sats);
towire_u32(&p, blockheight);
towire_bool(&p, dry_run);
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, struct amount_sat *requested_sats, u32 *blockheight)
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)
{
u16 local_shutdown_len;
@ -659,6 +662,7 @@ bool fromwire_dualopend_opener_init(const tal_t *ctx, const void *p, struct wall
*channel_flags = fromwire_u8(&cursor, &plen);
*requested_sats = fromwire_amount_sat(&cursor, &plen);
*blockheight = fromwire_u32(&cursor, &plen);
*dry_run = fromwire_bool(&cursor, &plen);
return cursor != NULL;
}
@ -932,4 +936,42 @@ bool fromwire_dualopend_dev_memleak_reply(const void *p, bool *leak)
*leak = fromwire_bool(&cursor, &plen);
return cursor != NULL;
}
// SHA256STAMP:d8a31a5de292561ccfb5b7050c911b590db44a50209e02469179904708328861
/* 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);
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);
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;
}
// SHA256STAMP:a0ce1d5dfc6518a35d2e346c770754c498558ac426e3040526e10bbbc237db6f

View File

@ -72,6 +72,8 @@ 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,
};
const char *dualopend_wire_name(int e);
@ -153,8 +155,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, struct amount_sat requested_sats, u32 blockheight);
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);
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);
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);
/* WIRE: DUALOPEND_FUNDING_SIGS */
/* dualopend->master received tx_sigs from peer */
@ -215,6 +217,11 @@ 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);
#endif /* LIGHTNING_OPENINGD_DUALOPEND_WIREGEN_H */
// SHA256STAMP:d8a31a5de292561ccfb5b7050c911b590db44a50209e02469179904708328861
// SHA256STAMP:a0ce1d5dfc6518a35d2e346c770754c498558ac426e3040526e10bbbc237db6f

View File

@ -15,6 +15,43 @@ 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')
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)
result = l1.rpc.queryrates(l2.info['id'], amount, amount * 10)
assert result['our_funding_msat'] == Millisatoshi(amount * 1000)
assert result['their_funding_msat'] == Millisatoshi(0)
assert 'weight_charge' not in result
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.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