Skip to content

Commit

Permalink
Add RPC cmd for 'alt-addr' and config param for 'alt-bind-addr'
Browse files Browse the repository at this point in the history
Signed-off-by: Max Rantil <[email protected]>
  • Loading branch information
maxrantil committed Jun 29, 2024
1 parent b001741 commit d212cd9
Show file tree
Hide file tree
Showing 13 changed files with 344 additions and 37 deletions.
22 changes: 18 additions & 4 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -545,10 +545,8 @@ static void handle_peer_splice_locked(struct peer *peer, const u8 *msg)
static void send_peer_our_alt_address(struct peer *peer) {
struct pubkey node_id;

if (pubkey_from_node_id(&node_id, &peer->id)) {
u8 *msg = towire_peer_alt_address(peer, &node_id, peer->our_alt_addr);
peer_write(peer->pps, take(msg));
}
if (pubkey_from_node_id(&node_id, &peer->id))
peer_write(peer->pps, take(towire_peer_alt_address(peer, &node_id, peer->our_alt_addr)));
}

static void handle_peer_channel_ready(struct peer *peer, const u8 *msg)
Expand Down Expand Up @@ -5702,11 +5700,27 @@ static void handle_dev_quiesce(struct peer *peer, const u8 *msg)
maybe_send_stfu(peer);
}

static void handle_channeld_alt_address(struct peer *peer, const u8 *msg)
{
struct pubkey peer_pk;
u8 *our_alt_addr;

if (!fromwire_channeld_alt_address(peer, msg, &peer_pk, &our_alt_addr)) {
master_badmsg(WIRE_CHANNELD_ALT_ADDRESS, msg);
}

if (pubkey_from_node_id(&peer_pk, &peer->id))
peer_write(peer->pps, take(towire_peer_alt_address(peer, &peer_pk, our_alt_addr)));
}

static void req_in(struct peer *peer, const u8 *msg)
{
enum channeld_wire t = fromwire_peektype(msg);

switch (t) {
case WIRE_CHANNELD_ALT_ADDRESS:
handle_channeld_alt_address(peer, msg);
return;
case WIRE_CHANNELD_FUNDING_DEPTH:
handle_funding_depth(peer, msg);
return;
Expand Down
6 changes: 6 additions & 0 deletions channeld/channeld_wire.csv
Original file line number Diff line number Diff line change
Expand Up @@ -352,3 +352,9 @@ msgdata,channeld_upgraded,new_type,channel_type,
# Tell peer about our latest and greatest blockheight.
msgtype,channeld_blockheight,1012
msgdata,channeld_blockheight,blockheight,u32,

# master -> channeld Send peer alternative addresses
msgtype,channeld_alt_address,1014
msgdata,channeld_alt_address,node_id,point,
msgdata,channeld_alt_address,alt_addr_len,u16,
msgdata,channeld_alt_address,alt_addr,u8,alt_addr_len,
3 changes: 1 addition & 2 deletions connectd/connectd.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,8 +520,7 @@ void handle_peer_alt_address(struct peer *peer, const u8 *msg)
master_badmsg(WIRE_PEER_ALT_ADDRESS, msg);
}

msg = towire_connectd_alt_address(NULL, &peer_id, peer_alt_addr);
daemon_conn_send(peer->daemon->master, take(msg));
daemon_conn_send(peer->daemon->master, take(towire_connectd_alt_address(NULL, &peer_id, peer_alt_addr)));

tal_free(peer_alt_addr);
}
Expand Down
5 changes: 4 additions & 1 deletion contrib/startup_regtest.sh
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,10 @@ start_nodes() {
log-level=debug
log-file=$LIGHTNING_DIR/l$i/log
addr=localhost:$socket
alt-addr=127.21.21.21:$socket
# alt-addr=127.20.20.20:$socket #TODO, Test what happends when both default and RPC are used.
alt-bind-addr=127.21.21.21:$socket
alt-bind-addr=127.22.22.22:$socket
bind-addr=127.29.29.29:$socket
allow-deprecated-apis=false
developer
dev-fast-gossip
Expand Down
61 changes: 61 additions & 0 deletions lightningd/channel_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,7 @@ static unsigned channel_msg(struct subd *sd, const u8 *msg, const int *fds)
break;
/* And we never get these from channeld. */
case WIRE_CHANNELD_INIT:
case WIRE_CHANNELD_ALT_ADDRESS:
case WIRE_CHANNELD_FUNDING_DEPTH:
case WIRE_CHANNELD_OFFER_HTLC:
case WIRE_CHANNELD_FULFILL_HTLC:
Expand Down Expand Up @@ -2341,3 +2342,63 @@ static const struct json_command dev_quiesce_command = {
.dev_only = true,
};
AUTODATA(json_command, &dev_quiesce_command);

static struct command_result *json_alt_addr(struct command *cmd,
const char *buffer,
const jsmntok_t *obj UNNEEDED,
const jsmntok_t *params)
{
struct node_id *peer_node_id;
struct pubkey p_pk; //maybe take this away again (send node_id struct instead) to clean it up?
struct peer *peer;
struct channel *channel;
const char *our_alt_addr;
bool more_than_one;

if (!param_check(cmd, buffer, params,
p_req("node_id", param_node_id, &peer_node_id),
p_req("alt_addr", param_string, &our_alt_addr),
NULL))
return command_param_failed();

peer = peer_by_id(cmd->ld, peer_node_id);
if (!peer) {
return command_fail(cmd, JSONRPC2_INVALID_REQUEST,
"No such peer: %s",
fmt_node_id(cmd, peer_node_id));
}

channel = peer_any_channel(peer, channel_state_can_add_htlc, &more_than_one);
if (!channel || !channel->owner)
return command_fail(cmd, LIGHTNINGD, "Peer bad state");
/* This is a dev command: fix the api if you need this! */
if (more_than_one)
return command_fail(cmd, LIGHTNINGD, "More than one channel");

if (command_check_only(cmd))
return command_check_done(cmd);

if (pubkey_from_node_id(&p_pk, peer_node_id)) {
//TODO, make 'our_alt_addr into a double pointer, array of arrays, for sending many.
subd_send_msg(channel->owner, take(towire_channeld_alt_address(peer, &p_pk, (u8 *)our_alt_addr)));
}

//TODO, After adding the 'our_alt_addr' here,
// we need to check against that when accepting the incoming connection.
//ADD our_alt_addr into our db under the peer with the id we specify here to create the whitelist for later confirmation when accepting incomming connection.
wallet_add_alt_addr(cmd->ld->wallet->db, peer_node_id, our_alt_addr, true);

//TODO, We need to add this to 'listnodes' command too!
//TODO, How can we send the peer msg without having a channel to the peer?

return command_success(cmd, json_stream_success(cmd));
}

static const struct json_command alt_addr_command = {
"alt-addr",
"developer",
json_alt_addr,
"Select an alternative private address for peer-to-peer connections",
.dev_only = true,
};
AUTODATA(json_command, &alt_addr_command);
22 changes: 17 additions & 5 deletions lightningd/connect_control.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,23 @@ static void try_connect(const tal_t *ctx,
const struct wireaddr_internal *addrhint,
bool dns_fallback)
{
const struct wireaddr_internal *peer_alt_addr = NULL;
struct delayed_reconnect *d;
struct peer *peer;
const struct wireaddr_internal *alt_addr;

alt_addr = wallet_get_peer_alt_addr(ld->wallet, id);
/* This is where we handle the whitelist of alt addr,
TODO: whitelist of addr(s!)*/
if (ld->alt_bind_addr) {
peer_alt_addr = wallet_get_peer_alt_addr(ld->wallet, id);
if (peer_alt_addr) {
const char *formatted_peer_addr = fmt_wireaddr_internal(ctx, peer_alt_addr);
if (strcmp((char *)ld->alt_bind_addr, formatted_peer_addr) != 0) {
log_debug(ld->log, "No match for alt_bind_addr and peer_alt_addr: '%s' != '%s'\n", ld->alt_bind_addr, formatted_peer_addr);
tal_free(peer_alt_addr);
peer_alt_addr = NULL;
}
}
}

/* Don't stack, unless this is an instant reconnect */
d = delayed_reconnect_map_get(ld->delayed_reconnect_map, id);
Expand All @@ -352,7 +364,7 @@ static void try_connect(const tal_t *ctx,
d = tal(ctx, struct delayed_reconnect);
d->ld = ld;
d->id = *id;
d->addrhint = tal_dup_or_null(d, struct wireaddr_internal, alt_addr ? alt_addr : addrhint);
d->addrhint = tal_dup_or_null(d, struct wireaddr_internal, peer_alt_addr ? peer_alt_addr : addrhint);
d->dns_fallback = dns_fallback;
delayed_reconnect_map_add(ld->delayed_reconnect_map, d);
tal_add_destructor(d, destroy_delayed_reconnect);
Expand Down Expand Up @@ -566,13 +578,13 @@ static void handle_peer_alt_addr_in(struct lightningd *ld, const u8 *msg)

if (!fromwire_connectd_alt_address(tmpctx, msg, &peer_node_id, &peer_alt_addr)) {
log_broken(ld->log, "Malformed peer_alt_addr_msg: %s",
tal_hex(tmpctx, msg));
tal_hex(tmpctx, msg));
return;
}

struct node_id id;
node_id_from_pubkey(&id, &peer_node_id);
wallet_add_peer_alt_addr(ld->wallet->db, &id, (char *)peer_alt_addr);
wallet_add_alt_addr(ld->wallet->db, &id, (char *)peer_alt_addr, false);
}

static void connectd_start_shutdown_reply(struct subd *connectd,
Expand Down
2 changes: 2 additions & 0 deletions lightningd/lightningd.c
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,8 @@ static struct lightningd *new_lightningd(const tal_t *ctx)
ld->recover_secret = NULL;
ld->db_upgrade_ok = NULL;
ld->num_startup_connects = 0;
ld->our_alt_addr = NULL;
ld->alt_bind_addr = NULL;

/* --experimental-upgrade-protocol */
ld->experimental_upgrade_protocol = false;
Expand Down
5 changes: 4 additions & 1 deletion lightningd/lightningd.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,10 @@ struct lightningd {
struct wireaddr *announceable;

/* Alternative address for peer connections not publicly announced */
u8 *our_alt_addr;
u8 *our_alt_addr; //TODO, make into double pointer

/* Alternative binding address for peer connections not publicly announced */
u8 *alt_bind_addr; //TODO, make into double pointer

/* Current node announcement (if any) */
const u8 *node_announcement;
Expand Down
32 changes: 31 additions & 1 deletion lightningd/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,26 @@ static char *opt_add_alt_addr(const char *arg, struct lightningd *ld)
return opt_add_addr_withtype(arg, ld, ADDR_LISTEN);
}

static char *opt_add_alt_bind_addr(const char *arg, struct lightningd *ld)
{
assert(arg != NULL);

//TODO, Add somewhat a flag here to check later with whitelist.
//TODO, This needs to be a list later I think...
ld->alt_bind_addr = tal_free(ld->alt_bind_addr);
ld->alt_bind_addr = (u8 *)tal_strdup(ld, arg); //tal_arr_expand?

return opt_add_addr_withtype(arg, ld, ADDR_LISTEN);
}

static char *opt_add_alt_announce_addr(const char *arg, struct lightningd *ld)
{
assert(arg != NULL);
//TODO, MAKE THIS FUNCTION.

return opt_add_addr_withtype(arg, ld, ADDR_LISTEN);
}

static char *opt_subdaemon(const char *arg, struct lightningd *ld)
{
char *subdaemon;
Expand Down Expand Up @@ -1610,9 +1630,17 @@ static void register_opts(struct lightningd *ld)
opt_set_uintval,
opt_show_uintval, &ld->config.ip_discovery_port,
"Sets the public TCP port to use for announcing discovered IPs.");

clnopt_witharg("--alt-addr", OPT_MULTI, opt_add_alt_addr, NULL,
ld,
"Set an alternative IP address (v4 or v6) to use selectively for private reconnections with established peers.");
"Set an alternative IP address (v4 or v6) to use by default for private reconnections with established peers.");
clnopt_witharg("--alt-bind-addr", OPT_MULTI, opt_add_alt_bind_addr, NULL,
ld,
"Bind an alternative IP address (v4 or v6) for listening, but do not announce or use automatically.");
clnopt_witharg("--alt-announce-addr", OPT_MULTI, opt_add_alt_announce_addr, NULL,
ld,
"Provide a reserved IP address (bound by --alt-bind-addr) to established channel peers.");

opt_register_noarg("--offline", opt_set_offline, ld,
"Start in offline-mode (do not automatically reconnect and do not accept incoming connections)");
clnopt_witharg("--autolisten", OPT_SHOWBOOL,
Expand Down Expand Up @@ -2209,6 +2237,8 @@ bool is_known_opt_cb_arg(char *(*cb_arg)(const char *, void *))
|| cb_arg == (void *)opt_add_bind_addr
|| cb_arg == (void *)opt_add_announce_addr
|| cb_arg == (void *)opt_add_alt_addr
|| cb_arg == (void *)opt_add_alt_bind_addr
|| cb_arg == (void *)opt_add_alt_announce_addr
|| cb_arg == (void *)opt_subdaemon
|| cb_arg == (void *)opt_set_db_upgrade
|| cb_arg == (void *)arg_log_to_file
Expand Down
81 changes: 81 additions & 0 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,87 @@ def test_connect_with_alt_addr(node_factory, bitcoind):
raise


def test_connect_with_alt_addr_rpc(node_factory, bitcoind):
logging.basicConfig(level=logging.INFO)

# Set up nodes
logging.info("Setting up two nodes with the capability to reconnect")
l1 = node_factory.get_node(may_reconnect=True)
l2 = node_factory.get_node(may_reconnect=True)

# Initial connection
logging.info(f"Initial connection from l1 to l2 using localhost and port {l2.port}")
l1.rpc.connect(l2.info['id'], 'localhost', l2.port)

# Checking initial connection state
logging.info("Waiting for both nodes to report they are connected...")
wait_for(lambda: only_one(l1.rpc.listpeers(l2.info['id'])['peers'])['connected'])
wait_for(lambda: only_one(l2.rpc.listpeers(l1.info['id'])['peers'])['connected'])

# Fund channel and log the event
logging.info(f"Funding channel between l1 and l2 with 10**6 satoshis")
l1.fundchannel(l2, 10**6)

# Send the alt-addr from l2 to l1
alt_addr = '127.21.21.21'
addr_without_bind = '127.22.22.22'
l2.rpc.alt_addr(l1.info['id'], f'{alt_addr}:{l2.port}')

# Modifying node configuration to use an alternative address
logging.info(f"Stopping l2 to change its address to {alt_addr}:{l2.port}")
l2.stop()
l2.daemon.opts['alt-bind-addr'] = f'{alt_addr}:{l2.port}'
l2.start()
logging.info("Restarted l2 with bind-addr")

# Verification of the alternative address setting
logging.info("Verifying bind address setting on l2")
try:
binding = l2.rpc.getinfo()['binding']
assert len(binding) > 0, "No binding found for l2"
assert any(bind['address'] == alt_addr for bind in binding), f"Expected bind-addr {alt_addr}, found {binding}"

except Exception as e:
logging.error(f"Bind address not set correctly: {e}")
raise

# Reconnection using the alternative address
logging.info("Attempting to reconnect using the new alternative address")
try:
if any(peer['connected'] for peer in l1.rpc.listpeers()['peers']):
l1.rpc.disconnect(l2.info['id'], force=True)
l1.rpc.connect(l2.info['id'], alt_addr, l2.port)
except Exception as e:
logging.error(f"Error reconnecting nodes using alternative address: {e}")
raise

# Verify the connection using the new address
logging.info("Verifying new connection details")
try:
connected_peer = l1.rpc.getpeer(l2.info['id'])
assert connected_peer['connected'], "Peers not connected"
assert connected_peer['netaddr'][0].startswith(alt_addr), f"Connection not using alt-addr: {connected_peer['netaddr'][0]}"
except Exception as e:
logging.error(f"Error verifying connection using alt-addr: {e}")
raise

# Disconnect and attempt to connect using the addr_without_bind
l2.rpc.alt_addr(l1.info['id'], f'{addr_without_bind}:{l2.port}')
l2.stop()
l2.start()
try:
l1.rpc.connect(l2.info['id'], addr_without_bind, l2.port)
logging.error("Connection should not be successful using addr_without_bind")
assert False, "Connection should fail using addr_without_bind"
except Exception as e:
logging.info(f"Expected failure connecting using addr_without_bind: {e}")

# Final verification
logging.info("Verifying no connection using addr_without_bind")
connected_peer = l1.rpc.listpeers(l2.info['id'])['peers']
assert len(connected_peer) == 0 or not connected_peer[0]['connected'], "Peer should not be connected using addr_without_bind"


def test_remote_addr(node_factory, bitcoind):
"""Check address discovery (BOLT1 #917) init remote_addr works as designed:
Expand Down
3 changes: 2 additions & 1 deletion wallet/db.c
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,8 @@ static struct migration dbmigrations[] = {
{SQL("ALTER TABLE channels ADD remote_htlc_minimum_msat BIGINT DEFAULT NULL;"), NULL},
{SQL("ALTER TABLE channels ADD last_stable_connection BIGINT DEFAULT 0;"), NULL},
{NULL, migrate_initialize_alias_local},
{SQL("ALTER TABLE peers ADD COLUMN alt_addr TEXT;"), NULL},
{SQL("ALTER TABLE peers ADD COLUMN peer_alt_addr TEXT DEFAULT '';"), NULL},
{SQL("ALTER TABLE peers ADD COLUMN our_alt_addr TEXT DEFAULT '';"), NULL},
};

/**
Expand Down
Loading

0 comments on commit d212cd9

Please sign in to comment.