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

support multisig bech32 address decoding #6237

Merged
merged 10 commits into from
Jul 23, 2020
Merged

Conversation

assafmo
Copy link
Contributor

@assafmo assafmo commented May 17, 2020

Description

Multisig addresses are 218 characters long (for 6 character length prefix like cosmos), but the bech32 specs limit the address length to 90 characters by default, and the library you're using to decode bech32 doesn't allow to pass the limit as an arg.

This only affects reading bech32 address, so for example on a chain with a multisig address, if you do gaiad export --for-zero-height > x.json and then gaiad validate-genesis x.json you'll get ERROR: error validating genesis file x.json: failed to unmarshal auth genesis state: decoding bech32 failed: invalid bech32 string length 218.

To fix this I forked the bech32 library and allowed for passing the character limit as an input, then passed 250 as the limit which is what needed for decoding multisig addresses and then some. I used 250 instead of 218 to support longer address prefixes for other chains. (In theory 1023 chars is still okay, longer than that the checksum at the end cannot guarantee error detection)

This PR also fixes this bug: #6054

Refs:

@auto-comment
Copy link

auto-comment bot commented May 17, 2020

👋 Thanks for creating a PR!

Before we can merge this PR, please make sure that all the following items have been
checked off. If any of the checklist items are not applicable, please leave them but
write a little note why.


For contributor use:

  • 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.
  • Code follows the module structure standards.
  • Wrote unit and integration tests
  • Updated relevant documentation (docs/) or specification (x/<module>/spec/)
  • Added relevant godoc comments.
  • Added a relevant changelog entry to the Unreleased section in CHANGELOG.md
  • Re-reviewed Files changed in the Github PR explorer

For admin use:

  • Added appropriate labels to PR (ex. WIP, R4R, docs, etc)
  • Reviewers assigned
  • Squashed all commits, uses message "Merge pull request #XYZ: [title]" (coding standards)

Thank you for your contribution to the Cosmos-SDK! 🚀

@codecov
Copy link

codecov bot commented May 17, 2020

Codecov Report

Merging #6237 into master will decrease coverage by 1.15%.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master    #6237      +/-   ##
==========================================
- Coverage   61.42%   60.26%   -1.16%     
==========================================
  Files         509      390     -119     
  Lines       31457    25331    -6126     
==========================================
- Hits        19321    15266    -4055     
+ Misses      10637     8897    -1740     
+ Partials     1499     1168     -331     

Copy link
Contributor

@alessio alessio left a comment

Choose a reason for hiding this comment

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

ACKd

@@ -18,7 +18,7 @@ func ConvertAndEncode(hrp string, data []byte) (string, error) {

// DecodeAndConvert decodes a bech32 encoded string and converts to base64 encoded bytes.
func DecodeAndConvert(bech string) (string, []byte, error) {
hrp, data, err := bech32.Decode(bech)
hrp, data, err := bech32.Decode(bech, 250)
Copy link
Contributor

Choose a reason for hiding this comment

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

Pretty sure we have 90 for a reason. But it won't work with multisigs. I'm not familiar with any issues that could arise if we change it.

/cc @zmanian

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, 90 is from the bech32 specs and it was hard coded in the bech32 library.

In practice cosmos-sdk uses longer addresses, mainly for multisig public keys, and the 90 limit creates bugs (e.g. in Enigma people are using multisigs and thus we cannot export the state and validate it if we want to hard fork)

@zmanian
Copy link
Member

zmanian commented May 18, 2020

What's annoying about the 90 character limit is that the bech32 spec suggests warning on > 90 characters. The kernel chosen should support up to 1024 characters reasonably.

Generally what to do about to support encodings of larger values seems to be an open problem.

See the Checksum design section from https://en.bitcoin.it/wiki/BIP_0173

A lot of people were angry at my for requiring forks of bech32 across the ecosystem in one my original designs.

@alexanderbez
Copy link
Contributor

alexanderbez commented May 18, 2020

Can we accept this PR @zmanian? We'll be able to finally support JSON encoding for accounts again using Bech32. I just don't know the repercussions of choosing a larger value.

@zmanian
Copy link
Member

zmanian commented May 18, 2020

I don't think the right place for the forked > 90 character bech32 implementation should live in the engima github org.

It should live in the Cosmos org or maybe a new github org that's going to be focused on maintaining multiple implementations of the modified bech32.

This is the line that always caused me some anxiety.

Even though the chosen code performs reasonably well up to 1023 characters, other designs are preferable for lengths above 89 characters (excluding the separator).

The advantages of using the same kernel with a larger character limit is that it's a small fork of widely available software.

The downside is that an alternative kernel would detect errors better in longer strings.

How do we maximally retain the good properties of bech32 while loosening the restriction?

@assafmo
Copy link
Contributor Author

assafmo commented May 19, 2020

That's really that only change I made to the base library: scrtlabs/btcutil@7ac7d43 (+ made the tests to reflect the changed API)
I don't know if there's anything to maintain here.

@zmanian
Copy link
Member

zmanian commented May 19, 2020

I think we will probably need to have implementations in other languages with this API so we should probably have fork.

Should we also enforce that limit must be no greater than 1023?

@romeo4934
Copy link

any update on this PR?

@alexanderbez
Copy link
Contributor

Again, I don't have enough context to really give approval here. Why 250? Why not 1024?

@assafmo
Copy link
Contributor Author

assafmo commented Jun 3, 2020 via email

@alexanderbez
Copy link
Contributor

My point is the value should not be arbitrary and have reasoning behind it. And not only that, but also be flexible for future use without having to do this again. So it would make sense to select the largest value possible, no?

@romeo4934
Copy link

romeo4934 commented Jun 3, 2020

I agree. FYI, it seems this command in the v38 is broken as well when I query a multisig

iovnscli query account star1j4dx6dwgz8nfpmtecmzvqlv5p4tjahvd5q9qte
ERROR: decoding bech32 failed: invalid bech32 string length 216

@assafmo
Copy link
Contributor Author

assafmo commented Jun 3, 2020

My point is the value should not be arbitrary and have reasoning behind it. And not only that, but also be flexible for future use without having to do this again. So it would make sense to select the largest value possible, no?

I agree. But I think the checksum guarantee is up to 1023 bytes: https://zips.z.cash/zip-0173

@romeo4934
Copy link

romeo4934 commented Jun 5, 2020

Do you think you will solve this issue?
Otherwise, we will do like Enigma or Kava and add our own patch.

@assafmo
Copy link
Contributor Author

assafmo commented Jun 28, 2020

@alexanderbez @zmanian Now the number isn't arbitrary. I changed it to 1023 chars, which is the maximum length that supports the checksum property of bech32. ☺️

@alexanderbez
Copy link
Contributor

Should we port this over to the cosmos repo? What are your plans around maintaining this library?

@assafmo
Copy link
Contributor Author

assafmo commented Jun 29, 2020

Should we port this over to the cosmos repo?

I'd prefer it, yes.

What are your plans around maintaining this library?

No plans to maintain this. We have a module in our mainnet right now that makes heavy use of multisigs, so this is already live in our mainnet, but I don't expect this module to stay with us for more than a year.

Ideally for us this PR will be merged, so that we could again point our code to this repo for cosmos-sdk.

@alessio
Copy link
Contributor

alessio commented Jun 29, 2020

How about importing github.com/enigmampc/btcutil/bech32 into the sdk? Either as top level package or crypto/ subdirectory

@assafmo assafmo requested review from alexanderbez and removed request for hschoenburg June 29, 2020 20:37
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.

ACK

@onyb
Copy link

onyb commented Jul 14, 2020

This issue will most likely be addressed once btcsuite/btcutil#177 gets merged. If you have an example of a multisig bech32 address used in Cosmos, I am happy to include it in our tests.

@assafmo
Copy link
Contributor Author

assafmo commented Jul 14, 2020

If you have an example of a multisig bech32 address used in Cosmos, I am happy to include it in our tests.

Currently, this is not possible on any cosmos-based chain, except I think for the Secret Network and Kava, and that is only because we applied this patch ourselves.

Anyway, here's an example: The address cosmos1337nkls6z6z2ygngzrawmp25l8qvywkw2xchwq with pubkey cosmospub1ytql0csgqvfzd666axrjzqmn5q2ucztcyxw8hvlzen94ay05tegaerkug5pn3xn8wqdymt598ufzd666axrjzqsxllmwacap3f6xyc4x30jl8ecrcs2tze3zzgxkmthcsqxnqxhwwgfzd666axrjzqs2rlu3wz5gnslgpprszjr8r65n0d6y39q657th77eyvengtk3z0y6h2pnk

If you try to broadcast a tx with that pubkey, it will fail.

$ gaiacli keys add 1                                        
{
  "name": "1",
  "type": "local",
  "address": "cosmos1w2h5x0kmxeluhvv8mvrmdujrs9aldwmsxewmmz",
  "pubkey": "cosmospub1addwnpepqg9pl7ghp2yfc05qs3cpfpn3a2fhkazgjsd209ml0vjxve59mg38jr2yftf",
  "mnemonic": "bind there illegal digital badge taxi guess oyster toy void lunch potato vivid oak canvas pill discover describe sentence disorder hint brisk husband winter"
}
$ gaiacli keys add 2
{
  "name": "2",
  "type": "local",
  "address": "cosmos1zhldgcq2lfgwzflewq0vr0nqpm5kz7xtge2fmj",
  "pubkey": "cosmospub1addwnpepqde6q9wvp9uzr8rmk03vej67j869u5wu3mwy2qecnfnhqxjd46zn78aj2yn",
  "mnemonic": "demise inner skin twist text elephant mind below reflect fan ecology bone black verify sweet song caught jump possible fetch marble language pony tongue"
}
$ gaiacli keys add 3
{
  "name": "3",
  "type": "local",
  "address": "cosmos18ds9jlaehg09vrkk9zmjr8qacqzmj73jhaxvj9",
  "pubkey": "cosmospub1addwnpepqgr0lahwuwsc5arzv2nghe0nuupug993vc3pyrtd4mugqrfsrth8yw7mcxh",
  "mnemonic": "outside nasty siege rice pave gloom mistake hood leaf true spider traffic bubble breeze swim announce feel release garbage exercise suspect kiwi artist beef"
}
$ gaiacli keys add --multisig=1,2,3 --multisig-threshold=3 m
Key "m" saved to disk.
$ gaiacli keys list                                         
[
  {
    "name": "1",
    "type": "local",
    "address": "cosmos1w2h5x0kmxeluhvv8mvrmdujrs9aldwmsxewmmz",
    "pubkey": "cosmospub1addwnpepqg9pl7ghp2yfc05qs3cpfpn3a2fhkazgjsd209ml0vjxve59mg38jr2yftf"
  },
  {
    "name": "2",
    "type": "local",
    "address": "cosmos1zhldgcq2lfgwzflewq0vr0nqpm5kz7xtge2fmj",
    "pubkey": "cosmospub1addwnpepqde6q9wvp9uzr8rmk03vej67j869u5wu3mwy2qecnfnhqxjd46zn78aj2yn"
  },
  {
    "name": "3",
    "type": "local",
    "address": "cosmos18ds9jlaehg09vrkk9zmjr8qacqzmj73jhaxvj9",
    "pubkey": "cosmospub1addwnpepqgr0lahwuwsc5arzv2nghe0nuupug993vc3pyrtd4mugqrfsrth8yw7mcxh"
  },
  {
    "name": "m",
    "type": "multi",
    "address": "cosmos1337nkls6z6z2ygngzrawmp25l8qvywkw2xchwq",
    "pubkey": "cosmospub1ytql0csgqvfzd666axrjzqmn5q2ucztcyxw8hvlzen94ay05tegaerkug5pn3xn8wqdymt598ufzd666axrjzqsxllmwacap3f6xyc4x30jl8ecrcs2tze3zzgxkmthcsqxnqxhwwgfzd666axrjzqs2rlu3wz5gnslgpprszjr8r65n0d6y39q657th77eyvengtk3z0y6h2pnk"
  }
]                   

@romeo4934
Copy link

Is it going to be released for launchpad as well? @alexanderbez

@alexanderbez
Copy link
Contributor

Is it going to be released for launchpad as well? @alexanderbez

Yes, it has to be actually. This is why I'm doing it primarily.

@alexanderbez alexanderbez reopened this Jul 23, 2020
@alexanderbez alexanderbez added the A:automerge Automatically merge PR once all prerequisites pass. label Jul 23, 2020
@mergify mergify bot merged commit ee04dd7 into cosmos:master Jul 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A:automerge Automatically merge PR once all prerequisites pass.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants