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

DB: Store the signatures of channel announcement sent from remote peer into DB #2619

Merged
merged 4 commits into from
May 29, 2019

Conversation

trueptolemy
Copy link
Collaborator

@trueptolemy trueptolemy commented May 5, 2019

Fixes #2409
Here I add a new wire type:
channel_got_announcement (1017)

When Channeld receives the remote channel announcement signatures, it will generate a msg1 like:
WIRE_CHANNEL_GOT_ANNOUNCEMENT + remote_ann_node_sig + remote_ann_bitcoin_sig
and send this msg to Master. (After sending signatures, Channeld will be holding until receive the reply from Master.)
Master resolves msg and stores the signatures into DB.

Like issue #2409 , the adventage is when we reenable our channel, you can init it with the remote announcement signatures from DB directly other than wasting your time to wait for remote peer's announce reply.

@trueptolemy trueptolemy requested a review from cdecker as a code owner May 5, 2019 12:55
type_to_string(peer, struct short_channel_id,
remote_scid));

/* make sure new node signature and bitcoin signature equal
Copy link
Collaborator

Choose a reason for hiding this comment

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

Shouldn't we just check that the signature is valid? Signatures change when re-signing (using different k value).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you! You're right, and there shouldn't compare with the old sigs if the remote generate new sigs. Gossipd will verify the announcement, so I think we'd better update the new sigs here.

@SimonVrouwe
Copy link
Collaborator

I think it would be nice to have some sort of (py)test to test the functionality. Also to get more clarity on what kind of behavior we actually want/expect.

Related questions:

@trueptolemy
Copy link
Collaborator Author

trueptolemy commented May 6, 2019

Also to get more clarity on what kind of behavior we actually want/expect.

@SimonVrouwe I think the point is we want to get into the phase of announcement from the phase of waiting for remote signatures more quickly.
We care how to get the valid signature faster, and we have 2 choices: waiting for remote reply or using the old signatures(rejection for the new sigs is a bad idea:( ). In the origin code, we just check the validity of signatures and don't consider if signatures are different with the ones received before.
In this PR, local peer still send local sigs to the remote and receives the remote reply. It commucates with others like original.
But the difference is : it use the old signatures directly to announce although it will also receive the remote reply with remote signatures(the old or the new). In my understanding, the announcement is ok iff the signatures are all valid.

@trueptolemy
Copy link
Collaborator Author

trueptolemy commented May 6, 2019

@SimonVrouwe Sorry. I find another change for the current PR: when we reenable the channel, we directly announce with the old sigs meanwhile sending remote peer our sigs. So we will announce once again when we receive the announcement reply.
Sending our own sigs to the remote peer is necessary. This leads directly to the issue that how to handle the reply from remote peer. Now I handle this special reply as usual, and also ask Channeld to announce.
Another alternative way is to ignore this reply.
I think the formor way(which was achived in this PR) seems more reasonable, but now we meet two announcement almost at the same time.
lightning-integration is needed though it doesn't matter for clightning.
Thank you very mush :)

@trueptolemy
Copy link
Collaborator Author

Sending our own sigs to the remote peer is necessary. This leads directly to the issue that how to handle the reply from remote peer. Now I handle this special reply as usual, and also ask Channeld to announce.
Another alternative way is to ignore this reply.

@cdecker hi, what do you think of these two ways?

Copy link
Member

@cdecker cdecker left a comment

Choose a reason for hiding this comment

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

Quite a nice pull request, congratulation 👍 I went through and I found a
few pieces that are a bit strange though (some are a bit pedantic I'll admit
😉). In particular there are a few places where you are over-checking
(signatures may change, scids may also change), and the last commit is a bit
too much sync that was likely caused by trying to only send the signatures
once. Please remove the reply in the last commit, and relax some of the checks.

Minor comments:

  • WIRE_CHANNEL_GOT_ANNOUNCEMENT is introduced in the first commit, but never
    handled. This breaks rebaseability (each commit must compile on its own).
  • The same applies to commit 71dcf4b which
    changes the signature of new_channel but doesn't fix the callers.

channeld/channeld.c Outdated Show resolved Hide resolved
channeld/channeld.c Outdated Show resolved Hide resolved
channeld/channeld.c Outdated Show resolved Hide resolved
const u8 *remote_upfront_shutdown_script)
const u8 *remote_upfront_shutdown_script,
secp256k1_ecdsa_signature *remote_ann_node_sig,
secp256k1_ecdsa_signature *remote_ann_bitcoin_sig)
Copy link
Member

Choose a reason for hiding this comment

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

@rustyrussell should these be marked as TAKES if we tal_steal them below? I can never remember the semantics of TAKES 😉

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I find we marked as TAKES if we tal_dup, and there's some similar cases in this function:
we didn't marked scid, last_tx, last_sent_commit as TAKES and we tal_steal them below,
but we marked transient_billboard as TAKES because we tal_strdup it below.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Rusty replied me that tal_steal doesn't need to be marked as TAKES, and tal_dup can be marked :)

wallet/db.c Outdated
{ "ALTER TABLE forwarded_payments ADD resolved_time INTEGER", NULL },
{ "ALTER TABLE channel_htlcs ADD received_time INTEGER;", NULL },
{ "ALTER TABLE forwarded_payments ADD received_time INTEGER;", NULL },
{ "ALTER TABLE forwarded_payments ADD resolved_time INTEGER;", NULL },
Copy link
Member

Choose a reason for hiding this comment

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

Please don't modify existing migrations. While this change may be harmless cosmetics, changing the migrations may end up corrupting deployed databases.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Sorry, I'll take care, too

channeld/channeld.c Outdated Show resolved Hide resolved
secp256k1_ecdsa_signature *remote_ann_bitcoin_sig;

remote_ann_node_sig = tal(tmpctx, secp256k1_ecdsa_signature);
remote_ann_bitcoin_sig = tal(tmpctx, secp256k1_ecdsa_signature);
Copy link
Member

Choose a reason for hiding this comment

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

You can allocate them directly off of channel if you tal_free them on error as well (which should be rare anyway).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, now I don't allocate them directly.


/* channel announcement sigs info from remote peer*/
secp256k1_ecdsa_signature *remote_ann_node_sig;
secp256k1_ecdsa_signature *remote_ann_bitcoin_sig;
Copy link
Member

Choose a reason for hiding this comment

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

Why are we keeping these in memory at all? We could just as well load them on demand when a new channeld gets started.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Now I replace it by a new load function, which is only called when we generate a channel_init message.

channeld/channel_wire.csv Outdated Show resolved Hide resolved
* In this case, master may receive announcement more than once!
*/
tal_free(channel->remote_ann_node_sig);
tal_free(channel->remote_ann_bitcoin_sig);
Copy link
Member

Choose a reason for hiding this comment

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

If the master was shut down we're dead as well. No need to handle that case here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I'll remove it.

@trueptolemy
Copy link
Collaborator Author

@cdecker Sincerely thank you! I'll correct these days. Many points, detials to learn and I hope I can do better next time :)

@trueptolemy trueptolemy force-pushed the issue-2409-new branch 17 times, most recently from 983414d to 43e47fb Compare May 19, 2019 11:08
@trueptolemy
Copy link
Collaborator Author

Hi, @cdecker I've correct the code. What do you think about it now? :-)

Copy link
Member

@cdecker cdecker left a comment

Choose a reason for hiding this comment

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

Excellent pull request @trueptolemy, I went through with a fine-toothed comb and commented all the minor things that came to mind.

@@ -595,6 +595,42 @@ wallet_htlc_sigs_load(const tal_t *ctx, struct wallet *w, u64 channelid)
return htlc_sigs;
}

bool wallet_remote_ann_sigs_load(struct wallet *w, u64 id,
secp256k1_ecdsa_signature **remote_ann_node_sig,
secp256k1_ecdsa_signature **remote_ann_bitcoin_sig)
Copy link
Member

Choose a reason for hiding this comment

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

If we are passing a pointer-to-pointer-of-sugnature we should also be in charge of allocating the signature, otherwise we need to allocate outside of the call, keep an alias, call the function and then free the alias in case of a failure. If however we allocate internally then the pointer will get populated correctly.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you! Now I manage the allocation in this func!

wallet/wallet.c Outdated

if (sqlite3_column_type(stmt, 0) != SQLITE_NULL) {
if (!sqlite3_column_signature(stmt, 0, *remote_ann_node_sig)) {
db_stmt_done(stmt);
Copy link
Member

Choose a reason for hiding this comment

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

In other parts of the code we use a fail: label to clear and return if we have multiple similar failure paths in a single function.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, now I use fail:

wallet/wallet.c Outdated Show resolved Hide resolved
wallet/wallet.c Outdated
@@ -1086,6 +1141,16 @@ void wallet_channel_save(struct wallet *w, struct channel *chan)
sqlite3_bind_null(stmt, 1);
sqlite3_bind_int64(stmt, 2, chan->dbid);
db_exec_prepared(w->db, stmt);

/* save announcement sigs only when we ge announcement*/
Copy link
Member

Choose a reason for hiding this comment

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

ge -> get

Copy link
Member

Choose a reason for hiding this comment

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

Also these fields ought to be initialized to null anyway in the DB, so no need to do this additional initialization.

* @w: wallet containing the channel
* @id: channel database id
* @remote_ann_node_sig: location to load remote_ann_node_sig to
* @remote_ann_bitcoin_sig: location to load remote_ann_bitcoin_sig to
Copy link
Member

Choose a reason for hiding this comment

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

Like mentioned above, with this signature I would have expected the function to allocate the signatures on the heap. But in that case we're missing a ctx to allocate from.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you! Now I add it.

if(!(channel->channel_flags & CHANNEL_FLAGS_ANNOUNCE_CHANNEL)) {
channel_internal_error(channel,
"No ANNOUNCE flag, no announcemnt signatures! %s",
tal_hex(tmpctx, msg));
Copy link
Member

Choose a reason for hiding this comment

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

This check, while correct, may be a bit aggressive in the current network and might result in a lot of failures (nodes usually just spray you with the signatures without checking the announcement flags). We can still enforce that we don't announce by simply not sending sigs ourselves.

Please don't fail the channel over such a minor offence, but log it and we can tighten this later.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you! Now I remove this check

peer->have_sigs[REMOTE] = true;

tal_steal(tmpctx, remote_ann_node_sig);
tal_steal(tmpctx, remote_ann_bitcoin_sig);
Copy link
Member

Choose a reason for hiding this comment

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

Why are you stealing these onto tmpctx? You could also pass the pointers to peer->announcement_node_sigs[REMOTE] and peer->announcement_bitcoin_sigs[REMOTE] into the fromwire_channel_init call directly, which avoids having these temp variables and having to deal with intermediate allocations and ownership.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, I find these tal_steal are extra. But I think fromwire_channel_init needs these 2 pointer, because they can handle NULL sigs:

channel_init,,remote_ann_node_sig,?secp256k1_ecdsa_signature channel_init,,remote_ann_bitcoin_sig,?secp256k1_ecdsa_signature

Copy link
Collaborator Author

@trueptolemy trueptolemy May 28, 2019

Choose a reason for hiding this comment

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

emm, after deleting tal_steal ,Travis CI tell me memory leak. So I tal_free these directly.(I find other variables used in fromwire_channel_init are freed directly, so I also do it.)

secp256k1_ecdsa_signature *remote_ann_node_sig, *remote_ann_bitcoin_sig;

remote_ann_node_sig = tal(tmpctx, secp256k1_ecdsa_signature);
remote_ann_bitcoin_sig = tal(tmpctx, secp256k1_ecdsa_signature);
Copy link
Member

Choose a reason for hiding this comment

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

This is the weird ownership I was talking about before. Please have wallet_remote_ann_sigs_load manage allocations. The only reason we aren't producing a memory leak here is that tmpctx and all child allocations is cleared in the io_loop.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you so much! Now I pass ctx to wallet_remote_ann_sigs_load and let it manage allocations.

@trueptolemy trueptolemy force-pushed the issue-2409-new branch 6 times, most recently from ab2371d to 4bf51a3 Compare May 28, 2019 14:42

fail:
*remote_ann_node_sig = tal_free(*remote_ann_node_sig);
*remote_ann_bitcoin_sig = tal_free(*remote_ann_bitcoin_sig);
Copy link
Member

Choose a reason for hiding this comment

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

This can easily lead to a double-free or freeing a random memory location since we do not allocate in all cases. It'd be better to check both fields for SQLITE_NULL at the top, and set the out-args to NULL and return immediately if either is SQLITE_NULL. If both are not SQLITE_NULL initialized both using tal and then goto fail; if either of the sqlite3_column_signature calls fails. That way we either initialize all or none, and free only when both are actually initialized.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thank you! What you said is better, kinds of cases make sense easily.

trueptolemy added 4 commits May 29, 2019 01:04
1. Add the fields of remote channel announcement signatures into TABLE channels
2. Add the related function
Channeld sends announcement signatures to Master by this message.
When Channeld receive a new channel announcement msg, (After channel locking)it will sends announcement signatures to Master by this message.
1. Add remote_ann_node_sigs and remote_bitcoin_sigs fields in channel_init message;
2. Master add announcement signatures into channel_init message, and send this message to Channeld.
Channeld will initial the channel with this signatures when it reenables the channel.
@cdecker
Copy link
Member

cdecker commented May 29, 2019

ACK e884bc6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Store the announcement_signature in the database to work around some impls not sending it on reconnect
3 participants