diff --git a/doc/developers-guide/plugin-development/hooks.md b/doc/developers-guide/plugin-development/hooks.md index 1e1cfdbdf41d..9852b646e8b5 100644 --- a/doc/developers-guide/plugin-development/hooks.md +++ b/doc/developers-guide/plugin-development/hooks.md @@ -245,6 +245,7 @@ This hook is called whenever a remote peer tries to fund a channel to us using t "to_self_delay": 5, "max_accepted_htlcs": 483, "channel_flags": 1, + "channel_type": {'bits': [12, 22], 'names': ['static_remotekey/even', 'anchors/even']}, "locktime": 2453, "channel_max_msat": 16777215000, "requested_lease_msat": 100000000, diff --git a/lightningd/dual_open_control.c b/lightningd/dual_open_control.c index 7835dcf660a6..fae8b1d4eee0 100644 --- a/lightningd/dual_open_control.c +++ b/lightningd/dual_open_control.c @@ -274,6 +274,7 @@ struct openchannel2_payload { u32 lease_blockheight_start; u32 node_blockheight; bool req_confirmed_ins_remote; + struct channel_type *channel_type; struct amount_sat accepter_funding; struct wally_psbt *psbt; @@ -325,6 +326,7 @@ static void openchannel2_hook_serialize(struct openchannel2_payload *payload, } json_add_bool(stream, "require_confirmed_inputs", payload->req_confirmed_ins_remote); + json_add_channel_type(stream, "channel_type", payload->channel_type); json_object_end(stream); } @@ -2098,7 +2100,8 @@ static void accepter_got_offer(struct subd *dualopend, &payload->shutdown_scriptpubkey, &payload->requested_lease_amt, &payload->lease_blockheight_start, - &payload->req_confirmed_ins_remote)) { + &payload->req_confirmed_ins_remote, + &payload->channel_type)) { channel_internal_error(channel, "Bad DUALOPEND_GOT_OFFER: %s", tal_hex(tmpctx, msg)); return; diff --git a/lightningd/opening_control.c b/lightningd/opening_control.c index 43163835a02c..36e89a513f56 100644 --- a/lightningd/opening_control.c +++ b/lightningd/opening_control.c @@ -647,6 +647,7 @@ struct openchannel_hook_payload { u8 channel_flags; u8 *shutdown_scriptpubkey; const u8 *our_upfront_shutdown_script; + struct channel_type *channel_type; char *errmsg; }; @@ -675,6 +676,7 @@ static void openchannel_hook_serialize(struct openchannel_hook_payload *payload, if (tal_count(payload->shutdown_scriptpubkey) != 0) json_add_hex_talarr(stream, "shutdown_scriptpubkey", payload->shutdown_scriptpubkey); + json_add_channel_type(stream, "channel_type", payload->channel_type); json_object_end(stream); /* .openchannel */ } @@ -854,7 +856,8 @@ static void opening_got_offer(struct subd *openingd, &payload->to_self_delay, &payload->max_accepted_htlcs, &payload->channel_flags, - &payload->shutdown_scriptpubkey)) { + &payload->shutdown_scriptpubkey, + &payload->channel_type)) { log_broken(openingd->log, "Malformed opening_got_offer %s", tal_hex(tmpctx, msg)); tal_free(openingd); diff --git a/openingd/dualopend.c b/openingd/dualopend.c index f6c183a917d2..4ef1ae721380 100644 --- a/openingd/dualopend.c +++ b/openingd/dualopend.c @@ -2528,7 +2528,8 @@ static void accepter_start(struct state *state, const u8 *oc2_msg) state->upfront_shutdown_script[REMOTE], state->requested_lease, tx_state->blockheight, - state->require_confirmed_inputs[REMOTE]); + state->require_confirmed_inputs[REMOTE], + state->channel_type); wire_sync_write(REQ_FD, take(msg)); msg = wire_sync_read(tmpctx, REQ_FD); diff --git a/openingd/dualopend_wire.csv b/openingd/dualopend_wire.csv index 2d0479f56094..1c2589abf9f0 100644 --- a/openingd/dualopend_wire.csv +++ b/openingd/dualopend_wire.csv @@ -98,6 +98,7 @@ 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, msgdata,dualopend_got_offer,require_confirmed_inputs,bool, +msgdata,dualopend_got_offer,ctype,channel_type, # master->dualopend: reply back with our first funding info/contribs msgtype,dualopend_got_offer_reply,7105 diff --git a/openingd/openingd.c b/openingd/openingd.c index e98609d09d9a..1990a9d2933f 100644 --- a/openingd/openingd.c +++ b/openingd/openingd.c @@ -1009,7 +1009,8 @@ static u8 *fundee_channel(struct state *state, const u8 *open_channel_msg) state->remoteconf.to_self_delay, state->remoteconf.max_accepted_htlcs, channel_flags, - state->upfront_shutdown_script[REMOTE]); + state->upfront_shutdown_script[REMOTE], + state->channel_type); wire_sync_write(REQ_FD, take(msg)); msg = wire_sync_read(tmpctx, REQ_FD); diff --git a/openingd/openingd_wire.csv b/openingd/openingd_wire.csv index c48e62b1cd71..c70de6cf01d2 100644 --- a/openingd/openingd_wire.csv +++ b/openingd/openingd_wire.csv @@ -44,6 +44,7 @@ msgdata,openingd_got_offer,max_accepted_htlcs,u16, msgdata,openingd_got_offer,channel_flags,u8, msgdata,openingd_got_offer,shutdown_len,u16, msgdata,openingd_got_offer,shutdown_scriptpubkey,u8,shutdown_len +msgdata,openingd_got_offer,ctype,channel_type, # master->openingd: optional rejection message msgtype,openingd_got_offer_reply,6105 diff --git a/tests/plugins/openchannel_hook_accepter.py b/tests/plugins/openchannel_hook_accepter.py index be409fa45cb4..58c988127c54 100755 --- a/tests/plugins/openchannel_hook_accepter.py +++ b/tests/plugins/openchannel_hook_accepter.py @@ -16,7 +16,7 @@ plugin = Plugin() -def run_openchannel(funding_sats_str, plugin): +def run_openchannel(funding_sats_str, ctype, plugin): # Convert from string to satoshis funding_sats = Millisatoshi(funding_sats_str).to_satoshi() @@ -46,19 +46,19 @@ def run_openchannel(funding_sats_str, plugin): return {'result': 'continue', 'close_to': 'bc1qlq8srqnz64wgklmqvurv7qnr4rvtq2u96hhfg2'} # - otherwise: accept and don't include the close_to - plugin.log("accept by design") + plugin.log(f"accept by design: channel_type {ctype}") return {'result': 'continue'} @plugin.hook('openchannel') def on_openchannel(openchannel, plugin, **kwargs): - return run_openchannel(openchannel['funding_msat'], plugin) + return run_openchannel(openchannel['funding_msat'], openchannel['channel_type'], plugin) @plugin.hook('openchannel2') def on_openchannel2(openchannel2, plugin, **kwargs): """ Support for v2 channel opens """ - return run_openchannel(openchannel2['their_funding_msat'], plugin) + return run_openchannel(openchannel2['their_funding_msat'], openchannel2['channel_type'], plugin) plugin.run() diff --git a/tests/test_plugin.py b/tests/test_plugin.py index ece9a020dc66..6c88952016bb 100644 --- a/tests/test_plugin.py +++ b/tests/test_plugin.py @@ -695,8 +695,10 @@ def test_openchannel_hook(node_factory, bitcoind): if 'anchors/even' in only_one(l1.rpc.listpeerchannels()['channels'])['channel_type']['names']: feerate = 3750 + expected['channel_type'] = r"{'bits': \[12, 22\], 'names': \['static_remotekey/even', 'anchors/even'\]}" else: feerate = 7500 + expected['channel_type'] = r"{'bits': \[12\], 'names': \['static_remotekey/even'\]}" if l2.config('experimental-dual-fund'): # openchannel2 var checks expected.update({ @@ -4328,3 +4330,17 @@ def test_peer_storage(node_factory, bitcoind): # This should never happen assert not l1.daemon.is_in_log(r'PeerStorageFailed') assert not l2.daemon.is_in_log(r'PeerStorageFailed') + + +@pytest.mark.openchannel('v1') +@pytest.mark.openchannel('v2') +def test_openchannel_hook_channel_type(node_factory, bitcoind): + """openchannel hook should get a channel_type field. + """ + opts = {'plugin': os.path.join(os.getcwd(), 'tests/plugins/openchannel_hook_accepter.py')} + l1, l2 = node_factory.line_graph(2, opts=opts) + + if 'anchors/even' in only_one(l1.rpc.listpeerchannels()['channels'])['channel_type']['names']: + l2.daemon.wait_for_log(r"plugin-openchannel_hook_accepter.py: accept by design: channel_type {'bits': \[12, 22\], 'names': \['static_remotekey/even', 'anchors/even'\]}") + else: + l2.daemon.wait_for_log(r"plugin-openchannel_hook_accepter.py: accept by design: channel_type {'bits': \[12\], 'names': \['static_remotekey/even'\]}")