8000 Add client recovery methods by blakecduncan · Pull Request #486 · getwax/bls-wallet · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content
This repository was archived by the owner on Nov 5, 2023. It is now read-only.

Add client recovery methods #486

Merged
merged 16 commits into from
Feb 1, 2023
Merged

Add client recovery methods #486

merged 16 commits into from
Feb 1, 2023

Conversation

blakecduncan
Copy link
Contributor
@blakecduncan blakecduncan commented Jan 25, 2023

What is this PR doing?

Adding the logic used recover an instant wallet to quill to the bls client module. There's probably room to add some more recovery functionality. This is just the logic that is currently used to recover from an Instant wallet to Quill.

How can these changes be manually tested?

Added tests to the contracts tests to make sure it works. Also, you could try recovering an instant wallet.

Does this PR resolve or contribute to any issues?

#220
#442

Checklist

  • I have manually tested these changes
  • Post a link to the PR in the group chat

Guidelines

  • If your PR is not ready, mark it as a draft
  • The resolve conversation button is for reviewers, not authors
    • (But add a 'done' comment or similar)

@blakecduncan blakecduncan marked this pull request as draft January 25, 2023 14:51
@github-actions github-actions bot added the contracts Smart contract related label Jan 26, 2023
@github-actions github-actions bot added the extension Browser extension related label Jan 26, 2023
@github-actions github-actions bot added aggregator Aggregator backend related aggregator-proxy Aggregator proxy related labels Jan 26, 2023
import { mcl } from "@thehubbleproject/bls";

export default (): string => {
return `0x${mcl.randFr().serializeToHexStr()}`;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I originally put this here because we know that mcl has been initialised inside the signer directory. However, logically I'm thinking now that I should pull this out of the signer. Curious to hear your thoughts. Right now if you want to call this you have to create a wallet wrapper first like so. You probably shouldn't have to create a wallet to get a new PK.

const wallet = await this.BlsWalletWrapper(pk);
const newPk = wallet.getRandomBlsPrivateKey();

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree creating a new wallet to get a random private key is not optimal, unsure on the best place to put this. But could it be a static method somewhere? Similar to how we have static methods for an address on BlsWalletWrapper.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah the more that I think about it, I'm gonna move it.

@blakecduncan blakecduncan marked this pull request as ready for review January 27, 2023 09:19
Copy link
Collaborator
@JohnGuilding JohnGuilding left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

All comments are minor/non-blocking so approving

return this.blsWalletSigner.getRandomBlsPrivateKey();
}

async getBundleSetRecoveryHash(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To check my understanding, this method sets the recovery hash and then returns 10000 that bundle?

Was originally thrown off by the naming here but not convinced there's anything better.

  1. getSetRecoveryHashBundle/getRecoverWalletBundle reads more like a sentence but not a fan of "get" and "set" next to each other for the first method.
  2. createSetRecoveryHashBundle/createRecoverWalletBundle reads better than "get" prefix to me, but prefer the "get" prefix semantically
  3. signSetRecoveryHashOperation/signRecoverWalletOperation think this reads well but using bundle instead of operation makes more sense to me
  4. setRecoveryHash/recoverWallet is misleading as the method is only creating the bundle and not executing it

Happy with to go with original (or one of 4 options if you think any make more sense) as I've spent far too long thinking about this lol

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I spent some time thinking about this. I ended up going with what I had cause I figured getBundle{FUNCTION_NAME} could be a good naming pattern.

I think your first one might be the most readable though so I might switch to that.

try {
return ethers.utils.formatBytes32String(salt);
} catch (e) {
throw new Error("Error convirting salt string to bytes 32 string.");
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Making an assumption here, but is this error handling to ensure we have a more helpful error message when an invalid salt is passed in?

This is more of a question than a suggestion, but if so, would it be helpful to also surface the ethers error too as part of the error message?

Also nitpick but while I'm here, noticed a tiny typo in the error message :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is a good shout. After thinking about it, this private method is unnecessary and we can just go with the default error

import { mcl } from "@thehubbleproject/bls";

export default (): string => {
return `0x${mcl.randFr().serializeToHexStr()}`;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree creating a new wallet to get a random private key is not optimal, unsure on the best place to put this. But could it be a static method somewhere? Similar to how we have static methods for an address on BlsWalletWrapper.

import * as mcl from 'mcl-wasm';
import { hexlify, randomBytes } from 'ethers/lib/utils';

export default async function randFr(): Promise<mcl.Fr> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there any differences between this function, and the randFr function used in getRandomBlsPrivateKey?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No they are meant to do the same thing. That randFr function comes from the hubble library. I added this file a while back by copying the logic from the hubble project

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gotcha cheers

salt: string,
recoverWalletAddress: string,
): Promise<Bundle> {
const saltBytes32String = BlsWalletWrapper.saltToBytes32String(salt);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the reason for changing references of this from saltHash/recoverySaltHash to saltBytes32String?

Don't have a strong opinion, but wondering if saltHash would read better than having the type in the name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No particular reason. I think in a lot of my code I called it saltBytes32String to copy the function name and Kautuk called it saltHash in other places. I might switch to saltHash

Comment on lines +185 to +186
expect(await vg.hashFromWallet(wallet4.address)).to.eql(newHash);
expect(await vg.walletFromHash(newHash)).to.eql(wallet4.address);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General recovery question - my understanding of this scenario is that wallet 3 should not be able to send transactions after being recovered to wallet 4, is that correct?

If so, do you see value in a test that checks wallet 3 cannot send transactions after being recovered? That might be covered by existing assertions/not be desirable, curious on your thoughts here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this situation, wallet 3 is actually the trusted wallet for wallet 4. I've been using the term trusted wallet recently because I think that makes more sense then what I've been calling it. In this situation, we can assume that it was actually wallet 4 that lost it's private key and needs to recover to a new one. Wallet 3 is the trusted wallet that can call the recover wallet function to swap the PK for wallet 4.

After the recovery is done, wallet 4's old private key should no longer be able to send txs. But the new private key that now maps to wallet 4's public key should be able to send transactions for that hash. That's why here I check that the public key hash for the new private key is the same as the hash that maps to wallet 4's address. If that's the case, the new private key can send txs for that address.

With regard to the test. We already have a decent amount of recovery tests in here which is why I just wanted to add a test to check that the bundle created from the new functions works. I just looked though and it doesn't look like we have a test that explicitly checks that the old private key doesn't work anymore. So I think that would be a valuable test.

I could add an extra line to this test to check that the old private key no longer maps to any public key hash. Which would mean it can't send any txs. When a recovery happens we just zero out the hash that the private key maps to in the mapping stored in the verification gateway. So that private key can't do anything anymore.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah think I was confused about how this was working. I've got a good grasp now, thanks for clearing that up!

Trusted wallet as a term also makes sense. Wondering if renaming blsWallet3 to something like trustedBlsWallet would improve readability?

Makes sense in regards to additional test case.

@@ -120,6 +127,15 @@ export default class BlsWalletWrapper {
return this.ExpectedAddress(verificationGateway, pubKeyHash);
}

static async getRandomBlsPrivateKey(): Promise<string> {
await mcl.init(mcl.BN_SNARK1);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @jacque006, I was curious to get your thoughts on this. I want the getRandomBlsPrivateKey function to be static but I need to make sure .init has been called on mcl. Looks like the only way we can guarantee that right now is if I put this code in the signer (I originally did that and then pulled it out because I didn't think it makes sense). Do you think it's an issue if I import the mcl library in this file?

Copy link
Collaborator
@jacque006 jacque006 Jan 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would try to keep any logic like this in the signer dir, and I would use @thehubbleproject/bls over a direct mcl-wasm import.

You should be able to do something like this:

./contracts/clients/signers/getRandomPrivateKey.ts

import { mcl } from "@thehubbleproject/bls";

export default function getRandomPrivateKey(): Promise<string> {
    await mcl.init();
    return `0x${mcl.randFr().serializeToHexStr()}`;
}

Then add as an export to ./contracts/clients/signers/index.ts and consume here.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might run into issues running mcl.init twice. If that happens will have to try another way.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This worked for me. Thanks, @jacque006 !

@blakecduncan blakecduncan merged commit 6af93bd into main Feb 1, 2023
@blakecduncan blakecduncan deleted the addClientRecoveryMethods branch February 1, 2023 19:32
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
aggregator Aggregator backend related aggregator-proxy Aggregator proxy related clients contracts Smart contract related extension Browser extension related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0