-
Notifications
You must be signed in to change notification settings - Fork 2.1k
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
multi+server.go: add initial permissions for some peers #9458
base: master
Are you sure you want to change the base?
Conversation
Important Review skippedAuto reviews are limited to specific labels. 🏷️ Labels to auto review (1)
Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
First shallow pass done, great work! Concept ACK! 🎉 Going to spend some more time with the details. I think in the mean time feel free to reply to my comments with your thoughts/ideas.
channeldb/db.go
Outdated
// - The second map denotes the set of nodes that have temporary access to our | ||
// server. The value denotes the number of pending-open channels that they have | ||
// with us. If this value gets to 0, their access status will change. | ||
func (c *ChannelStateDB) FetchPermAndTempPeers(chainHash []byte) ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suggest refining the return value of this function to make it more general and readable. Rather than categorizing peers as permanent or temporary, we could simply return a single map (map[string]ChanCount
) containing the open, pending, and closed channel counts for each peer, where ChanCount
is:
struct ChanCount {
open int
pending int
closed int
}
If you agree, could you please also add a basic unit test for this function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree that the maps can be merged but I don't think we should track either open or closed counts, there's no point in the current punishment mechanism
// is allowed according to our banning heuristic. This is here because | ||
// we do not learn the remote node's public static key until we've | ||
// received and validated Act 3. | ||
remoteKey := brontideConn.RemotePub() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively, shouldAccept
could receive the pubkey and return false if it's nil
so we don't need two separate rejection blocks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Think this is fine as-is, would rather the server not have to take public key pointers
server.go
Outdated
@@ -361,6 +411,26 @@ type server struct { | |||
// of new blocks. | |||
blockbeatDispatcher *chainio.BlockbeatDispatcher | |||
|
|||
// banScoreMtx is used for the server's ban tracking. This should only | |||
// be locked after the server mutex has been locked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why use an alternative mutex if we already that expect the server mutex is locked?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll change the comment, it's meant to convey that if the server mutex is going to be locked, this mutex cannot be locked before the server mutex
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, now clearer.
// NewPendingOpenChan is called after the pending-open channel has been | ||
// committed to the database. This may transition a restricted-access peer to a | ||
// temporary-access peer. | ||
func (s *server) NewPendingOpenChan(remoteHex string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally the ban logic could be separated into its own type altogether and so we could add extensive test coverage through unit tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you elaborate? Do you mean like a new struct/pkg?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible would be nice to decouple the peer banning logic from the server
to its own type (struct) so we can cover it with tests separately.
|
||
// The banScoreMtx cannot be held when the | ||
// below DisconnectPeer call occurs. | ||
return s.DisconnectPeer(remotePub) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iiuc we could end up demoting more than one peer at the same time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How so?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC when we decrement s.numRestricted
when we disconnect and then remove the peer. But the banScoreMtx
is lifted in between so it could be that if we reach the ceiling then concurrent calls to this function would still see the same counts. Doesn't seem to be an issue to me, more just a remark.
newStatus := peerSlotStatus{ | ||
state: protected, | ||
} | ||
s.peerScores[remoteHex] = newStatus |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we really need the peerScores
map either, we can just have a function returning the status based on channel state counts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
prefer to keep as-is for now in case we actually start scoring
s.banScoreMtx.Unlock() | ||
|
||
// This should not be possible. | ||
return fmt.Errorf("invalid peer access status") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: should we add the status to the error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's unreachable code, only added for completeness
77c8555
to
cbd7d60
Compare
cbd7d60
to
15e16ae
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good! Could you please add an itest covering the added functionality?
server.go
Outdated
@@ -361,6 +411,26 @@ type server struct { | |||
// of new blocks. | |||
blockbeatDispatcher *chainio.BlockbeatDispatcher | |||
|
|||
// banScoreMtx is used for the server's ban tracking. This should only | |||
// be locked after the server mutex has been locked. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, now clearer.
channeldb/db.go
Outdated
@@ -730,6 +731,194 @@ func (c *ChannelStateDB) FetchChannelByID(tx kvdb.RTx, id lnwire.ChannelID) ( | |||
return c.channelScanner(tx, selector) | |||
} | |||
|
|||
// ChanCount is used by the server in determining access control. | |||
type ChanCount struct { | |||
OpenClosed bool |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: consider renaming to HasOpenOrClosedChan
for better readability. Also please add godocs to these exported fields.
|
||
internalErr := chainBucket.ForEach(func(chanPoint, | ||
val []byte) error { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume we also want to skip non-buckets, so:
// If there's a value, it's not a bucket so ignore it.
if v != nil {
return nil
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add it. only buckets are stored here though and anybody who is messing with this database structure needs to know what they're doing already and should not be adding key-values here as it would mess up other things
channeldb/db.go
Outdated
// ChanCount is used by the server in determining access control. | ||
type ChanCount struct { | ||
OpenClosed bool | ||
Pending uint64 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Similarly maybe best to rename to PendingOpenCount
for clarity.
channeldb/db.go
Outdated
|
||
nodeStr := hex.EncodeToString(nodePub) | ||
|
||
count, exists := peerCounts[nodeStr] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Given that we iterate over the openChannelBucket
which is keyed by the peer pubkey, the peerCounts
map should not have any count for nodeStr
. Also the two cases of the below if
are exactly the same.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah that's right. I wrote the if-else
the other way without cases and thought it was confusing for the reader. I'll change this to just insert
channeldb/db_test.go
Outdated
count2, found := peerCounts[pubKeyHex2] | ||
require.True(t, found, "unable to find peer 2 in peerCounts") | ||
require.False(t, count2.OpenClosed, "found erroneous channels") | ||
require.Equal(t, count2.Pending, uint64(1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: expected comes first.
} | ||
|
||
if shouldDisconnect { | ||
return access, ErrGossiperBan |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think it'd be better to explicitly return peerStatusRestricted
here and above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
imo doesn't matter, we're returning an error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on second thought, we're dealing with access control so I'll explicitly return
// NewPendingOpenChan is called after the pending-open channel has been | ||
// committed to the database. This may transition a restricted-access peer to a | ||
// temporary-access peer. | ||
func (s *server) NewPendingOpenChan(remoteHex string) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible would be nice to decouple the peer banning logic from the server
to its own type (struct) so we can cover it with tests separately.
|
||
// The banScoreMtx cannot be held when the | ||
// below DisconnectPeer call occurs. | ||
return s.DisconnectPeer(remotePub) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC when we decrement s.numRestricted
when we disconnect and then remove the peer. But the banScoreMtx
is lifted in between so it could be that if we reach the ceiling then concurrent calls to this function would still see the same counts. Doesn't seem to be an issue to me, more just a remark.
I tested the code in a different way and found that the demotion logic for |
bd212b6
to
72ec6df
Compare
We introduce a new func FetchPermAndTempPeers that returns two maps. The first map indicates the nodes that will have "protected" access to the server. The second map indicates the nodes that have "temporary" access to the server. This will be used in a future commit in the server.go code.
This signal will be used in the server.go code to potentially demote temporary-access peers to restricted-access peers.
Here we introduce the server caches that will determine the access control status of our peers. Peers that have had their funding transaction confirm with us are protected. Peers that only have pending-open channels with us are temporary access and can have their access revoked. The rest of the peers are granted restricted access. The channelPeers map contains protected-access peers and the pendingChannelPeers map contains temporary-access peers.
This modifies the various channelnotifier notification functions to instead hit the server and then call the notification routine. This allows us to accurately modify the server's maps.
72ec6df
to
5a7b753
Compare
This patch adds initial access permissions in the server for some peers:
protected
access.temporary
status.restricted
status.In the future, we can tune this criteria. Some of the
discovery
ban code has also been moved toserver.go
so that it is somewhat unified.