Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

Wallet recovery #342

Merged
merged 17 commits into from
Dec 15, 2022
Merged

Wallet recovery #342

merged 17 commits into from
Dec 15, 2022

Conversation

blakecduncan
Copy link
Contributor

@blakecduncan blakecduncan commented Sep 20, 2022

Merging is blocked by #367

What is this PR doing?

  • Adding an addRecoveryWallet method to the RPC
  • UI changes to allow the user to recover

Video demo of recovery
https://www.loom.com/share/34e84b23cd8647058dc92afc43a9ed28

How can these changes be manually tested?

  • You can test by recovering from an instant wallet to the Quill extension (as seen in the demo video above)
  • This won't currently work on Goerli because there is a bug fix in progress. If you want to test it locally you need to use [email protected]

Does this PR resolve or contribute to any issues?

#281

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)

@github-actions github-actions bot added the extension Browser extension related label Sep 20, 2022
@blakecduncan blakecduncan marked this pull request as draft September 20, 2022 01:38
blakecduncan and others added 3 commits October 20, 2022 10:55
* added UI for entering recovery hash

* added ui for salt

* completed workflow for wallet recovery

* lint: fix

* updated hex conversion of salt

* using temp wallet for recovery

* replace address after recovery
@blakecduncan blakecduncan self-assigned this Oct 26, 2022
@blakecduncan blakecduncan marked this pull request as ready for review October 26, 2022 09:08
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.

Tested locally and works well. Was able to recover a wallet successfully from the instant wallet into Quill.

I'm not too confident reviewing the changes in the KeyringController.ts as it's quite a lot of new stuff for me, so would be good to get a second opinion on that specific file :)

const RecoverWalletModal = () => {
const [modalIsOpen, setIsOpen] = useState(false);
const [pageIndex, setPageIndex] = useState(0);
const [walletPrivateKey, setWalletPrivateKey] = useState<string>('');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Do we need to explicitly add the type here? Can see that we're letting typescript infer the type for the above useState's

Copy link
Contributor

Choose a reason for hiding this comment

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

My personal preference is to have explicit typing everywhere. Copied the above 2 lines from somewhere else in the codebase so missed adding type to it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I may lean towards letting typescript infer the type but don't have a strong opinion either way, so happy with either option as long as we're being consistent :)

Copy link
Collaborator

Choose a reason for hiding this comment

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

@kautukkundan Are you sure that's your preference in this case where TypeScript is correctly inferring the type? Would you also add <boolean> and <number> to the previous lines?

Copy link
Contributor

@kautukkundan kautukkundan Nov 3, 2022

Choose a reason for hiding this comment

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

@voltrevo Fine with me as is in this particular case.

The problem starts happening when using complex types. eg - let's say a new Address type is defined as a string that always starts with "0x". In this case initializing with default '' would also work as it'll infer type string, but may break code later when things get complex. This had been the case earlier when using sol.G1 and sol.G2 types in the HubbleBLS library.

So looking at broader picture and anticipating future I prefer being explicit even with simpler types to maintain consistency, If that makes sense.

<br />
<div className="text-[10pt] text-grey-700 leading-loose font-bold mt-2">
Do not close this modal until you have completed all the steps that
follows else you will lose access to your original keys!
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thoughts on:

"...steps that follow, else you..."

}> = ({ onComplete, walletPrivateKey }) => {
const { rpc } = useQuill();

const [salt, setSalt] = useState<string>('');
Copy link
Collaborator

Choose a reason for hiding this comment

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

Similar point here and line below, do we want to explicitly state the type?

</div>
<div className="mt-4 bg-grey-900 bg-opacity-25 rounded-md p-5">
{loading ? (
<div className="flex gap-4 text-[12pt] items-center ">
Copy link
Collaborator

Choose a reason for hiding this comment

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

Tiny bit of whitespace :)

* of wallet being recovered
* @param recoverySaltHash Salt used to set the recovery
* hash on the wallet that is being recovered
* @param signerWalletPrivateKey the private key of the wallet that is used
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpicky but just asking for consistency's sake, should the word "the" after signerWalletPrivateKey be capitalised here? Or should the capitalised words after the param names above this be uncapitalised?

Comment on lines +310 to +313
const addressSignature = await this.signWalletAddress(
recoveryWalletAddress,
newPrivateKey,
);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Might not be fully understanding here, my two questions are:

  1. Is this creating a signature using a randomly generated private key and an already generated contract address?

  2. If so, how is this linking an already created address to a randomly generated private key? My knowledge of ECDSA cryptography is that the address must be derived from the public key, which is derived from the private key. But I admit my knowledge may be lacking here as these are smart contract wallets and we're not using ECDSA? And I don't fully understand how they are instantiated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@JohnGuilding good question

  1. Yeah it is. It's the smart contract address of the 'instant wallet'. So the wallet we are recovering. Signed by the newly generated private key that will become the new private key for the address we are recovering.

  2. I may not be the best at explaining this but this is my understanding. This signature is basically how the trusted wallet (the one that was set in the recovery hash) says that this private key now has permission to act on behalf of the instant wallet address. When the recovery happens this private key will create a new public key but instead of deterministically generating an address the public key will map to the existing instant wallet address. This signature is a way of verifying and setting this PK as the PK to the old address without revealing the private key.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for the answer, have a better understanding now. Will make sure to re-read your recovery article too

@@ -110,6 +115,19 @@ export default class KeyringController {
return networkData.address;
},

createTempAccount: async (_request) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Same question for addRecoveryWallet(), do we need to add unit tests for this new logic? I'm assuming as this is the extension which isn't meant to be production code, we are happy with not adding tests?

Copy link
Collaborator

Choose a reason for hiding this comment

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

@JohnGuilding We generally don't have tests for the extension as they would be complicated to setup & manage. However, as extension gets more stable might make sense to add some e2e tests for more common paths. Would save us some regression testing as well.

Copy link
Collaborator

@jacque006 jacque006 left a comment

Choose a reason for hiding this comment

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

It might also be beneficial to move some of the functionality into the clients lib so that other wallets/dApps can take advantage of this functionality, similar to #220 . But I would be okay if that's follow up work.

};

createWallet();
// eslint-disable-next-line react-hooks/exhaustive-deps
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why are deps disabled here? Should just be rpc.

Copy link
Contributor

Choose a reason for hiding this comment

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

I sometimes don't understand these deps. Like it's not possible to change some deps without explicit reload which is gonna rerender anyway.

But yeah, let's add rpc here.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Useful comment on this issue with links for further reading from Dan Abramov
facebook/create-react-app#6880 (comment)

) : (
<div className="flex justify-between">
<div className="font-mono text-[11pt]">{walletAddress}</div>
{/* eslint-disable-next-line */}
Copy link
Collaborator

Choose a reason for hiding this comment

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

What rule was disabled here? Better to disable a specific rule than all linting rules.

Copy link
Contributor

Choose a reason for hiding this comment

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

it wanted to also have a "keyboard input" for the copy button which didn't make much sense to me.

@@ -110,6 +115,19 @@ export default class KeyringController {
return networkData.address;
},

createTempAccount: async (_request) => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@JohnGuilding We generally don't have tests for the extension as they would be complicated to setup & manage. However, as extension gets more stable might make sense to add some e2e tests for more common paths. Would save us some regression testing as well.

@@ -110,6 +115,19 @@ export default class KeyringController {
return networkData.address;
},

createTempAccount: async (_request) => {
const pKey = generateRandomHex(256);
Copy link
Collaborator

@jacque006 jacque006 Oct 27, 2022

Choose a reason for hiding this comment

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

Use a BN254 spaced key instead of just a random hex. See https://github.com/thehubbleproject/hubble-bls/blob/master/src/mcl.ts#L186 . We should expose that via the signer in the client library somewhere.

Copy link
Contributor Author

@blakecduncan blakecduncan Oct 27, 2022

Choose a reason for hiding this comment

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

@jacque006 Ahh interesting we do generateRandomHex a couple of other places in the keyring controller which is where I copied this from. Should I also switch those?

Also, just curious, what is the benefit of BN254 over a random hex?

Copy link
Contributor Author

@blakecduncan blakecduncan Oct 27, 2022

Choose a reason for hiding this comment

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

Just want to double check that this is the right way to call that function?

randFr().serializeToHexStr()

Copy link
Contributor

Choose a reason for hiding this comment

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

yeah makes sense

what is the benefit of BN254 over a random hex?

If I understand correctly, The random number generated might sometimes lie outside the range of the BN curve we are using. This function ensures it's in range.

Copy link
Collaborator

Choose a reason for hiding this comment

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

@kautukkundan correct. If the key is out of range, we just hash it into a key that is in range. I have a bucklist item/change that will be more explicit about this in the future thehubbleproject/hubble-bls#30

const netCfg = getNetworkConfig(network, this.multiNetworkConfig);

// Create new private key for the wallet we are recovering to.
const newPrivateKey = generateRandomHex(256);
Copy link
Collaborator

Choose a reason for hiding this comment

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

See above comment on BLS private key gen.

Copy link
Contributor

@kautukkundan kautukkundan left a comment

Choose a reason for hiding this comment

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

LGTM 🚀

@jacque006 jacque006 merged commit 9078f61 into main Dec 15, 2022
@jacque006 jacque006 deleted the wallet-recovery branch December 15, 2022 20:58
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
extension Browser extension related
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants