-
-
Notifications
You must be signed in to change notification settings - Fork 232
test(KeyringController): mock encryptor upgrade #5943
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
base: main
Are you sure you want to change the base?
Conversation
2c0dbd5
to
209c58d
Compare
@@ -2924,14 +2919,12 @@ describe('KeyringController', () => { | |||
it('should unlock the wallet also if encryption parameters are outdated and the vault upgrade fails', async () => { | |||
await withController( | |||
{ | |||
skipVaultCreation: 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.
Initial vault creation was being skipped in submitPassword
tests because the default withController
behavior is to generate a vault and leave the controller unlocked. So when these tests get executed KeyringController will already be unlocked.
For some of these test cases, it shouldn't be a problem. Though for some of them, there's the risk of getting false positives
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.
initially i've changed it because the withController
function didn't allow to setup with encryption key. i've changed that now, so will check if i can change it back in some cases. would be good to know in which cases it's problematic to have this skipped.
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 it's potentially problematic for any test case that aims to verify how unlocking works, since the controller is already unlocked before the tests.
e.g. if a test case asserts expect(controller.state.isUnlocked).toBe(true);
, that would potentially be a false positive
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've changed skipVaultCreation
back where i could.
tbh, i think it might be better to simply lock the controller in cases where you need a locked controller.
the issue i had with setting up the vault like this is that it depends on the implementation of the mock controller, so everytime this is changed, you need to change the fixed vault value that is used in 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.
tbh, I think it might be better to simply lock the controller in cases where you need a locked controller.
This may work in some cases, though you'd have to use another public function to test another one, which is ok, but better to avoid it if we can make the test simpler and more independent
the issue i had with setting up the vault like this is that it depends on the implementation of the mock controller
Perhaps you are referring to the encryptor? If so, that's the reason why we set up these mock responses:
jest.spyOn(encryptor, 'decryptWithKey').mockResolvedValueOnce([
{
type: KeyringTypes.hd,
data: '0x123',
}
);
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.
Sorry. Yes. Mock encryptor
// TODO actually this does trigger re-encryption. The catch is | ||
// that this test is run with cacheEncryptionKey enabled, so | ||
// `encryptWithKey` is being used instead of `encrypt`. Hence, | ||
// the spy on `encrypt` doesn't trigger. 🤦♂️ |
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 cached key is only used if:
cacheEncryptionKey
is set to true- There's an actual key cached
- There's a vault
- The vault is already using the latest cipher configuration
This means that if any of the above is false, encryptWithDetail
will be called instead of encryptWithKey
, which used to call encrypt
internally.
This isn't the case anymore, but only after changes in this PR.
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 not true. You can revert to main branch and debug step through the execution. You will see that it goes through the useCacheKey
branch and therefore calls encryptWithKey
.
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.
In that case, can we fix the test, or remove it altogether?
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.
Probably OK to fix in a later PR?
async ({ controller, initialState }) => { | ||
const initialVault = controller.state.vault; | ||
const initialKeyrings = controller.state.keyrings; | ||
await controller.createNewVaultAndRestore( | ||
password, | ||
uint8ArraySeed, | ||
); | ||
expect(controller.state).not.toBe(initialState); | ||
expect(controller.state.vault).toBeDefined(); | ||
expect(controller.state.vault).toStrictEqual(initialVault); | ||
expect(controller.state.keyrings).toHaveLength( |
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 this deletion still relevant after the mock encryptor refactor, or, do you think we can check in other ways if the new vault is the intended one?
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 you add that, this is what you get
so the mnemonic and id are different. (as expected because mnenomic is randomnly generated in the first case and fixed in the second. not sure about the id, but probably similar case.)
feel free to make a suggestion how this check can be added back again (or at least the intention behind it)
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.
Indeed, I guess we could make it match if we made this test import the same SRP that was in-place before. But I would interpret the intent to be importing a new SRP, so them not matching seems appropriate.
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 do agree that matching the new vault with the initial one doesn't make sense, though what I was proposing was to leverage the new mock encryptor capabilities to check if the seed in the new vault is the one imported
0025960
to
3f83da4
Compare
rebase |
3f83da4
to
72d3d75
Compare
skipVaultCreation: true, | ||
state: { | ||
vault: freshVault, | ||
// @ts-expect-error we want to force the controller to have an |
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: Hmm. I don't quite understand why we would need this here, as the encryptionSalt
state is not used for anything other than detecting a mismatch between encryptionKey
and vault
(which is not relevant for this test, certainly because encryptionKey
is not even present).
I see that it is a pre-existing widespread pattern in these tests though, so we can fix it later.
// Generate random salt. | ||
|
||
// return crypto.randomUUID(); | ||
return SALT; // TODO some tests rely on fixed salt, but wouldn't it be better to generate random value 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.
Indeed, a more realistic mock encryptor would certainly have advantages. I wonder if we could take it a step further even than making the mock more 'realistic', and instead use a real encryptor that uses some very fast/insecure encryption strategies.
@@ -3242,7 +3268,7 @@ describe('KeyringController', () => { | |||
await expect( | |||
controller.submitEncryptionKey( | |||
MOCK_ENCRYPTION_KEY, | |||
initialState.encryptionSalt as string, | |||
initialState.encryptionSalt as string, // TODO Why need the salt 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.
Nit: Answered in the other PR, but this is used because the client backs up the key and salt in session storage, and we use the salt to ensure the key matches the vault
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.
removed in 94ad779
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!
tbh, not sure why the coverage changed. i've checked manually and it looks like exactly the same branches as before are not covered. still, it shows up as one less covered branch (163 vs 164 before).
- add back skipVaultCreation where it was used before - revert change to branch coverage threshold, as this went back to before levels after reverting the above
72d3d75
to
12f8275
Compare
@matthiasgeihs are you ok with merging this one? |
Explanation
For testing envelope encryption (#5940), we need a more versatile mock encryptor. (I.e., one that can handle multiple ciphertexts at the same time.)
Hence, here we upgrade the mock encryptor as a preparation for #5490.
References
Changelog
Checklist