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

R4R: Bech32 Empty Addresses #2601

Merged
merged 7 commits into from
Feb 8, 2019
Merged

R4R: Bech32 Empty Addresses #2601

merged 7 commits into from
Feb 8, 2019

Conversation

sunnya97
Copy link
Member

@sunnya97 sunnya97 commented Oct 26, 2018

closes #2580

  • Targeted PR against correct branch (see CONTRIBUTING.md)

  • Linked to github-issue with discussion and accepted design OR link to spec that describes this work.

  • Wrote tests

  • Updated relevant documentation (docs/)

  • Added entries in PENDING.md with issue #

  • rereviewed Files changed in the github PR explorer


For Admin Use:

  • Added appropriate labels to PR (ex. wip, ready-for-review, docs)
  • Reviewers Assigned
  • Squashed all commits, uses message "Merge pull request #XYZ: [title]" (coding standards)

@codecov
Copy link

codecov bot commented Oct 26, 2018

Codecov Report

Merging #2601 into develop will increase coverage by 0.08%.
The diff coverage is 81.57%.

@@             Coverage Diff             @@
##           develop    #2601      +/-   ##
===========================================
+ Coverage     57.3%   57.39%   +0.08%     
===========================================
  Files          179      179              
  Lines        14125    14143      +18     
===========================================
+ Hits          8094     8117      +23     
+ Misses        5546     5531      -15     
- Partials       485      495      +10

@sunnya97 sunnya97 changed the title WIP: Bech32 Empty Addresses R4R: Bech32 Empty Addresses Oct 27, 2018
Copy link
Contributor

@cwgoes cwgoes left a comment

Choose a reason for hiding this comment

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

utACK, 🌱

Copy link
Contributor

@cwgoes cwgoes left a comment

Choose a reason for hiding this comment

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

One concern. If we want to keep it this way we have to ensure these cases are handled elsewhere.

@@ -55,6 +56,10 @@ func AccAddressFromHex(address string) (addr AccAddress, err error) {

// AccAddressFromBech32 creates an AccAddress from a Bech32 string.
func AccAddressFromBech32(address string) (addr AccAddress, err error) {
if len(strings.TrimSpace(address)) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Are we sure we want to do this direction too (decoding empty strings as valid bech32-addresses)? That seems like it could lead to UX confusion or griefing (tricking users into sending coins to empty addresses, for example).

Copy link
Collaborator

Choose a reason for hiding this comment

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

Good point. @sunnya97 what's the new empty address in bech32 ?

Copy link
Member Author

Choose a reason for hiding this comment

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

That should be caught by the ValidateBasic of the Msgs.

Copy link
Contributor

Choose a reason for hiding this comment

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

I feel like its reasonable to make all msg types ensure that the address is tmhash.size digest in their validate basics.

Copy link
Contributor

Choose a reason for hiding this comment

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

Really, that seems kinda annoying, why not ensure it at the type level instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

Lets make a new issue for this though, its basically entirely independent of this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't quite follow - I'm concerned about this particular addition, which will accept "" (the empty string) as a valid bech32-address. Previously that wasn't the case, and any address when de-serialized in GetFromBech32 would be automatically checked for the correct length and checksum. Addresses can still be []byte, that's not my concern.

Copy link
Contributor

Choose a reason for hiding this comment

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

i think the more underlying problem is that check tx may pass a tx with invalid address. That should ideally be caught asap. Then any built tx would fail in it's check tx phase.

Copy link
Contributor

Choose a reason for hiding this comment

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

Can we not just do the length check in these function calls (TM address length)?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think we should return an error for invalid addresses here, rather than returning an invalid AccAddress{}.
For simplicity we should use a single global constant tmhash.TruncatedSize (definitely not 32 bytes, but 20).
And if the length is invalid, this function should return an error immediately.

@@ -169,6 +178,10 @@ func ValAddressFromHex(address string) (addr ValAddress, err error) {

// ValAddressFromBech32 creates a ValAddress from a Bech32 string.
func ValAddressFromBech32(address string) (addr ValAddress, err error) {
if len(strings.TrimSpace(address)) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above.

@@ -284,6 +301,10 @@ func ConsAddressFromHex(address string) (addr ConsAddress, err error) {

// ConsAddressFromBech32 creates a ConsAddress from a Bech32 string.
func ConsAddressFromBech32(address string) (addr ConsAddress, err error) {
if len(strings.TrimSpace(address)) == 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

Same as above.

ValarDragon
ValarDragon previously approved these changes Oct 30, 2018
Copy link
Contributor

@ValarDragon ValarDragon left a comment

Choose a reason for hiding this comment

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

LGTM

@fedekunze
Copy link
Collaborator

I think i'd be helpful (eg. for burning) to add sdk.Bech32EmptyAddr and sdk.Bech32EmptyConsPubKey as constants to types/address.go. Or same as solidity something like address(0)

@ValarDragon
Copy link
Contributor

I don't think that should be done here, or that burning should be done like that. We don't need total burned in state forever. Let's introduce things for burning after we've decided on how to do it.

Copy link
Contributor

@alexanderbez alexanderbez left a comment

Choose a reason for hiding this comment

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

utACK

jaekwon
jaekwon previously requested changes Nov 6, 2018
Copy link
Contributor

@jaekwon jaekwon left a comment

Choose a reason for hiding this comment

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

Needs to return error for anything that isn't 20 bytes.

@fedekunze
Copy link
Collaborator

status of this PR ?

@alexanderbez
Copy link
Contributor

Comments need to be addressed :-/

@sunnya97
Copy link
Member Author

Needs to return error for anything that isn't 20 bytes.

@jaekwon Why is that something we want enforced by the Address type? If the .Address() function of a PubKey returns something of a different size, isn't that fine?

@cwgoes
Copy link
Contributor

cwgoes commented Nov 16, 2018

@jaekwon Why is that something we want enforced by the Address type? If the .Address() function of a PubKey returns something of a different size, isn't that fine?

Because we use these functions to sanitize user input.

@sunnya97
Copy link
Member Author

Shouldn't user input should be sanitized by where the input is received? Why should the SDK itself enforce a specific Address size, maybe different apps want different Address lengths or something.

@cwgoes
Copy link
Contributor

cwgoes commented Nov 19, 2018

Shouldn't user input should be sanitized by where the input is received? Why should the SDK itself enforce a specific Address size, maybe different apps want different Address lengths or something.

Maybe different address sizes are a useful option - then the address size could be part of the application configuration, like the bech-prefixes already are. I still think we should sanitize addresses in the bech32-from-string functions, it substantially reduces the possibility of accidentally unsanitized input.

@ValarDragon
Copy link
Contributor

different address sizes are a useful option

They should totally be a thing imo. I feel kinda convinced that they should ideally be 21 or 22 bytes lol.

@alexanderbez
Copy link
Contributor

alexanderbez commented Nov 19, 2018

I don't see a reason to allow flexibility here directly. They should be a fixed size as @cwgoes and @ValarDragon suggested. If other applications need different address/account types, they should be able to define and use those modularly. No?

@ValarDragon
Copy link
Contributor

I think its reasonable to have multiple sized addresses in one chain, not sure why that would need to be restricted. For example z-addr's are 96 bytes, whilst t-addr's are 36 bytes in their bech32 forms.

@cwgoes
Copy link
Contributor

cwgoes commented Nov 20, 2018

I think its reasonable to have multiple sized addresses in one chain, not sure why that would need to be restricted. For example z-addr's are 96 bytes, whilst t-addr's are 36 bytes in their bech32 forms.

Seems a bit niche but sure - if they're different sizes, though, they should definitely have different bech32-forms and thus different bech32-conversion functions, so the bech32-conversion functions can still check the size (AFAIK that's the only debate we're having right now).

@rigelrozanski
Copy link
Contributor

have Jae's comments been addressed? This PR looks close

@ValarDragon ValarDragon dismissed their stale review November 29, 2018 20:31

I haven't kept up with this PR

@cwgoes
Copy link
Contributor

cwgoes commented Dec 4, 2018

Have Jae's comments been addressed? This PR looks close.

Looks like not yet.

@jaekwon
Copy link
Contributor

jaekwon commented Dec 5, 2018

Can we make MarshalJSON and UnmarshalJSON allow for nil & empty addresses, AND make the functions AccAddressFromBech32 etc expect a non-nil/empty address? I think that would be the best of both worlds.

I want structures to have nil/empty addresses sometimes. MarshalJSON/UnmarshalJSON should support that. Yet the function "AccAddressFromBech32" sounds like it expects a valid Bech32, and "" is not a valid Bech32.

I don't really like the idea of having .String() return a "". Even a []byte{} or []byte(nil) returns "[]" when formatted... .String() return "" can be confusing because rather than showing something descriptive like "<nil>" or whatever, it's returning nothing.

I'd prefer that .String() return "<nil>" and that we use another function like .Bech32() that panics when the address is nil/empty. The user would first check that the address is valid via .Valid() before calling .Bech32() or .String().

Validate methods would have to manually check whether an address is .Valid() or not, but that's OK... it should, because address can be nil in some contexts, and not in others.

@fedekunze
Copy link
Collaborator

fedekunze commented Dec 11, 2018

I agree with @jaekwon's comments. I still think an empty address .String() should be set to something like sdk.EmptyAddr = cosmos10000000000000000...

@cwgoes
Copy link
Contributor

cwgoes commented Dec 12, 2018

@sunnya97 Did we come to consensus on what to do here?

@sunnya97
Copy link
Member Author

sunnya97 commented Dec 17, 2018

@cwgoes I'm not sure...

@jaekwon I don't think I understood your post.

I don't really like the idea of having .String() return a "".

Next paragraph:

I'd prefer that .String() return "" ...

uhhhhh 😅😅 😅 😅 😅


I still think an empty address .String() should be set to something like sdk.EmptyAddr = cosmos10000000000000000...

We want the string/bech32 of an empty/nil sdk.AccAddress to be empty string, because then in JSONs, URL query params, etc, you can drop the param. Let's say to get all the redelegations, instead of having to do GET /stake/redelegations?delegator=cosmos10000000000000000&from_validator=cosmos10000000000000000&to_validator=cosmos10000000000000000, it can just be GET /stake/redelegations. Because omission == empty string.

Also, keep in mind, it wouldn't be cosmos10000000000000000000 as that wouldn't be valid bech32. It would actually have to be cosmos100000000000000000002sdj93 (or something, didn't actually check what actual bech32 checksum of the 0 address is 😛). Keep in mind, this whole PR started because the current string of the empty address is cosmos1550dq7, which we thought was ugly/confusing/took up space when it could be empty.

@fedekunze
Copy link
Collaborator

This PR has 3 mo already... any updates?

@sunnya97
Copy link
Member Author

@jaekwon

@jaekwon
Copy link
Contributor

jaekwon commented Jan 17, 2019

Oops it was formatting. Fixed to say "<nil>". I think that's preferable to a special nil bech32 address which can still be confusing... e.g. would users be forced to type/paste "cosmos100000000000000000002sdj93" when they mean to say that no address exists? Or that "cosmos100000000000000000002sdj93" be displayed to represent that no address exists? That doesn't seem good. I'd rather we only represent nil addresses by their absence in struct fields, printable/debuggable via .String() returning "<nil>" but not going the other way around, e.g. "<nil>" shouldn't parse to a nil address.

@cwgoes
Copy link
Contributor

cwgoes commented Feb 5, 2019

Bump @sunnya97.

@@ -203,6 +207,10 @@ func ValAddressFromBech32(address string) (addr ValAddress, err error) {
return nil, err
}

if len(bz) != AddrLen {
return nil, errors.New("Incorrect address length")
Copy link
Member

Choose a reason for hiding this comment

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

can this common error live somewhere?

Copy link
Contributor

@cwgoes cwgoes left a comment

Choose a reason for hiding this comment

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

Changes look fine, but we should update all the == nil checks to instead call Empty(), and see if we need any additional Empty() checks where addresses should not be empty.

@cwgoes cwgoes added this to the v0.31.0 (Launch RC) milestone Feb 8, 2019
@cwgoes cwgoes dismissed jaekwon’s stale review February 8, 2019 22:28

Comments addressed, I think - we decided not to go with ""

@dsgnsupporter
Copy link

cosmos-696x449

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.

Better Bech32 encoding of an empty address
9 participants