stacks-network / stacks-core

The Stacks blockchain implementation
https://docs.stacks.co
GNU General Public License v3.0
3.01k stars 674 forks source link

[bns] Function to get all names associated with a principal #2211

Closed aulneau closed 3 years ago

aulneau commented 3 years ago

I was playing around with how to possibly integrate read-only calls into the explorer related to BNS, and it seems that it's not currently possible to call a function to return any names associated with a given principal. I think this would be very useful for clients such as the explorer and the wallet(s). I spoke with Ludo this morning and he said that while we don't currently have a function for that, it is technically possible.

If there was a read only call that provided this functionality, it might provide a good interim state of useful functionality while we wait for the stacks-blockchain-api to surface similar features.

hstove commented 3 years ago

Hey @aulneau , I believe this is exactly the work we're figuring out related to BNS endpoints. We want to support all the same BNS endpoints as https://core.blockstack.org .

jcnelson commented 3 years ago

I think you can just query the owner-name map directly on the node, via the /v2/map_entry/${boot_address}/bns/owner-name endpoint. However, this only works with on-chain names -- off-chain names are handled by the side-car. Also, the rules are one on-chain name per address now -- no need to worry about getting a list of them!

aulneau commented 3 years ago

Also, the rules are one on-chain name per address now -- no need to worry about getting a list of them!

I see -- is it the case that previously an address could have multiple on-chain names?

lgalabru commented 3 years ago

@aulneau that was the case yes, we're introducing this limitation 1 address, 1 name in v2.

aulneau commented 3 years ago

@aulneau that was the case yes, we're introducing this limitation 1 address, 1 name in v2.

What happens if there are addresses that currently have 2+ names? (maybe there aren't any)

Is there any further context/discussion I could read up on this choice?

lgalabru commented 3 years ago

Addresses that have multiple names will keep resolving on v2, and their owner will be able to update / transfer / revoke / renew (if giving a new principal) these names. We just no longer authorize users to register multiple names.

MarvinJanssen commented 3 years ago

We just no longer authorize users to register multiple names.

What is the reasoning behind this?

GinaAbrams commented 3 years ago

This seems like a big change from the UX perspective, for users and app creators alike. I am used to having the option to try out apps with a number of IDs. Any way this can be reconsidered?

jcnelson commented 3 years ago

You can own multiple names; you just need to put each name on its own address. This is the way the Browser has worked for years, and will continue to be the case going forward.

jcnelson commented 3 years ago

@lgalabru Addresses that have multiple names will keep resolving on v2, and their owner will be able to update / transfer / revoke / renew (if giving a new principal) these names.

Wait, how is this represented in the owner-name map? Are there multiple on-chain names today that are currently owned by the same address?

friedger commented 3 years ago

This address has the most names registered: https://core.blockstack.org/v1/addresses/bitcoin/1AQpTFvdq8yARVGvgderi4EWvynnc5gHxW

Others: https://core.blockstack.org/v1/addresses/bitcoin/17MJkt1kfmV5SjWDeNmzjdJpawwCXn9kyY https://core.blockstack.org/v1/addresses/bitcoin/1A2cUvkpL6G6RKaiLD3sA8TS65RbPmYhZD https://core.blockstack.org/v1/addresses/bitcoin/1A65TXd3823vAhKf4dHWw3FgkEJZkUdyBk

and more personal: https://core.blockstack.org/v1/addresses/bitcoin/17Gqtn8Ekit3dctn395zEU7FpcDhbrwSQM https://core.blockstack.org/v1/addresses/bitcoin/18QeNVVZ7TKvzVZxaXznAJVpUKXm2WHUBV https://core.blockstack.org/v1/addresses/bitcoin/1PXmG9FjGTDH1FcNPe7ttDNkEnML7VgNGo

aulneau commented 3 years ago

@lgalabru @jcnelson curious if this choice is locked in? I could see rationale for a single address to be able to own more than one ID, and I'm curious what we gain/lose by adding this restriction.

MarvinJanssen commented 3 years ago

It will also throw a wedge in one of our projects. We can get around it by generating keys from the app key or something but we would much rather have all names owned by the same address.

hstove commented 3 years ago

Marvin, if this is for the android wallet, I would definitely recommend against having on address control everything. Maybe we should sync about how we're handling this kind of thing. It would be great for us all to be on the same page

jcnelson commented 3 years ago

@aulneau I could see rationale for a single address to be able to own more than one ID, and I'm curious what we gain/lose by adding this restriction.

The rationale for removing the ability for one address to own multiple names is the fact that the BNS authentication protocol signs the user into an app by their key, not by their name. The name is just a pointer to the user's public key to use. So, if you own multiple names on the same address, and you sign into an app with one of them, all of those names will get the same application private key. Moreover, as far as Gaia is concerned, all of those names can read and write to the same bucket (since Gaia doesn't know or care about names).

This is problematic for privacy. If you have a "work" and a "personal" name owned by the same key, for example, and you sign into a sensitive app at work with the "work" name, your "personal" name will also be granted access. Similarly, if you store data with your "personal" name, your "work" name will be able to see it. This would be a nasty surprise for users.

For on-chain names, we can't prevent the user from accidentally trying to register two names to the same address. This is because there's a window of time in-between when the user signs and sends the preorder/register transactions, and when the name gets instantiated in the BNS smart contract (this is true in Stacks 1.0 as well -- you have to wait for the transactions to get mined). Today, the wallet tries to ensure that each name gets registered to a different address by locally caching which addresses have in-flight name registrations. But, if the user uninstalls/reinstalls the wallet, or uses a web wallet in incognito mode and/or clears the app storage for it, then this information gets lost. If the user then proceeds to register more names while having in-flight name registrations, then today the user will end up with multiple names owned by the same address (potentially leading to nasty privacy surprises down the road). With the one-name-per-address policy, these subsequent name registrations would simply fail (which is also nasty -- the user doesn't get the money back -- but it's also an easier pitfall to help users avoid).

aulneau commented 3 years ago

This is problematic for privacy. If you have a "work" and a "personal" name owned by the same key, for example, and you sign into a sensitive app at work with the "work" name, your "personal" name will also be granted access. Similarly, if you store data with your "personal" name, your "work" name will be able to see it. This would be a nasty surprise for users.

I feel like we are making some rather big assumptions for the end user. While yes, if the user has these two different contexts (work, personal), it would not be ideal for them both to be associated with the same address, but that feels more like a problem to be fixed via education and whatever application is implementing the authentication/username registration. A privacy informed choice we made for the authenticator was to display a message warning people when they use the same ID/account for many apps.

I can see that there are reasons someone might want to share IDs between a single address, eg I want aulneau.stx and aulneau.btc. Maybe there is an app that only is aware of names in the .btc namespace, and others that are only aware (or make use) of a different namespace, but I don't want them to be different addresses.

@MarvinJanssen @friedger I'd be curious about your perspective on it

jcnelson commented 3 years ago

@aulneau While yes, if the user has these two different contexts (work, personal), it would not be ideal for them both to be associated with the same address, but that feels more like a problem to be fixed via education and whatever application is implementing the authentication/username registration. A privacy informed choice we made for the authenticator was to display a message warning people when they use the same ID/account for many apps.

Gaia does not know what name you signed in with. As long as we treat BNS names as labels for separate accounts with their own data (which is what we've been doing), then it will be surprising to the user that two different names end up reading and writing the same state. Moreover, it would be possible for two different accounts to overwrite and corrupt one another, since they would have the same application private key.

Can you describe a plausible app where you sign in with different usernames, but no matter which username you use, you get the same account state?

aulneau commented 3 years ago

Gaia does not know what name you signed in with. As long as we treat BNS names as labels for separate accounts with their own data.

I would say this could be reason enough for either argument (for having multiple names or for having one).

I think I'm coming at this from not an app-data based perspective (because usernames are not known in that context as you've said), but more from a perspective of other functions where an address might want/need to be used with different IDs.

Can you describe a plausible app where you sign in with different usernames, but no matter which username you use, you get the same account state?

It would not be so much an app where you sign in with different usernames, but instead an app that has a namespace restriction. Let's say you have a social media application that has their own namespace, .instagram. I currently own aulneau.id, but when I go to use the app, they request I register a name in their namespace for purposes of their own. I would create aulneau.instagram because it's the same context and I'm okay with my two names being associated to the same address, I might even prefer that. (I understand that an off-chain name would be better here, but an app could have any tx for registering a name in this namespace be sponsored.) By providing the ability to have many IDs share a name, I can still have access to any funds/tokens within that address, and not need to send between addresses. This is just one example, of which more I'm sure will come up over time.

What I'm trying to express is that I don't believe the correct answer for this one, specific issue:

If you have a "work" and a "personal" name owned by the same key, for example, and you sign into a sensitive app at work with the "work" name, your "personal" name will also be granted access

is to simply prevent an address from owning many on-chain IDs. I feel as though whatever the rationale for having many off-chain names is, could apply to on-chain names, too.

It seems like we are restricting something that could have real, valid use cases over time that maybe we can't accurately predict.

jcnelson commented 3 years ago

It would not be so much an app where you sign in with different usernames, but instead an app that has a namespace restriction. Let's say you have a social media application that has their own namespace, .instagram. I currently own aulneau.id, but when I go to use the app, they request I register a name in their namespace for purposes of their own. I would create aulneau.instagram because it's the same context and I'm okay with my two names being associated to the same address, I might even prefer that. (I understand that an off-chain name would be better here, but an app could have any tx for registering a name in this namespace be sponsored.)

In this case, all you'd need to do is derive a private key from aulneau.id's app private key, and use it to register (and own) aulneau.instagram. This ought to be straightforward to do in Connect -- you'd simply be registering aulneau.instagram from within the app by signing the BNS preorder/register transactions and sending them to the transaction broadcaster endpoint for subsequent relay (much like how we do today). These would be sponsored transactions -- you'd be signing them with an app-specific key, but you'd be funding them from your wallet key in the Connect app.

Once the app private key for aulneau.id owns aulneau.instagram, the app can authenticate you once you sign in with aulneau.id simply by checking that aulneau.instagram exists and is owned by the particular private key derived from the app private key.

Also, the app itself probably doesn't need to use BNS for this. If all the app wants to do is ensure that the name owner also owns a particular NFT (e.g. to show that they paid for access), then there are far simpler ways to achieve this. For example, the app could simply deploy a smart contract with a public function you have to call that will mint you the NFT in exchange for some STX.

By providing the ability to have many IDs share a name, I can still have access to any funds/tokens within that address, and not need to send between addresses. This is just one example, of which more I'm sure will come up over time.

The fact that sponsored transactions exist in Stacks 2.0 should remove the need to put your name and tokens on the same key in order to spend those tokens in behalf of that name's account. In general, sponsored transactions allow you to use one key to fund a transaction signed by another key.

What I'm trying to express is that I don't believe the correct answer for this one, specific issue ... is to simply prevent an address from owning many on-chain IDs. I feel as though whatever the rationale for having many off-chain names is, could apply to on-chain names, too.

I can think of many reasons to keep it at one-name-per-address, both for on-chain and off-chain names.

MarvinJanssen commented 3 years ago

@hstove this is for the .BTC namespace project. We want to create a UX where a user signs in with their Stacks account and can then register one or more .BTC domains and manage them using that single account. The idea is to stick close to what people are used to when using conventional registrars: you have one account to manage a multitude of domains. If only one name per address is allowed, we would need to do one of the following:

  1. Use the app key as a seed and derive a new key for each new domain name to be registered. The main account would then sponsor all those registration/management transactions. This was the original plan. The names will then not be usable in other apps. (Unless you somehow extract the derived keys from the app, which I am against.)
  2. Have the user switch accounts in Connect for every domain name. Perhaps it could be an idea to give apps the ability to hint which account the user should sign into and allow for fast account switching (something like a requestSignIn("marvin.btc") and have corresponding UI to approve or deny the switch). It would still rather pollute your Connect app if you are one of those people who owns tons of domains.

Both options feels cumbersome and detrimental to the UX. I side with @aulneau and think it is more of an educational question. I likewise own a few aliases that I honestly would have preferred to all be owned by a single address. Even something simple like conventional email allows for aliases these days. Having messages delivered to the same dapp inbox, whether it would be sent to marvin.id, marvin.btc, or marvin.stx, seems like a very basic feature. Switching usernames whilst keeping your user data is also cumbersome as you would have to transfer your current one away, then transfer your new one in. Since names are NFTs in BNSv2, why make it so each address can only own one?

Regarding the other points, I think most dapps do not care about what the actual username is, and those that do can provision for it. Half the apps break if you sign in with an account without a username so more work is left to be done in that field regardless. (Come to think of it, I was doing some preliminary research on this a few months ago and it turns out most dapps never even check the validity of usernames, so you can impersonate anyone on some of those dapps.)

And when it comes to security, I personally do not like the idea that my valuable usernames might be owned by some derived app private keys that exists on my machine. I would much rather have the names owned by keys that never leave a hardware device (hint). Imagine your names being stolen due to a flaw in a dapp. This alone precludes option 1.

jcnelson commented 3 years ago

Even something simple like conventional email allows for aliases these days.

Be that as it may, nearly all Web apps today will treat each email address as a unique account identifier. Just because foo@me.com is an alias for bar@me.com doesn't mean that when I log into Facebook with foo@me.com, I see the profile data for bar@me.com. That would be weird.

Also, BNS names are not email addresses. They are account identifiers.

I side with @aulneau and think it is more of an educational question.

I'm on the side of minimizing user surprise. If we make an unforced design decision that leads to having to re-educate users on how to do something as fundamental as logging in, then it's probably the wrong choice to make. A good deal of R&D has gone into making Connect as easy-to-use and as low-surprise as it is today (and admittedly, "low"-surprise is relative, since very few Web apps today require you to recall a 12-word seed phrase), but I can't think of a single widely-used Web app where your account state has multiple account identifiers. I think it's already hard enough on users that we're re-educating them on how to use a seed phrase to log into apps.

What evidence do you and @aulneau have to suggest that giving accounts multiple distinct identifiers would further minimize user surprise? Asking because so far, this system behavior has been found to be highly surprising [1] [2] [3] [4].

If only one name per address is allowed, we would need to do one of the following:

I am not convinced that these are the only two options.

For example, a third option could be to make it so each name is owned by a 2-of-2 multisig address, where one signer is a key derived from your app private key and the other signer is a key derived from your wallet (such as your Ryder device). Then, if you needed to do something with your name, the app would still need to prompt you to sign the transaction that does so via an external wallet whose key it cannot access on its own.

As another example, a fourth option could be to instantiate an on-chain holding contract for each name (recall that a contract can own a name). The holding contract would be a value-add to users, since it could add extra security features like:

Each contract's address would be linked to your app private key to prove your ownership of each name, but as far as BNSv2 is concerned, each name is "owned" by a distinct address.

Since names are NFTs in BNSv2, why make it so each address can only own one?

BNSv2 names are implemented as NFTs on-chain in order to guard them from loss or theft with post-conditions, so that a user interacting with a buggy or malicious smart contract won't accidentally get their name taken away. That's the only reason we use NFTs to represent them.

Also, you can own many names with the same private key; you just need to derive different sub-keys from it that own each name. The need to have one key owning may names is fulfilled with one-name-per-address.

It seems like we are restricting something that could have real, valid use cases over time that maybe we can't accurately predict.

As a general rule of software engineering, it's a bad idea to implement a feature unless you can think of a plausible non-trivial application that cannot be built without it. I'm not opposed to making it so an address can own multiple names in principle, but I am having a very hard time finding adequate justification for doing so. Given how much user surprise it has generated in practice, I'm currently inclined to consider the one-name-per-address change to be a bugfix.


[1] https://forum.stacks.org/t/switching-between-multiple-ids-that-have-the-same-address/5086

[2] https://github.com/blockstack/blockstack-browser/issues/1121

[3] https://github.com/blockstack/blockstack-browser/issues/1055

[4] https://github.com/blockstack/blockstack-browser/issues/1075

MarvinJanssen commented 3 years ago

Be that as it may, nearly all Web apps today will treat each email address as a unique account identifier. Just because foo@me.com is an alias for bar@me.com doesn't mean that when I log into Facebook with foo@me.com, I see the profile data for bar@me.com. That would be weird.

The first example I can think of is Apple ID. I have multiple email addresses (call them identifiers) associated with it, and I can use any of them to sign in. I can even use my phone number to sign in. They all lead to the same account an associated user data (iCloud Drive and so on.) Messages sent to any of those associated identifiers also end up in the same inbox. I find it extremely convenient but this does not add any surprises for people that do not use multiple identifiers for their Apple ID by virtue of them not having multiple identifiers. It would come down to the UI/UX of Connect (or should I say Wallet) in how it displays accounts and usernames (if at all).

Also, BNS names are not email addresses. They are account identifiers.

The email address example was just one instance in where owning multiple usernames with a single address is useful. See the Apple ID example for another. The nomenclature will also require education for new users of the ecosystem. "Username", "email/message address", "wallet address", "domain name".

Granted, selecting a different username and seeing the same user data could cause confusion for users but I think it depends on the UX. I would say one could select a "display name" if multiple names are associated with an address and Wallet just shows that one. And if this is considered advanced usage or "discouraged", a setting to toggle multiple username display and association is an option. Like Dan says in that old issue, I have also always considered it a feature.

For example, a third option could be to make it so each name is owned by a 2-of-2 multisig address, where one signer is a key derived from your app private key and the other signer is a key derived from your wallet [...]

That could be a solution for our namespace project but it would not guarantee other apps do it that way, and then one is left transferring those names to multisig addresses manually.

As another example, a fourth option could be to instantiate an on-chain holding contract for each name (recall that a contract can own a name) [...]

I like this idea and we considered it, but does that not mean we have to deploy an (arguably) simple contract for every username? Is this where we can find a compromise; i.e.: allow contracts to own multiple names? (Although it still sounds a little painful having to deploy one or more contracts just to manage your names.)

jcnelson commented 3 years ago

The first example I can think of is Apple ID. I have multiple email addresses (call them identifiers) associated with it, and I can use any of them to sign in. I can even use my phone number to sign in. They all lead to the same account an associated user data (iCloud Drive and so on.) [..]

In both Apple IDs and BNS, you still have a primary account identifier key. In BNS, it's your ID-address. Internally, I'd wager that Apple IDs do something similar -- your phone number, your alternative email addresses, and so on all point to your primary account identifier in Apple's database.

The one-name-per-address rule does not stop you from doing something similar in BNS. You can add secondary identifiers to BNS ID-addresses via other smart contracts. For example, I could deploy my own smart contract that maps phone numbers to BNS ID-addresses, and I could structure it so that any registrations or updates to this map can only happen with both my permission and the ID-address owner's permission. This way, if you wanted to bind your phone number to your ID-address in my contract, you'd have to convince me that it's actually your phone number.

I could easily create such a service. It would prompt you to sign in with your ID-address, use Twilio or the like to text your number a one-time short-lived password that you then have to enter on my service's Web page, and once you have done so, it would send a transaction that updates the map. I could even charge you for the transaction fee. Then, you (or an app you use) would be able to use your phone number and this contract's data map to find your ID-address. Importantly, anyone could create such services, and they could do so without having to update the BNS contract.

So, I don't see how one-name-per-address is a limitation here. It looks orthogonal to me -- no matter how many or few names an address can own, it's always possible to make these secondary ID <--> ID-address mapping contracts. Moreover, app developers can add (or remove) support for reading these secondary mappings as they see fit. It would be helpful if the ecosystem could standardize on how secondary mappings work (if they do prove necessary in the first place), but that's a concern best handled by the SIP process.

Granted, selecting a different username and seeing the same user data could cause confusion for users but I think it depends on the UX

If you take a look at the issues I linked above, the problem with multiple names per address is that it can happen by accident, which leads to the bulk of the surprise. I agree that it would be less of an issue if users had intended to register multiple names. But, it's not (just) the UX that's the problem here -- it's the race condition that arises from having to wait for a name registration to be confirmed. Only by preventing multiple names from being owned by the same address can we fix this race condition.

That could be a solution for our namespace project but it would not guarantee other apps do it that way, and then one is left transferring those names to multisig addresses manually.

I'm not sure I follow? Each BNS name created this way would still show up in the BNS contract, as well as emit all of the relevant events for explorers to track. But since you're creating an app that registers and manages BNS names, then surely the app would be rightfully responsible for generating the appropriate transactions for sending names in and out of it?

I like this idea and we considered it, but does that not mean we have to deploy an (arguably) simple contract for every username?

Yes, it does mean that. It might be an option for the security value-adds I described above, but as you point out, it would get painful if every name needed it. But, it is an option if for some reason you must have a single address own many names.

friedger commented 3 years ago

I can see the benefit of using more than one username for the same address, in particular if I have to pay for my gaia hub.

It shouldn't be too difficult to update Stacks Wallet/Connect to show you all the username aliases for the id address when authenticating.

With email you have sometimes similar aliases using + like foo@mail.me and foo+bar@mail.me. Sometimes these are two different identifiers, sometimes they are the same. Sometimes, I can also use the email with or without domain.

Furthermore, you can have ssl certificates for more than one domain.

If possible I would try to keep the one to many relationship between address and usernames.

The current implementation of BNS overrides the previous names if a principal owns more than one, if I understand the code correctly.

I don't like the instagram namespace example because an app should not force me to use a certain username. It is my identity. Maybe I haven't understood the semantics of namespaces yet.

aulneau commented 3 years ago

I appreciate all the healthy debate in this thread! :)

Some more general thoughts:

To me it still seems that we are making a lot of assumptions related to this choice. These all seem like preferences rather than any hard requirement. Sure, this choice would remove some harder UX things, but is it the responsibility of this contract to do so? Isn’t it more the responsibility of the application that implements authentication? I feel like if the answer is to prevent people from having a one-to-many relationship with address to names, then the auth app should work to prevent that.

I feel as though part of the conflict in this topic is that it seems like we might have different meanings or purposes associated with having a name.

Open questions: Why do we have names and what function do they serve? Why do we have namespaces and what function do they serve?

These are some general patterns that seem to be present in this thread:

jcnelson commented 3 years ago

Sure, this choice would remove some harder UX things, but is it the responsibility of this contract to do so?

Making it so you don't accidentally register multiple names to the same address, when you intended not do this, is definitely the contract's responsibility. Only the contract has a consistent up-to-date view of which address owns which name(s) at all times, and only the contract (or a set of contracts running in the same transaction) can check that the address was the intended address and permit or deny the registration in an atomic operation.

I feel like if the answer is to prevent people from having a one-to-many relationship with address to names [..]

We aren't preventing this at all. Per my telephone number <--> ID-address example above, nothing stops you from having multiple identifiers for your ID-address. It's just that your identifiers would need to live in separate contracts. I think this is actually better than trying to get BNSv2 to permit an address to have multiple identifiers, because:

Given this, I'm more convinced now that having multiple names per ID-address in BNS would create a layering violation. It's not BNS's job to keep track of all current and future identifiers for your ID-address, especially since we don't even know what kinds of identifiers people will invent in the future.

BNS's primary responsibility is to keep track of your ID-address state. As a secondary responsibility, it comes with a built-in naming system to ensure that you do not need to remember a bare ID-address to find the associated state. But because Stacks 2.0 has smart contracts, supporting multiple, different identifiers can be handled by other smart contracts -- we don't have to do everything ourselves.

... then the auth app should work to prevent that.

The auth app can't prevent this, per above. It's not in any position to prevent it without the contract's help.

Why do we have names and what function do they serve?

They are globally-unique identifiers for your ID-address and associated state (like your zone file hash).

Why do we have namespaces and what function do they serve?

They encode different longevity and pricing rules for your BNSv2 names.

These are some general patterns that seem to be present in this thread:

Per my above comment, BNS is no longer the only option for identifying your ID-address, and namespaces are no longer the only grouping mechanism for identifiers. It's just that they're the built-in default ones, so we have something to work with on day 1 (and also because we're migrating everyone over from Stacks 1.0). Other contracts can also point to ID-addresses and their associated state.

In fact, it wouldn't be too hard to define a contract trait interface for resolving a contract-specific identifier to a BNS ID-address and its state. There could be many such contracts that implement this trait -- one per distinct BNS ID-address identifier type -- and an explorer would be able to keep track of them and give a list of their contract addresses to any app that needs to resolve a custom identifier.

If the premise is that each name is always a different account, then it would make sense to have 1 address → 1 name

One address owns one name only within BNS.

Regardless of the reasoning for making names NFTs, they are NFTs, and as such, there will be a marketplace for them and some set of users will use them as such, what material benefit does it serve to restrict them in this way?

I'd argue that making BNS names tradeable is out of scope. It is sufficient to represent them as NFTs to support future tradeability. If BNS names are valuable enough to be traded, then someone will figure out a way to trade them.

markmhendrickson commented 3 years ago

Whoooo there's a lot of text in this thread 😆

We may want to jump on a call soon to hash this out, perhaps on the Discord #dev-hangout. In the meantime, here are some thoughts from yours truly.

Two UX priorities stick out to me here:

  1. Users can authenticate to any app with any address that already has an associated username, then subsequently register another username that the app and wallet providers can subsequently map back to the same address (e.g. display that you have these N usernames for X address). @MarvinJanssen's .BTC app needs this, and preferably we wouldn't either A) send the user back through the Stacks Wallet / other wallets to select / generate a new address sans username before proceeding to register a .BTC etc username for it, nor B) require the user to sign multiple transactions to get the job done from within the context of the app. One should be enough.
  2. Users can receive STX and other token transfers to a given address by handing out any number of usernames / aliases that map back to it. We've already discussed adding "Send to username" functionality directly into the Stacks Wallet, for example, and its usage should be quick and painless as well (i.e. it shouldn't matter if the sender submits a BNS or non-BNS registered username. It just "works" in that the underlying address gets its STX).

On the dev side, it seems prudent to make the implementation of the above UX as painless as possible for clients, whether they be apps or wallets, in that they don't have to do excessive gymnastics to handle different types of usernames. Perhaps this could be helped by Stacks Blockchain API mechanisms, or perhaps on the chain level.

I'm not sure which is the "right" technical approach here, given these considerations, though I sense that relying on newly deployed contracts for additional usernames would incur this kind of gymnastics. And while I sympathize with the desire to prevent inadvertent registrations due to race conditions, I'm not sure we've seen this in practice, and it seems that clients may have an easier time addressing those conditions instead of dealing with non-BNS username aliases?

That said, I don't quite follow why The auth app can't prevent [the race condition]. It's not in any position to prevent it without the contract's help. In a strict sense, I suppose that's true if a user tries to register one username with a given wallet then rushes to use another wallet to register another username with the same underlying key.

However, wouldn't micro blocks – plus the expectation that virtually no one's likely to change wallets immediately for this purpose – give the user's single wallet a chance to prevent unwanted double registration within a small time window?

jcnelson commented 3 years ago

Users can authenticate to any app with any address that already has an associated username, then subsequently register another username that the app and wallet providers can subsequently map back to the same address (e.g. display that you have these N usernames for X address).

If you're going to make it so the app can ask the authenticator for an ID-address to register the name on, then the authenticator can generate a fresh ID-address. This is orthogonal to the question of how many names can be held by a single ID-address in the BNS contract.

Users can receive STX and other token transfers to a given address by handing out any number of usernames / aliases that map back to it.

And, I'm saying that this mapping does not need to be handled exclusively by BNS. All you need is a "name resolver" trait that defines the API a smart contract must implement in order to resolve a (contract-specific) identifier back to your ID-address. Once you have this, then the authenticator or wallet or whatever would just use that contract's public API (exposed via the Stacks node's RESTful API) to resolve the name to the ID-address. The API call's inputs and outputs would be the same for all such contracts, since they're defined by the trait that they all implement. The authenticator can ship with a list of known contracts that implement this trait, and/or it can rely on a block explorer to give it a list of such contracts (since the explorer is already able to tell which contracts implement which traits).

In addition, a user could list in their BNS profile which contracts they consider trusted to resolve other names to this ID-address (i.e. the act of registering a new identifier for your ID-address is also the act of updating your profile to list this new identifier and the contract that can resolve it). Then, the authenticator's name lookup procedure would simply verify that the contract(s) it was able to use to reach this profile were on the user's list of trusted resolver contracts. This prevents someone from creating identifiers for you without your consent -- something we'll definitely want in order to avoid online abusers.

On the dev side, it seems prudent to make the implementation of the above UX as painless as possible for clients, whether they be apps or wallets, in that they don't have to do excessive gymnastics to handle different types of usernames. Perhaps this could be helped by Stacks Blockchain API mechanisms, or perhaps on the chain level.

Everything I described above can be automated. No user interaction would be needed beyond entering the identifier. There exists a way to resolve any name to an ID-address via any name-resolving contract, and verify that the resolving contract was trusted by the user to make that resolution.

And while I sympathize with the desire to prevent inadvertent registrations due to race conditions, I'm not sure we've seen this in practice, and it seems that clients may have an easier time addressing those conditions instead of dealing with non-BNS username aliases?

As my linked issues demonstrate: it has happened in practice, and authenticators have done nothing about it. Some of these issues are over 2 years old. Clearly, fixing this in the authenticator is low-priority. So, I'm kind of surprised this issue is getting so much attention all of the sudden, now that a fix for it is imminent and off the authenticator's plate.

However, wouldn't micro blocks – plus the expectation that virtually no one's likely to change wallets immediately for this purpose – give the user's single wallet a chance to prevent unwanted double registration within a small time window?

Microblocks do not solve the problem of racing while registering; they just make it marginally less likely. You'd want to send your preorder and register at least one Stacks block apart anyway, lest you get front-runned.

Also, microblocks do nothing to solve the problem of accidentally transferring an existing name to an ID-address that is already in use. This could arise if you tried to buy several names in rapid succession, for example.


Wrapping up here:

So unless we can convince ourselves that there exists a set of plausible, non-trivial apps that cannot be built without allowing multiple names per ID-address within BNS, I think we're justified in enforcing a one-name-per-address policy.

markmhendrickson commented 3 years ago

If you're going to make it so the app can ask the authenticator for an ID-address to register the name on, then the authenticator can generate a fresh ID-address.

I think here's the rub UX-wise.

If we don't handle multiple usernames per address via BNS, users will get into this type of situation e.g. with @MarvinJanssen's .BTC app, though he should confirm that this scenario is accurate and relevant in his view:

  1. User arrives at app landing page
  2. User initiates authentication with app, going to the wallet for generating new Secret Key and first address
  3. User submits value for first username, for association with that address
  4. User returns to app, now authenticated with that first address+username pair
  5. App proposes to user that they register a second username
  6. User accepts proposal in app, going back to the wallet for generating a second address
  7. User either submits value for second username within wallet OR goes directly back to app
  8. User is now authenticated with second address (and possibly complete address+username pair)
  9. User submits value for second username, if they haven't already in the wallet per step 7, for association with second address
  10. App can show just second username value in UI... or can it query all usernames somehow via associated contracts? 🤔

A similar scenario would play out should the user first authenticate per step 4 with a Secret Key and address+username pair they've registered at an earlier date for a different app.

In contrast, if we do handle multiple usernames per address via BNS, it seems we could cut this down to:

  1. User arrives at app landing page
  2. User initiates authentication with app, going to the wallet for generating new Secret Key and first address
  3. User submits value for first username, for association with that address
  4. User returns to app, now authenticated with that address+username pair
  5. App proposes to user that they register second username
  6. User submits value for second username via signed transaction
  7. App can show both first and second username values (e.g. "manage your usernames" UX)

Specifically here, the user doesn't have to interact with the wallet an additional time, and the app can display all N+ usernames.

I assume it wouldn't be possible or even desirable to provision and re-authenticate the user with a new address for the app on the fly via Stacks.js without having them explicitly generate a new address like this. Though of course, if that were an option, part of the above UX concern would be resolved on the surface of it.

The Blockstack Browser issues you link above appear to indicate interest in multiple usernames per address as much as they do a concern about this race condition, if not more. Concretely, I see only this comment revealing a clear way in which that authenticator has allowed this to happen inadvertently (multiple tabs open at a time, which is getting resolved implicitly by packaging the Stacks Wallet as an extension).

To be clear, I say all this while also appreciative of the effort to simplify things and avoid this race condition on a chain level, since I don't want to downplay that concern per se. I've also been mainly of the mind this year that "one username per address" is generally a favorable approach, since it's simplified our work on the Stacks Wallet to date at the very least. I probably take partial credit (blame? 😄 ) for the direction we've headed here to date given our previous convos on the matter.

However, I also have a growing sympathy for those vocalizing a desire for "multiple usernames per address" and wonder whether the safer approach is to leave more options on the table at the chain-level and navigate these UX issues at the client level, since presumably this is an immutable decision for chain design come mainnet?

MarvinJanssen commented 3 years ago

@markmhx that seems on the money. Basically having to bounce back between Wallet and the app and not being able to manage multiple names at once is not the best UX. All the proposed solutions to this limitation seem unnecessarily cumbersome. E.g.:

We aren't preventing this at all. Per my telephone number <--> ID-address example above, nothing stops you from having multiple identifiers for your ID-address. It's just that your identifiers would need to live in separate contracts. (Emphasis mine.)

It is the "just" that concerns me. Having to deploy separate contracts just to be able to manage multiple names / aliases makes me shiver. For reference, I am working on a certain EVM contract that requires each user to deploy a small proxy contract in order to be able to interact with it. When the network is congested and the gas prices go up, that proxy contract deployment can cost US$80. Very prohibitive if the contract interactions itself are supposed to be for something cheap. And disregarding deployment cost, the headaches of redeploying and managing the same contracts just does not seem worth it.

In both Apple IDs and BNS, you still have a primary account identifier key. In BNS, it's your ID-address. Internally, I'd wager that Apple IDs do something similar -- your phone number, your alternative email addresses, and so on all point to your primary account identifier in Apple's database.

Right, that is exactly my point. Would that not be very useful to have in BNS? Given that the ID-address is the main identifier as pointed out, those usernames are basically aliases to your ID-address already, why limit it to one?

The one-name-per-address rule does not stop you from doing something similar in BNS. You can add secondary identifiers to BNS ID-addresses via other smart contracts. For example, I could deploy my own smart contract that maps phone numbers to BNS ID-addresses, and I could structure it so that any registrations or updates to this map can only happen with both my permission and the ID-address owner's permission. This way, if you wanted to bind your phone number to your ID-address in my contract, you'd have to convince me that it's actually your phone number.

But doing so makes those "secondary identifiers" not show up in Wallet nor will it allow users to easily sign into other apps with them. If I had been using "marvin.id" on some chat dapp and now I want to use my fancy new "marvin.btc" without losing my chat history (Gaia storage), it is not straightforward. I therefore do not see how a custom contract of such nature adds anything.

It is worth mentioning at this point that ENS allows one address to own many domains.

jcnelson commented 3 years ago
  1. App proposes to user that they register second username
  2. User submits value for second username via signed transaction
  3. App can show both first and second username values (e.g. "manage your usernames" UX)

Thinking about this some more, it's become apparent to me that the app can't be trusted to generate the BNS name registration transactions in the first place. I wish I had seen this sooner; it could have saved a few comment posts.

The reason is because the name-preorder must take a secret hash salt that must remain secret until the name-registration is sent. If it is leaked by the app (or the app chooses an easily-guessed salt), then someone could front-run your registration by guessing the hash preimage (e.g. with a lookup table). Also, for similar reasons, it's a bad idea for the app to know what name the user is about to register -- this information could also get leaked. Maybe it's for the best that the app simply ask that the user register a name in a particular namespace?

In order to make it so apps can safely register names, the authenticator and stacks.js would need a dedicated API for allowing the app to request that a particular name be registered. The authenticator, not the app, would be trusted to generate a good salt and keep it secret. But because this pushes the responsibility of generating those transactions back to the authenticator (since only the authenticator can know the salt used to generate the transactions), the authenticator is always in a position to ensure that the ID-address that the name gets registered to has no names on it yet.

All of this is true regardless of how many names an address can have. So, the one-name-per-address design choice is not a hindrance to your second UX proposal here.


I'm going to leave you with what I said before:

So unless we can convince ourselves that there exists a set of plausible, non-trivial apps that cannot be built without allowing multiple names per ID-address within BNS, I think we're justified in enforcing a one-name-per-address policy.

Emphasis added. So far, everything we have discussed has been shown to be realizable with one-name-per-address. One-name-per-address in BNS does not prevent you from making separate identifiers, nor does it stop you from writing an app that registers names to your BNS keychain. And, as written in an earlier comment, we have several good reasons to do one-name-per-address, and I don't think it's a good use of the blockchain team's time to be re-writing significant portions of BNS this close to the release when viable alternatives exist. So, I'm going to close this issue as "not-a-bug." Please re-open only if you are able to come up with a plausible app that is truly impossible to create without it.

jcnelson commented 3 years ago

@MarvinJanssen Apologies, I replied to @markmhx and closed this issue before your comment showed up for me.

It is the "just" that concerns me. Having to deploy separate contracts just to be able to manage multiple names / aliases makes me shiver.

There would be one contract per type of identifier. In my phone number example above, there would be only one phone number <--> ID-address contract. The user only pays to run a contract-call in this contract to add their phone number <--> ID-address mapping to the contract's data map. Users aren't deploying their own contracts here; they're instead calling into existing contracts to register different secondary identifiers. Cost-wise, this isn't any different than BNS, where you'd be paying for each name you register.

Right, that is exactly my point. Would that not be very useful to have in BNS? Given that the ID-address is the main identifier as pointed out, those usernames are basically aliases to your ID-address already, why limit it to one?

It's not useful to BNS because it creates (has created) other problems. But, by using one contract per type of identifier, we can make it so an ID-address can have as many identifiers as the user wants but without re-introducing the problems.

But doing so makes those "secondary identifiers" not show up in Wallet nor will it allow users to easily sign into other apps with them. If I had been using "marvin.id" on some chat dapp and now I want to use my fancy new "marvin.btc" without losing my chat history (Gaia storage), it is not straightforward. I therefore do not see how a custom contract of such nature adds anything.

As described in an earlier comment, it is imminently possible for the Wallet to keep track of which secondary identifier contracts exist, and display their secondary identifiers to the user as part of the sign-in. As long as the Wallet can use the secondary identifier to find the ID-address and zone file, it can generate the authentication payload required to sign the user in.

It is worth mentioning at this point that ENS allows one address to own many domains.

Making it so that your ID-address can have secondary identifiers that are registered and resolved by different contracts is very similar to what ENS provides with EIP-137. If anything, this change makes BNS more similar to ENS than it is today.

vsbtcus commented 3 years ago

Had developed a BNS market (price.btc.us, redirect to https://pricedotbtcus.bitbucket.io/)

blockstack-devops commented 3 weeks ago

This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.