-
-
Notifications
You must be signed in to change notification settings - Fork 291
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
Optimize UX loading for keystores #5043
Conversation
Performance Report✔️ no performance regression detected Full benchmark results
|
f70f720
to
8ed2b04
Compare
const password = passwords.join(""); | ||
// We can't use Keystore.parse as it validates the `encrypted message` to be only 32 bytes. | ||
const keystore = new Keystore(JSON.parse(fs.readFileSync(cacheFilepath, "utf8"))); | ||
const secretKeyConcatenatedBytes = await keystore.decrypt(password); |
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.
What's the point of encrypting the keystore at all if we store the passwords in the same place? I assume the decrypting still takes a long time if the keystore is big but probably still better then decryption every single key separately as it was done before
It does not seem ideal to me that we store passwords in plain text at all which is currently done by Lodestar as we have a keystore
and a secrets
(with passwords) file. Those files are stored on the same device (and even the same folder) so it pointless in terms of security.
In my opinion, we should never store the password, only the first time the user imports a encrypted keystore the password should be interactively provided through the cli or keymanager API. After keystores are imported they should be stored unecrypted which makes subsequently loading them much faster and we never store passwords in plain text.
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.
- Local keystores is a feature, which is consistent across all CL clients. As you mentioned it's not the best way in terms of security.
- To overcome security issue, there is a feature of remote key manager, which does not involve storing passing in local file.
It's upto user to use one feature or other. In regard to decryption, for cache decryption it will take time upto one keystore. So if we have 100 validators keys we can save upto 99% of time with the cache.
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.
That's what I figured, looks like all CLs implement it like this. We also quickly discussed this topic in the standup, I will create a separate issue for this to get some ideas how we could improve the current security model but it might just be the case that a remoter signer/key manager is the only solution.
So if we have 100 validators keys we can save upto 99% of time with the cache.
that's a great performance improvement, I was assuming it will be much faster but having constant time is really a big deal if you have a lot of validators
This reverts commit a5c78e5.
Co-authored-by: Nico Flaig <[email protected]>
66e45db
to
86706bb
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.
LGTM! Really good UX and performance improvement!
const password = passwords.join(""); | ||
// We can't use Keystore.parse as it validates the `encrypted message` to be only 32 bytes. | ||
const keystore = new Keystore(JSON.parse(fs.readFileSync(cacheFilepath, "utf8"))); | ||
const secretKeyConcatenatedBytes = await keystore.decrypt(password); |
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.
That's what I figured, looks like all CLs implement it like this. We also quickly discussed this topic in the standup, I will create a separate issue for this to get some ideas how we could improve the current security model but it might just be the case that a remoter signer/key manager is the only solution.
So if we have 100 validators keys we can save upto 99% of time with the cache.
that's a great performance improvement, I was assuming it will be much faster but having constant time is really a big deal if you have a lot of validators
packages/cli/src/util/progress.ts
Outdated
@@ -16,6 +16,7 @@ export function showProgress({ | |||
let current = 0; | |||
let last = 0; | |||
let lastProcessTime: number = Date.now(); | |||
let internalId: NodeJS.Timeout; |
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.
could use a more descriptive name for the interval, I think it will also not be just an ID but the whole Timeout
object
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 common convention among JS community to suffix output of setTimeout
or setInterval
as Ids
because these are just the pointers to v8 internal timers.
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 changed the name to be more descriptive.
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 common convention among JS community to suffix output of setTimeout or setInterval as Ids because these are just the pointers to v8 internal timers
gotcha, thanks for pointing that out, I didn't know about that
packages/cli/test/unit/cmds/validator/keymanager/keystoreCache.test.ts
Outdated
Show resolved
Hide resolved
describe("writeKeystoreCache", () => { | ||
it("should write a valid keystore cache file", async () => { | ||
await expect(writeKeystoreCache(keystoreCacheFile, signers, passwords)).to.fulfilled; | ||
expect(fs.existsSync(keystoreCacheFile)).to.be.true; |
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.
is there an easy way to do some basic sanitiy checks on the written file? I guess the proper check happens in the others test were the file is written and then loaded, so maybe should not bother too much here
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 authenticity of files could better be checked in e2e tests.
return decryptKeystoreDefinitions(keystoreDefinitions, { | ||
...args, | ||
onDecrypt: needle, | ||
cacheFilePath: `${args.importKeystores[0]}.cache`, |
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.
@nazarhussain I noticed that the validator_keys.cache
is stored in the same directory as my validator_keys
folder which I set via --importKeystores
. This is a not ideal in a containerized environment as the cache file is likely not mounted to the container or persistent in a volume. Maybe it would be better to store the cache file in the data directory (dataDir
).
In our docker-compose.validator.yml
file we also only mount the /keystores
which would not include the cache file.
lodestar/docker-compose.validator.yml
Lines 4 to 11 in 2042c3b
image: chainsafe/lodestar:next | |
restart: always | |
volumes: | |
- validator:/data | |
- logs:/logs | |
- ./keystores:/keystores | |
env_file: .env | |
command: validator --dataDir /data --importKeystores /keystores --importKeystoresPassword /keystores/password.txt --server http://beacon_node:9596 --logFile /logs/validator.log --logFileLevel debug --logFileDailyRotate 5 |
…importing of validator keys (ChainSafe#5043)
🎉 This PR is included in v1.6.0 🎉 |
…importing of validator keys (ChainSafe#5043)
Motivation
Improve the UX for the keystores loading.
Description
Add key caching to the keystore loading logic.
Closes #4179
Steps to test or reproduce