Repository navigation
feature, routing: treat 266 in a node announcement as unknown - #13
Conversation
The even bit of option_independent_secrets is only valid in channel_type. lnd knows the bit, so a node announcing it passed the check for unknown required bits and was paid and routed through, while a node that doesn't know the bit refuses it. lightning-blake2b/bolts#17 now has a receiver of 266 in node_announcement treat it as an unknown even bit. ValidateRoutable adds that to ValidateRequired, and pathfinding uses it for the destination and for every intermediate node. init keeps its own, clearer refusal.
|
Thanks, this does the routing half of the new bolts#17 rule: the destination and every intermediate node go through Two notes for later, neither specific to this PR:
|
|
Thanks Paul, agreed on both notes. A security review of the lnd side (#7 and this PR) found the blinded paths one too, and nothing that could lose funds. Two more for later:
|
|
Two small follow-ups from another review of the lnd side (it found nothing that could lose funds, and confirmed the dev-tag build fix you already made in 204d9ad):
|
|
One more from a review of the lnd side, older than this work and probably upstream too, so you may want to check it there. A zero-conf channel's funding confirmation can refresh the live channel state in the middle of ReceiveRevocation. When the funding confirms, waitForZeroConfChannel (funding/manager.go:4127) reports the short channel ID, and channelLink.UpdateShortChanID (htlcswitch/link.go:2541) calls Refresh() on the link's live OpenChannel from the funding manager's goroutine. ReceiveRevocation (lnwallet/channel.go:6240-6250) changes the revocation store and the remote revocation points in memory without the OpenChannel mutex, and only writes them in AdvanceCommitChainTail. A Refresh() in between reloads the old values from disk, and AdvanceCommitChainTail then writes them back while advancing the remote commitment height. With the timing forced in a test (a hook calling Refresh() right after the in-memory update), on a shachain channel ReceiveRevocation succeeds but the database is left with the remote height advanced and the store missing the secret it just received: NewBreachRetribution for that state and ChanSyncMsg both fail, so that revoked state can't be punished and the channel can't reestablish. On a channel with option_independent_secrets the newest-secret check in AdvanceCommitChainTail refuses the write, the transaction rolls back, and after a reload the retransmitted revoke_and_ack is accepted, so those channels recover. How often the real timing hits that window I don't know; it needs the confirmation to land while a revocation is being processed. A fix would be to refresh only the short channel ID fields rather than the whole state, or do the refresh on the link's goroutine, or hold the OpenChannel mutex across ReceiveRevocation's in-memory update and its write. Happy to send the test and hook if useful. The same Refresh() call is still in UpdateShortChanID on lnd master, and I couldn't find an issue for it there. The closest is lightningnetwork#11259, a different race between two copies of the same OpenChannel while a channel opens. |
What
A small follow-up to #7, from the spec review on lightning-blake2b/bolts#17: a node that sets bit 266 (the even bit of
option_independent_secrets) in itsnode_announcementis now treated as one requiring a feature lnd doesn't know. It isn't paid from its announcement, and it isn't routed through.Why
The even bit is only valid in
channel_type. lnd knows it, so a node announcing it passedValidateRequiredand was paid and routed through, while a node that doesn't know the bit refuses it. bolts#17 now says a receiver of 266 innode_announcementtreats it as an unknown even bit, as BOLT 7 describes.How
feature.ValidateRoutableisValidateRequiredplus a refusal of 266.ValidateRequiredwas used before.initkeeps its own, clearer refusal of 266 from multi: add option_independent_secrets channel type #7.Tests
TestPathFindingcase, "independent secrets even bit in node announcement", which runs the existing unknown-required-features test with a node announcing 266. It fails without the change. AndTestValidateRoutableRefusesIndependentSecretsEvenBit: 266 refused, 267 accepted, unknown required bits still refused.go build ./...passes, and so do thefeature,routingandpeertests andgo vetonfeatureandrouting.