-
Notifications
You must be signed in to change notification settings - Fork 492
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
Register keys with ParticipationRegistry. #2808
Register keys with ParticipationRegistry. #2808
Conversation
Codecov Report
@@ Coverage Diff @@
## feature/partkey #2808 +/- ##
===================================================
+ Coverage 47.44% 47.48% +0.04%
===================================================
Files 352 352
Lines 56857 56868 +11
===================================================
+ Hits 26975 27005 +30
+ Misses 26846 26832 -14
+ Partials 3036 3031 -5
Continue to review full report at Codecov.
|
node/node.go
Outdated
// Look for keyreg events and notify the participationRegistry. | ||
for _, tx := range block.Payset { | ||
if tx.Txn.Type == protocol.KeyRegistrationTx { | ||
id := account.MakeParticipationID(tx.Txn.Sender, tx.Txn.KeyregTxnFields) | ||
err := node.participationRegistry.Register(id, block.BlockHeader.Round) | ||
// If the key is not installed on this node, ErrParticipationIDNotFound is quickly returned. | ||
if err != nil && err != account.ErrParticipationIDNotFound { | ||
node.log.Error("Problem with participationRegistry.Register: %w", err) | ||
} | ||
} | ||
} | ||
|
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'm not sure that this is the best way to do that. Is it possible to conceptually defer this to the moment where we actually need the information and check on the ledger ?
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.
So when GetKeys is called it fetches all keys here and checks if any should be registered?
I think that could work and be less error-prone but require more account lookups. I'll investigate.
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 think that GetKeys would be called only on user-driven cases, whereas this would be called on each and every round.. so we should try and make it so that we push the "extra work" to be per-user case.
data/account/participation.go
Outdated
FirstValid basics.Round `codec:"fv"` | ||
LastValid basics.Round `codec:"lv"` | ||
KeyDilution uint64 `codec:"kd"` | ||
transactions.KeyregTxnFields |
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 think that the previous implementation was fine - the identity of the participation key is not really related to the key registration transaction fields. ( it happened to have a similar set of fields, but it's a different "creature" ).
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.
My thinking was that Idan's project is likely to change both of these, and maybe this would allow the change to happen in one place.
Interestingly, the existing ID doesn't include key dilution. I'm not sure if that's a bug, but including it here definitely gives more entropy to the ID.
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.
This ParticipationID hash need to capture the "identity" of the file.. but that's about it. I don't think we need to worry too much about Idan's upcoming changes, as there would probably mean adding one or two more fields to the transactions.KeyregTxnFields
which we can add to the ParticipationID, but don't have to.
|
||
// GetAll of the participation records. | ||
GetAll() ([]ParticipationRecord, 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.
Most of the changes in this file are related to simplifying the interface call. There isn't a DB error to pass along anymore, so no need for the error value. The caller can check for a zero value / empty list if they need to know that nothing was found.
node/node.go
Outdated
// This is usually a no-op, but the first time it will update the DB. | ||
err := node.participationRegistry.Register(part.ParticipationID(), keysRound) | ||
if err != nil { | ||
node.log.Error("Failed to register participation key (%s) with participation registry.", part.ParticipationID()) |
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.
Error
-> Warnf
.
Errors are meant for critical functional issues with the node. The node would continue to work just fine even if this call fails.
@@ -1161,6 +1161,12 @@ func (node *AlgorandFullNode) VotingKeys(votingRound, keysRound basics.Round) [] | |||
} | |||
participations = append(participations, part) | |||
matchingAccountsKeys[part.Address()] = true | |||
|
|||
// This is usually a no-op, but the first time it will update the DB. | |||
err := node.participationRegistry.Register(part.ParticipationID(), keysRound) |
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.
please benchmark the Register
and make sure it won't take too long. ( if it takes less than 50ms, then it's probably just fine. Otherwise, we need to consider moving it to a go-routine )
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 very fast to me. When you say ms are you think microseconds or milliseconds? Either way we're still under 50 if there's only 1 account registered:
BenchmarkKeyRegistration1/KeyInsert_1
BenchmarkKeyRegistration1/KeyInsert_1-8 202903 6154 ns/op
BenchmarkKeyRegistration1/KeyRegistered_1
BenchmarkKeyRegistration1/KeyRegistered_1-8 26833 44209 ns/op
BenchmarkKeyRegistration1/NoOp_1
BenchmarkKeyRegistration1/NoOp_1-8 194818 5980 ns/op
BenchmarkKeyRegistration5
BenchmarkKeyRegistration5/KeyInsert_5
BenchmarkKeyRegistration5/KeyInsert_5-8 41196 30087 ns/op
BenchmarkKeyRegistration5/KeyRegistered_5
BenchmarkKeyRegistration5/KeyRegistered_5-8 5782 221874 ns/op
BenchmarkKeyRegistration5/NoOp_5
BenchmarkKeyRegistration5/NoOp_5-8 39292 29898 ns/op
BenchmarkKeyRegistration10
BenchmarkKeyRegistration10/KeyInsert_10
BenchmarkKeyRegistration10/KeyInsert_10-8 19551 59042 ns/op
BenchmarkKeyRegistration10/KeyRegistered_10
BenchmarkKeyRegistration10/KeyRegistered_10-8 2823 448233 ns/op
BenchmarkKeyRegistration10/NoOp_10
BenchmarkKeyRegistration10/NoOp_10-8 19406 60828 ns/op
BenchmarkKeyRegistration50
BenchmarkKeyRegistration50/KeyInsert_50
BenchmarkKeyRegistration50/KeyInsert_50-8 3784 394140 ns/op
BenchmarkKeyRegistration50/KeyRegistered_50
BenchmarkKeyRegistration50/KeyRegistered_50-8 394 3207132 ns/op
BenchmarkKeyRegistration50/NoOp_50
BenchmarkKeyRegistration50/NoOp_50-8 2625 427918 ns/op
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.
milliseconds. and these looks good. ( I just wanted to make sure )
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.
Do you think it's worth making this asynchronous? This design means that we'll frequently re-check for keys to register so it no longer needs to update the DB in real-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.
The VotingKeys
is called by the pseudonode once every round so that it could use the keys for voting on that round. By slowing this down, we're holding off the agreement from making the proposal message ( which is a time critical). This could be really bad if it takes too long; however, if we're taking about 50Kns, than it's probably wouldn't make a difference. Just a side note that if we take any lock in there - that lock might take considerably more than 50Kns, so we need to be careful around that.
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.
There is a lock taken for most of the record function. There is probably a clever solution with multiple mutexes that would avoid a DB access lock in the agreement path, I'll need to think about that more.
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.
Created #2878 to track further optimizations to limit mutexes to very short read/copy events, and use a channel to optimize Record/Register.
Summary
Call participationRegistry Register command in
OnNewBlock
.Required adjusting how the ParticipationID is computed, it's now a hash of the keyreg fields + account address.
Test Plan
Existing unit tests.