Skip to content
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

[cli] add alias support for sui addresses in the CLI #13738

Merged
merged 3 commits into from
Dec 2, 2023

Conversation

stefan-mysten
Copy link
Contributor

@stefan-mysten stefan-mysten commented Sep 11, 2023

Description

This PR adds support for basic alias naming of Sui addresses in the keystore. If an alias is not provided, it will automatically generate a random name as first-second based on a list of predefined strings (adjectives and precious stone gems).

Test Plan

Added tests to ensure new functionality works as expected.

Manual tests:
sui client new-address ed25519 my_alias_test / sui client new-address ed25519
image


If your changes are not user-facing and not a breaking change, you can skip the following section. Otherwise, please indicate what changed, and then add to the Release Notes section as highlighted during the release process.

Type of Change (Check all that apply)

  • protocol change
  • user-visible impact
  • breaking change for a client SDKs
  • breaking change for FNs (FN binary must upgrade)
  • breaking change for validators or node operators (must upgrade binaries)
  • breaking change for on-chain data layout
  • necessitate either a data wipe or data migration

Release notes

Added initial support for basic alias naming of Sui addresses in the keystore.

@vercel
Copy link

vercel bot commented Sep 11, 2023

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
mysten-ui ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 2, 2023 6:17am
sui-core ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 2, 2023 6:17am
sui-typescript-docs ✅ Ready (Inspect) Visit Preview 💬 Add feedback Dec 2, 2023 6:17am
3 Ignored Deployments
Name Status Preview Comments Updated (UTC)
explorer ⬜️ Ignored (Inspect) Visit Preview Dec 2, 2023 6:17am
multisig-toolkit ⬜️ Ignored (Inspect) Visit Preview Dec 2, 2023 6:17am
sui-kiosk ⬜️ Ignored (Inspect) Visit Preview Dec 2, 2023 6:17am

Copy link
Contributor

@amnn amnn left a comment

Choose a reason for hiding this comment

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

I realised only after I reviewed that this PR was in draft! I just got the email for it and blindly started reviewing. (I guess I was excited cos this is a cool feature :) ).

High level notes:

  • You'll need to add tests for this new command, especially for the edge cases around aliases not existing and already existing.
  • You need to handle the case of a keystore that does not have an alias for a particular address, because people will update their CLI and use it with an existing key store.

crates/sui-keys/src/random_names.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/random_names.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/random_names.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui/src/client_commands.rs Outdated Show resolved Hide resolved
crates/sui/src/client_commands.rs Outdated Show resolved Hide resolved
crates/sui/src/keytool.rs Outdated Show resolved Hide resolved
@stefan-mysten
Copy link
Contributor Author

stefan-mysten commented Sep 12, 2023

I realised only after I reviewed that this PR was in draft! I just got the email for it and blindly started reviewing. (I guess I was excited cos this is a cool feature :) ).
High level notes:
You'll need to add tests for this new command, especially for the edge cases around aliases not existing and already existing.
You need to handle the case of a keystore that does not have an alias for a particular address, because people will update their CLI and use it with an existing key store.

Sorry about that @amnn. I wanted to push it up to be able to advertise it and get early feedback on this, and was planning to work on it more. It's a bigger feature than I expected so it will require a few more iterations! Thanks though for taking a look at it!

@amnn
Copy link
Contributor

amnn commented Sep 12, 2023

No problem, usually it's fine to push it as a draft, I think the issues happen if the PR is ever published!

Copy link
Contributor

@amnn amnn left a comment

Choose a reason for hiding this comment

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

Much improved, thanks @stefan-mysten ! Main comments in this review:

  • Can we store the public key, rather than the private key in the aliases table?
  • Make sure error messages make sense to users of the tool, not just us as developers (no debug output, no mention of internal types, etc).
  • Some other clean-ups.

crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
@@ -270,6 +394,10 @@ impl AccountKeystore for InMemKeystore {
None => Err(anyhow!("Cannot find key for address: [{address}]")),
}
}

fn create_alias_if_not_exists(&self, _alias: &Option<String>) -> Result<String, anyhow::Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

This makes me think that this function doesn't belong on this trait. Instead, why don't you implement it as a free function that accepts a &dyn AccountKeystore to get access to the existing aliases from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I've been torn on it. Initially I had aliases for the InMemKeystore but it kinda does not make sense to have one, I think. I'll fix this, thank you.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

&dyn AccountKeystore might not work because sign_hashed takes a generic type T, and the compiler complains. I'll find a workaround.
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@amnn this is the only thing that I am not sure how to go about. Otherwise, PR is ready for re-review.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'll need to see the rest of the error message (e.g. why this trait cannot be made into an object) to know for sure how to fix this issue, but I think it's possible to side step this entirely, because I was only interested in creating a function like this:

fn create_alias_if_not_exists(store: &dyn AccountKeyStore, alias: &Option<String> -> Result<String> {
    let existing: HashSet<_> = store.alias_names().into_iter().collect();
    match alias {
        Some(a) if existing.contains(a) {
            bail!("Alias {a} already exists. Please choose another alias.");
        }

        Some(a) => Ok(a),
        None => random_name(&existing_aliases)
    }
}

Or better yet, change the interface, so that AccountKeyStore can tell you whether an alias_exists, and that should be an easier to re-use primitive.

crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/random_names.rs Outdated Show resolved Hide resolved
@joyqvq joyqvq self-requested a review November 28, 2023 02:13
Copy link
Contributor

@amnn amnn left a comment

Choose a reason for hiding this comment

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

Left a suggestion on how to improve the interface and some other small nits, but these are not blockers!

crates/sui/src/client_commands.rs Outdated Show resolved Hide resolved
crates/sui-keys/tests/tests.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Show resolved Hide resolved
crates/sui-keys/src/keystore.rs Outdated Show resolved Hide resolved
@@ -270,6 +394,10 @@ impl AccountKeystore for InMemKeystore {
None => Err(anyhow!("Cannot find key for address: [{address}]")),
}
}

fn create_alias_if_not_exists(&self, _alias: &Option<String>) -> Result<String, anyhow::Error> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'll need to see the rest of the error message (e.g. why this trait cannot be made into an object) to know for sure how to fix this issue, but I think it's possible to side step this entirely, because I was only interested in creating a function like this:

fn create_alias_if_not_exists(store: &dyn AccountKeyStore, alias: &Option<String> -> Result<String> {
    let existing: HashSet<_> = store.alias_names().into_iter().collect();
    match alias {
        Some(a) if existing.contains(a) {
            bail!("Alias {a} already exists. Please choose another alias.");
        }

        Some(a) => Ok(a),
        None => random_name(&existing_aliases)
    }
}

Or better yet, change the interface, so that AccountKeyStore can tell you whether an alias_exists, and that should be an easier to re-use primitive.

@stefan-mysten
Copy link
Contributor Author

Thanks @amnn for the suggestions.

@stefan-mysten
Copy link
Contributor Author

I'll need to see the rest of the error message (e.g. why this trait cannot be made into an object) to know for

image

Here's the error; as per the docs, sign_secure takes a generic type, so this is not an object safe trait.
https://doc.rust-lang.org/reference/items/traits.html#object-safety.

@vercel vercel bot temporarily deployed to Preview – mysten-ui December 2, 2023 06:17 Inactive
@stefan-mysten stefan-mysten merged commit 1265c0a into MystenLabs:main Dec 2, 2023
34 checks passed
@stefan-mysten stefan-mysten deleted the cli_add_address_alias branch December 2, 2023 06:40
@amnn
Copy link
Contributor

amnn commented Dec 2, 2023

Got it, yep. So the trick would be to try and make that API use dynamic dispatch as well by changing it to accept a msg: &dyn Serialize.

This may not work because Serialize might not be object safe either, in which case that's time to give up 🥲, but that's what I would try.

@stefan-mysten
Copy link
Contributor Author

Got it, yep. So the trick would be to try and make that API use dynamic dispatch as well by changing it to accept a msg: &dyn Serialize.

This may not work because Serialize might not be object safe either, in which case that's time to give up 🥲, but that's what I would try.

I'll give it a try in a different PR. There's a bunch of commands that need integration with alias now, and that's a bit more important. But I'd love to refactor this a bit later once everything is in, because then I will have a more clear picture of what's going on, what's needed, and where. Thanks @amnn for guiding this one!

@FrankC01
Copy link

FrankC01 commented Dec 7, 2023

My 0.2 cents and trying to rationalize community SDK impacts.

Firstly (pet peeve)
As with the key-pair file they would not be tied to an env (dev,test,etc.). It would have been grand to associate specific keyfiles and aliases to the env alias in client.yaml.

Clarity
If I understand the end result of previous comments, Address may have a 0 or 1 alias association?

  • Initially, a new config will auto generate an alias?
  • Keytool will accept an alias on generate, import, load-keypair?
  • Will keytool also accept on signing?
  • What about MultiSig? Methinks not on this one ass MS addresses are not persisted
  • CLI, all commands that take an address will also accept an alias?

I see that in 1.16 the alias file is being generated already so I assume as per current schedule we are looking at Jan '24... happy new year!

@amnn
Copy link
Contributor

amnn commented Dec 7, 2023

I will let @stefan-mysten clarify the detailed workings of this feature, but on this point:

As with the key-pair file they would not be tied to an env (dev,test,etc.). It would have been grand to associate specific keyfiles and aliases to the env alias in client.yaml

I think this is something that we'll need to think about more holistically than in the context of just this feature. Technically speaking, there is no association between a given keypair and a network: People are free to use the same keypair across multiple networks and this is also true in the wallet (the wallet allows users to switch addresses independently of the network).

In practice, people do tend to associate addresses with networks though, so we should discuss whether that is a feature we want to support, but need to be careful about not breaking backwards compatibility for people who do use the same address across multiple networks.

@FrankC01
Copy link

FrankC01 commented Dec 7, 2023

In practice, people do tend to associate addresses with networks though, so we should discuss whether that is a feature we want to support, but need to be careful about not breaking backwards compatibility for people who do use the same address across multiple networks.

Yes, separation of concerns so to speak. Again, pet-peeve.

Notwithstanding the Telegram aspect of 'use at own risk' reading the binary setup config has been as base capability of pysui, there are other options that let dev set RPC URL and private keys (ephemeral). However; aliases (IMO) is a nice feature and of course I'd rather leverage what is sitting there than build something anew.

@FrankC01
Copy link

FrankC01 commented Dec 8, 2023

@stefan-mysten
Following up from Telegram. Here were original questions:

  1. Initially, a new config will auto generate an alias?
  2. Keytool will accept an alias on generate, import, load-keypair?
  3. Will keytool also accept on signing?
  4. What about MultiSig? Methinks not on this one ass MS addresses are not persisted
  5. CLI, all commands that take an address or public key will also accept an alias?

And, mapping to your comments in Telegram

General:
Address may have a 0 or 1 alias association?

It shall be 1:1 (error otherwise)

  1. you: Yes
  2. Keytool will accept an alias on generate, import, load-keypair?

You:

  • Generate does not put the keys in keystore, just puts a file out so this is moot. Not suggesting to add alias to that file.
  • Load is moot as well
  • Import puts key into keystore and I gleaned from your response it will take an optional alias, generating one otherwise.
  1. Will keytool also accept on signing?
    My clarification on question: CLI hassui keytool sign --address <- Will this command take an alias as well?

  2. What about MultiSig? Methinks not on this one ass MS addresses are not persisted
    My clarification on question: This is moot as while there is an address associated to a mulisig, the keypair itself is derived from 2 or more normal keypairs and because of that, there is no multisig keypair that goes in sui.keypairs. I must not have had enough coffee at the time I wrote the question

  3. you: That is the plan.

Additional questions:
For the alias name itself, are there any constraints there? The obvious is you can't have the same alias name referring to two keys but:

  1. Any length constraints?
  2. Any format constraints?
  3. Any character constraints?

Finally, does the public key base64 string value for the alias include the keytype prefix or is it the raw 32 or 33 public keybytes only?

@stefan-mysten
Copy link
Contributor Author

stefan-mysten commented Dec 11, 2023

@FrankC01 thanks for following up and apologies for the slow response (I was OOO).

Will keytool also accept on signing?

Now I understand the question, yes, it should be available there too!

For the alias name itself, are there any constraints there? The obvious is you can't have the same alias name referring to two keys but:

Good question. I understand that all sorts of weird stuff can be done when naming things, so here's what I've done. Given that the alias is supposed to make it easier to distinguish addresses, and its main purpose is to aid and simplify the experience for the user when using the CLI, I think a few simple rules are useful here.
The alias must start with a letter and can contain only letters, digits, hyphens (-), or underscores (_). There is no maximum length, but if it is too big it will likely mess the table formatting (will need to revisit that at some point to do automatic wrapping everywhere), so naively, I will hope for now that folks are reasonable with their alias length. I can enforce it later if needed.

@FrankC01
Copy link

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants