Skip to content
This repository has been archived by the owner on Jun 3, 2018. It is now read-only.

Suggestion: defining a standard prefix and type to use for private keys? #49

Open
avl42 opened this issue Jan 17, 2018 · 14 comments
Open

Comments

@avl42
Copy link
Contributor

avl42 commented Jan 17, 2018

Maybe base32 could also be used for export and import of private keys. It can deal with 256 bit data, so why not also use it for that?

@deadalnix
Copy link
Collaborator

I think it is a great idea.

@schancel
Copy link
Contributor

I was talking to another person last night about this. We should probably add another version type for privkeys.

@avl42
Copy link
Contributor Author

avl42 commented Jan 18, 2018

I'd definitely vote for both a new prefix and a new type, to prevent any accidental use of a privkey as a payment target - visibly (by prefix) or technically (even if prefix is stripped).

"bchprivkey" and type=8 , thus the versionbyte would be '43' (bits regrouped by fields: 0 1000 011)

Of course the old base58 formats "5..." and "K..." ought to be still supported as well.

@schancel
Copy link
Contributor

Can you do a pull request for this?

@avl42
Copy link
Contributor Author

avl42 commented Feb 2, 2018

I can, I will, but not yet sure when I'll get to it. One more detail that I forgot to mention before is the aspect of "compressed" versus "uncompressed" keys. Not sure, if the distinction is still a thing nowadays, or if import of a priv-key adds both addresses to the wallet anyway... I'd appreciate some feedback on that point before I start.

@DesWurstes
Copy link

DesWurstes commented Feb 2, 2018

Summary:

Uncompressed address: Ripemd160(Sha256(0x04 + X coordinate + Y coordinate))

Compressed address, y is odd: Ripemd160(Sha256(0x03 + X coordinate))

Compressed address, y is even: Ripemd160(Sha256(0x02 + X coordinate))

+ being the array merge operator.

Structure of WIF:

1 Byte Private key version byte + 32 bytes private key (if smaller than 32, fill zeros to the left and then put the privkey)

Structure of WIF for compressed addresses:

1 Byte Private key version byte + 32 bytes private key (if smaller than 32, fill zeros to the left and then put the privkey) + 1 byte of 0x01.

@avl42
Copy link
Contributor Author

avl42 commented Feb 2, 2018 via email

@bitcoincashorg bitcoincashorg deleted a comment from Ruethairat Apr 2, 2018
@Steve132
Copy link

Steve132 commented May 19, 2018

One concern here is that while CashAddr allows arbitrarily large keys, the "address space" so to speak of how many "address types" there can be is limited by the current spec to only 16 (4 bits) of space from the version byte. 2 of those (0b0000 and 0b0001) are used for p2pkh and p2sh script addresses, respectively.

If private keys were encoded with CashAddr, then the spec could either reuse 0b0000 and 0b0001 respectively (for compressed and uncompressed), or it could pick new type bits.

If the spec reused 0b0000 and 0b0001, then validating a given address becomes somewhat difficult: If the user omitted the optional prefix, then telling the difference between a private key and a long address requires validating against both prefixes. Without doing that, you wouldn't be able to tell if a given parse failed because it was a private key or if it failed because of a checksum error, which could be bad for user experience. Also, addresses would look visually similar to addresses, which could also be bad for user experience.

If the spec chose new type bits (say 0b00100, then that's using up 7% (or 14% if we include compressed and uncompressed as separate types) of the available number of total types CashAddr can support.

Both options seem to have some significant downsides in some way or another. The first uses less type space but is more complicated for wallets to implement and can provide bad user experience from addresses looking similar. The second seems to be better but uses up a LOT of an already limited resource (CashAddr 'type' space).

My preferred solution would be this: Implement the second solution by allocating new type id 2 to compressed private keys. HOWEVER in the same update change the cashaddr spec to use the reserved first bit as a flag to extend the address space for the version byte..

This can be done in a backwards compatible way by modifying the cashaddr spec to specify that the version byte becomes a version varint. The bottom 3 bits of the version varint are still the size, but now the entire thing is parsed using the varint algorithm used in the rest of bitcoin instead of just reading a single byte.

If the version value (type and size) is any bitpattern currently supported by CashAddr, (0-127) then for types 0-15 and sizes 0-7 it will still have the same representation as it currently does.

However, with this change, using code already likely to be present in any wallet implementation, its possible to read up to 2**61 possible 'types' that will be allowed in CashAddr.

Wallets won't have pressure to implement this change to the spec until type 15 is defined, but it would give us future proofing for other address/data types to be supported when that happens. (which could be sooner than expected if type 2 and 3 are allocated for this purpose).

Even if it's decided to solve this issue by using another prefix and reusing 0-1, this future proofing would help a lot in case BCH actually wants to support a plurality of more complex address types in the future.

Steve132 added a commit to Steve132/bitcoincash.org that referenced this issue May 19, 2018
The current CashAddr specification includes a version byte that has a 4-bit field to represent the 'type' of the address.   Although 16 possible address types seems like a lot considering we currently only have two, it could become necessary to extend the CashAddr format quickly if we ever start to have more than that number.    Considering that BCH is re-enabling future opcodes, we might need more than 15 address types sooner rather than later.  In that hypothetical, it would be better to have extended it now, while there are fewer implementers, because the later we extend it, the more CashAddr implementations will exist in the ecosystem and extending it will be harder.   

Any such extension would need to also be backwards-compatible with the current behavior for all currently supported version bits, in order to not break existing code.  Furthermore, it would be better to re-use existing methods that are tested in existing node/wallet code.  This proposal means that wallets won't be pressured to upgrade existing code until we start to get close to 16 types.

This change is a pull request in the spec implementing a possible way to extend the 'type' space but achieve both goals (backwards compatibility, simplicity of implementation, doesn't break existing wallets).  It changes the version 'byte' into a version "VarInt" which incidentally parses exactly the same for existing types 0-15, while allowing an essentially infinite number of types in the future.  It also uses code that is likely to already exist and be well tested in any environment where CashAddr is implemented.  

It is referenced by bitcoincashorg/spec#49, but is not solely useful in that context.

If necessary, I can also write test-vectors and a few implementations.
@avl42
Copy link
Contributor Author

avl42 commented May 19, 2018

I don't really foresee anything like a "continuing trend" towards using type values. (imho, future would more likely get to grow a need for further size-values instead.)

So, my suggestion stays "use one new type value" - eat up a portion of 7% of the type space - and only if rate of type-allocating suggestions increases... 'then' start touching the MSB of the version byte.

It's not like we'd need to prepare that beforehand. We could still do that when the last currently available type value has been put to use and another proposal comes up.

@Steve132
Copy link

Steve132 commented May 20, 2018

I don't really foresee anything like a "continuing trend" towards using type values. (imho, future would more likely get to grow a need for further size-values instead.)

Lots of interesting new blockchain uses, including various kinds of opcodes, could eventually supercede the bounds of p2sh, especially if people start signaling what kind of script or contract the address represents, or use CashAddr to represent other primitives (like is being discussed here).

In contrast, BCH is pretty much hard-coded to use 128-bit strong security everywhere, for which the current size support for 512 bits of hash is more than enough, even against birthday attacks. So I see it the opposite way, basically: More types is more likely than larger hash sizes.

It's not like we'd need to prepare that beforehand. We could still do that when the last currently available type value has been put to use and another proposal comes up.

You want to make those kinds of revisions as early and as generically as possible. As adoption of CashAddr grows, upgrades to all of those implementations in the ecosystem becomes exponentially harder over time. It's far better to make the change as soon as possible in the spec and the reference implementations so that we can get ahead of it before the ecosystem grows and copy-pasted implementations spread all over. If we wait to define the change until as late as possible, then there will be more wallets and services who need to implement the change in a very short time than there are if we implement the change now.

It's very easy for a service to have code that says "Parse the extended type bits. If the type bits aren't what is expected here as a valid address, then report that to the user or handle it some how" if the extension spec is well defined, then that code will continue to work with new extended addresses whenever new types are added. If the code doesn't need to validate for a particular type (suppose it's an address book of valid cashaddr formats or a BCH block explorer), then even when new types are introduced that code will continue to work.

In contrast, if we wait on the extension spec, then when a user in the future enters an extended address into one of those services that didn't implement the extension, the address will fail to parse completely because of an invalid payload. Well programmed services will point out to the user that the MSB is invalidly set with no other information regarding address type, (even ones that are otherwise agnostic to type), and careless implementations will inaccurately report a checksum error in the payload.

My point is that it's important for future user experience and maintenance throughout the ecosystem that, if we're going to extend it ever, that we extend it sooner, and preferably in a backwards-compatible way.

I have a proposal over here bitcoincashorg/bitcoincash.org#19 to do that.

@avl42
Copy link
Contributor Author

avl42 commented May 22, 2018

  • Unlike e.g. block-sizes, any extra need of types is something that emerges from new features and/or opcodes added. It's a self-made need, and as we create the need we can then also add the remedy.

  • What you achieve with the "extension spec", namely safe detection of later extensions, is exactly what the "MSB is 0" is already for. If we ever run out of type codes, we can then decide on the new spec (and add another new marker to that new spec, for even later extendability). If we're too eager with conquering the MSB=1 space now, it might turn out that future uses (beyond the one-dimensional type-range) will be confined to really awkward version-prefixes.

PS: I think your proposal is fine in the case that we really end up needing gobs of type values. It's just pre-mature for the actual need. It's easily possible that future extension needs will develop into a completely different direction than just adding more type values.

@Steve132
Copy link

Unlike e.g. block-sizes, any extra need of types is something that emerges from new features and/or opcodes added. It's a self-made need, and as we create the need we can then also add the remedy.

I agree you can in a vacuum where there's only one implementation and one node software anyone uses, but I'm saying you can't, because by then the amount of code implementing cashaddr will have spread. There are frontends and backends and wallets and db integrations and web code and lots of different wallets and square anyone who accepts bch will have to upgrade at that time. Getting it all to be correctly extended will be a huge hassle. However, if we spec the msb behavior now before adoption explodes then that maintanence effort will be less.

You aren't thinking about the wider ecosystem. Yes, it's possible to spec the MSB only whenever we need it, but that's shortsighted and will hurt adoption the longer we wait.

What you achieve with the "extension spec", namely safe detection of later extensions, is exactly what the "MSB is 0" is already for.

I know that. However, since it doesn't specify how to handle it nobody can currently implement it. This leads to the problem we will encounter above whenever we eventually decide to do it.

If we're too eager with conquering the MSB=1 space now, it might turn out that future uses (beyond the one-dimensional type-range) will be confined to really awkward version-prefixes.

If we have 64 bits of space I don't think that will be a problem.

I think your proposal is fine in the case that we really end up needing gobs of type values. It's just pre-mature for the actual need. It's easily possible that future extension needs will develop into a completely different direction than just adding more type values.

And those can be added in addition later..... Say for a version varint of 0x5 means something else entirely to be parsed differently...whatever.

My point is that we need some kind of MSB handling that we intend to later implement now in order to make it so that adoption doesn't suffer in the future. AND that Handling msb=1 using a tried and true algorithm that is already well tested and guaranteed to be present already in any wallet codebase is a good idea imho.

@avl42
Copy link
Contributor Author

avl42 commented May 23, 2018

Anyway, lets leave this discussion about general extension of wallet format to your PR.

Whatever is the outcome of your PR, this "issue" here about base32'izing private keys would fit as easily into current state (eating one or 2 of the 14 still available types), and into that new "ecosystem" you're dreaming of, as well as into a decent extension whatsoever.

@Steve132
Copy link

Whatever is the outcome of your PR, this "issue" here about base32'izing private keys would fit as easily into current state (eating one or 2 of the 14 still available types), and into that new "ecosystem" you're dreaming of, as well as into a decent extension whatsoever.

I agree with that.

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

No branches or pull requests

5 participants