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

Rationale for introducing token-aware CashAddress type bits #32

Closed
A60AB5450353F40E opened this issue Jul 15, 2022 · 13 comments · Fixed by #58
Closed

Rationale for introducing token-aware CashAddress type bits #32

A60AB5450353F40E opened this issue Jul 15, 2022 · 13 comments · Fixed by #58

Comments

@A60AB5450353F40E
Copy link
Contributor

Suggested rationale

Wallet Address Type

Because we require transactions involving categorized outputs to be signed using modified signing serialization, funds unintentionally sent to non-upgraded wallets can't be accidentally lost and will be recoverable by importing the recipient's wallet into upgraded software.

The existing CashAddress standard supports extending the address through the use of a type bit, which could be used to indicate wallet support for receiving tokens:

Encoding the size of the hash in the version field ensure that it is possible to check that the length of the address is correct.

Type bits Meaning Version byte value
0 P2KH 0
1 P2SH 8

Further types will be added as new features are added.

However we expect that, over time, most wallets will support receiving and sending tokens.
Introducing two more address types just to signal readiness to receive tokens would only have a temporary benefit, while the cost of having to maintain two more address encodings would be permanent.

Therefore, we do NOT recommend a new address type.

Risks - Wrong Wallet Implementation

If a wallet would obtain a categorized output but be unable to spend it, then it could cause it to temporarily become unusable until the issue is resolved.

This could happen in two ways:

  1. Non-upgraded and badly implemented wallet registering a categorized output as one of its own, which is unlikely and has been discussed;
  2. Upgraded wallet having a bug in transaction building, which can be expected since bugs happen, and not all wallets pass through the same quality assurance processes.

Burning funds by non-upgraded wallets will not be possible since they wouldn't be able to produce a valid signature, and it would at worst result in temporary loss of service - users unable to send their funds until the issue is resolved.
The same would hold for upgraded wallets that are upgraded but have a bug that would prevent them from producing valid transactions.

The riskiest class of bugs is that of an upgraded wallet able to produce signatures but failing to properly account amounts to create categorized "change" outputs that would capture amounts not intended to be spent.
This risk was already present with pure BCH wallet implementations, where the result would be a donation to miners in form of fees.
With this proposal, the risk applies the same to token amounts - but instead of being donated they'd be burned.

This is a risk external to protocol development, and the only mitigation available is to write good documentation and suggest using wallet software that has good quality-assurance processes.
Since most popular wallets have proper processes in place and upgrades are reviewed and tested by multiple developers, we consider this risk to be low.

@bitjson
Copy link
Member

bitjson commented Jul 18, 2022

The rationale doesn't address losses incurred by users sending tokens to existing addresses that are not monitored for tokens; this was a huge issue for users at the BCH-BTC split, where users attempted to send BCH to "BTC addresses".

If the receiving wallet/service wasn't capable of monitoring/crediting the user/re-sending the BCH, the BCH was locked for an indefinite period. Some services shut down before giving those users any received BCH, and some clarified their terms to designate any received BCH as forfeited. Likewise, some wallet software was never updated to support sending the received BCH, forcing users to manually export keys, hire recovery services, or abandon their funds.

The CashTokens upgrade is designed to ensure wallets and services are not required to upgrade to remain on the BCH network. If users of token supporting wallets attempt to send tokens to non-upgraded software/services, the tokens will be frozen until/unless the receiver is upgraded. In some cases, existing key handling strategies will prevent new signing strategies (like token signing serialization) from being used with existing private keys; to accept tokens, these systems will need to use new private keys, and any tokens sent to older private keys might be permanently inaccessible.

The primary purpose of designating CashToken-specific CashAddress types is to prevent users from unknowingly sending tokens to addresses where the tokens will be inaccessible to the intended recipient.

Introducing two more address types just to signal readiness to receive tokens would only have a temporary benefit, while the cost of having to maintain two more address encodings would be permanent.

By design, it will forever be possible to create new token-unaware wallets, e.g. if the wallet creator doesn't expect tokens to ever be useful for the wallet's use case (token support can add non-trivial maintenance cost). It will always be important for these wallet to continue indicating their non-support for tokens – i.e. type 0 or 1 addresses.

All CashAddress-compatible software has already paid the cost of maintaining up to 16 types (with a bit reserved for future extensibility), and test vectors have included the proposed types since the format came into use (2018). The real cost here is simply that we are spending 2 more of these available types. The cost is not trivial, but this rationale seems to overstate it.

The CashTokens CHIP would be the first upgrade to reserve new type bits in the CashAddress format since its inception. I'm actually struggling to imagine a more appropriate case to reserve new CashAddress type bits – if not for tokens, when?

categorized output

Nit: do you mean "outputs with tokens"? I think describing an output as "categorized" is terminology from OP_CHECKCOLORVERIFY? It seems confusing/incorrect to apply it here – tokens are independent entities that can be placed in outputs, but they don't "color"/"categorize" the satoshis in the output.

@bitjson bitjson changed the title Recommend NOT introducing new addresses Rationale for introducing token-aware CashAddress type bits Jul 18, 2022
@A60AB5450353F40E
Copy link
Contributor Author

A60AB5450353F40E commented Jul 18, 2022

In some cases, existing key handling strategies will prevent new signing strategies (like token signing serialization) from being used with existing private keys; to accept tokens, these systems will need to use new private keys, and any tokens sent to older private keys might be permanently inaccessible.

How could a private key be prevented from making a certain kind of signature? It can sign any "message" (signature preimage) - it's just that software would need to know how to construct the right message, and for that you could import the private key into upgraded software. Or do you mean some HW wallet which keeps the key in a secure element so key export would not be possible and the hardware that has the key would only knows how to do "old" signature serialization and not allow signing arbitrary message digests - and it could take a long while, if ever, before firmware is upgraded. Yeah, HW wallets are another thing to add to the rationale FOR addresses.

By design, it will forever be possible to create new token-unaware wallets, e.g. if the wallet creator doesn't expect tokens to ever be useful for the wallet's use case (token support can add non-trivial maintenance cost). It will always be important for these wallet to continue indicating their non-support for tokens – i.e. type 0 or 1 addresses.

Great counter-point, defeats the main point against new address format - that of it being a temporary benefit.

All CashAddress-compatible software has already paid the cost of maintaining up to 16 types (with a bit reserved for future extensibility), and test vectors have included the proposed types since the format came into use (2018). The real cost here is simply that we are spending 2 more of these available types. The cost is not trivial, but this rationale seems to overstate it.

I had some preliminary feedback from Calin where as maintainer of Electron Cash he'd prefer not to introduce a new format... but now that we have a stronger rationale, let's ask again. :)

Nit: do you mean "outputs with tokens"?

Yeah, output with tokens. My use of "categorized" was a reference to the whole UTXO as a categorized object, not its satoshi payload. Membership in the category serves as proof of UTXO origin, regardless of the payload (sats, script, ft amount, nft state). Sure, it's good to harmonize the language we use.

Anyway, above is the best I could come up with AGAINST new address type bit, and with your response I feel that rationale FOR is stronger.

@bitjson
Copy link
Member

bitjson commented Jul 18, 2022

How could a private key be prevented from making a certain kind of signature?

If the private key was loaded into some sort of trusted, "Hardware Security Module", the hardware may know how to sign existing transaction types, but the signing code might not be updatable without wiping existing private keys. (Or destroying the module to perform an expensive/risky key extraction in a lab.)

For the record, I strongly prefer multisig over these trusted hardware-centric approaches. Multisig often offers better security diversification and less risk of key loss.

@imaginaryusername
Copy link
Contributor

imaginaryusername commented Jul 18, 2022

Note:

  1. Some wallets are expected to practically never support tokens, and that is okay. The notion that they "will" does not stand, and incurs confusion that will immediately harm BCH for an uncertain "long term" benefit. Please DO introduce the new type bits (but continue to use cashaddr - nobody likes writing new libraries).

  2. While we're introducing new bits, note that this is also a golden opportunity to replace the unwieldy bitcoincash: prefix with bch: . Such use will signal (in my opinion, rightfully) a very visible beginning of a "new era" in BCH brought upon by the accumulation of scripting work since covenants were first enabled as a side benefit of CHECKDATASIG.

  3. A different type bit/prefix can also be cross-used to "deprecate" P2SH20 - if you see bch: , you're on P2SH32, no need to check length. This is imo valuable for signaling the enhanced security brought by P2SH32, and relevant for some Cashtoken usecases.

@A60AB5450353F40E
Copy link
Contributor Author

if you see bch: , you're on P2SH32, no need to check length

You may still want to have 20b P2SH addresses, they too would benefit from bch: but I guess it would conveniently signal that the wallet has been upgraded to an updated standard (even if it chooses to never generate p2sh32 or token addresses), it's like we get a "free" version bump with bch:

@imaginaryusername
Copy link
Contributor

imaginaryusername commented Jul 18, 2022

Yeah, the idea is that bch: signals "upgraded" wallets, and if you upgraded for one standard you might as well do the whole package.

If it doesn't include P2SH20, specialized contract or high-security wallets can choose to only use bch: to ensure security. Having both mix in the same prefix yet only supporting P2SH32 may result in user confusion, a la

"omg dumb wallet i thought i generated bitcoincash: address, why won't binance send to this address??"

lengthy explanation about P2SH32, intricacies about version bits and birthday attacks ensues

vs

"hey why does this only generates this newfangled bch: address? Can't load that to binance"

"it's the new upgraded address, switch to old mode/the other wallet if you don't like it, this wallet only deals new"

"how can i tell???"

"bitcoincash = old, bch = new, old compatible with old, new compatible with new"

"gotcha thanks"

the latter is clearly preferable for support staff around the ecosystem. Before anyone sound the alarm bell, this does not mean we expect most wallets to make a clean break from bitcoincash:, this is only relevant for services where legacy usecases are of no relevance and enforcement of heightened security is desired, developers should make their own choices.

@imaginaryusername
Copy link
Contributor

imaginaryusername commented Jul 18, 2022

Also note that we likely don't need to change bchtest: prefix for testnet because developers can juggle their internal differences within the same prefix much better than consumers.

@A60AB5450353F40E
Copy link
Contributor Author

I had some preliminary feedback from Calin where as maintainer of Electron Cash he'd prefer not to introduce a new format... but now that we have a stronger rationale, let's ask again. :)

asked and answered, copying from Slack:

Hmm right. You did capture my fear/frustration with a new address type: namely if in some distant or near future ALL wallets support CashTokens, we wouldn't need the type bit and supporting yet another address format is just burden.

However it's also possible that for the foreseeable future there will be a “split” in support. Some wallets or hw wallets just will never support CashTokens — CashTokens is opt in.

In that case having two groups of addresses — CT and non CT is a good idea.

Yeah idk it's all guesswork right?

I do def buy Jason's argument tho — we did see btc being sent to bch and vice Versa and this happened a bit less when we introduced cashaddr

Yeah makes sense.

@bitjson
Copy link
Member

bitjson commented Jul 26, 2022

Another note for rationale: types 2 and 3 happen to be particularly appropriate because they set bit 2 for the existing formats:

  • p2pkh: type bits: 0b0000 (0)
  • p2sh: type bits: 0b0001 (1)
  • p2pkh+tokens: type bits: 0b0010 (2)
  • p2sh+tokens: type bits: 0b0011 (3)

Future upgrades will have the option of either continuing this scheme (by creating 2 new versions, one with token support and one without) or simply assuming all future address formats require token support.

So e.g. if we add a p2pk address (maybe with a MAST/Taproot upgrade), it could be at 0b0100 for "no tokens" and 0b0110 for "with tokens", but choosing 2/3 now also doesn't completely lock us into considering that bit to be the "token bit".

Alternatively, we could use bit 4, so the types would be 8/9 respectively. If we expect all future address types will have both token/no-token modes, we'd end up with a cleaner scheme by not splitting the space with a token bit. I think there's a reasonable chance we'll also end up with a similar bit to represent "supports fraction satoshis", and in that case we'd almost certainly want these boolean flags to start on the other side of the range...

Actually, after the earlier discussion around always retaining support for "no token" addresses, I'm now leaning toward using the highest type bit:

  • p2pkh: type bits: 0b0000 (0)
  • p2sh: type bits: 0b0001 (1)
  • p2pkh+tokens: type bits: 0b1000 (8)
  • p2sh+tokens: type bits: 0b1001 (9)

Anyone have other arguments for/against?

@A60AB5450353F40E
Copy link
Contributor Author

Anyone have other arguments for/against?

If we go for the bit, I'm fine with either. Btw, if we need more bits I think we could hack it: use the last bit as extension bit, one that would tell the wallet to interpret X bits of payload as extended data e.g. you'd be encoding "extended" 20b P2SH using the 192-bit encoding, but only 160bits would be the hash, while 32 would be the extended data.

(maybe with a MAST/Taproot upgrade)

side-note: we kinda got that already with introspection hehe, we can emulate OP_EVAL using dust utxo "sidecar" inputs which bring in the code to be executed, so I think we can do MAST, and I'm not sure for Taproot (meaning hiding even the existence of an alternate spending path?)

@cculianu
Copy link
Contributor

cculianu commented Jul 31, 2022

Oh I missed this discussion. Negative. Please just populate the type enum from the bottom-up, as you had it before. This is the standard way to populate such values. E.g. just do:

p2pkh: type bits: 0b0000 (0)
p2sh: type bits: 0b0001 (1)
p2pkh+tokens: type bits: 0b0010 (2)
p2sh+tokens: type bits: 0b0011 (3)

We lack a crystal ball and we lack the ability to predict the future. Allocating in the middle of a block of numbers like this is just annoying. Stop trying to be too clever. There is value in knowing what came first, what came after, etc, historically, from just examining the enum's values as they increase.

Please revert the spec back to what you had before.

@imaginaryusername
Copy link
Contributor

Agreed, there's no particular reason to leave the middle empty except for some possible aesthetics gain in the future , at the immediate detriment of current implementations. Please revert to 2/3.

@bitjson
Copy link
Member

bitjson commented Aug 1, 2022

Ah, good points, thanks!

Agreed, both options allow us to identify a single token bit, and the aesthetic benefit of 8/9 is only realized if the "token bit" continues to be optional for future types. If not, we've lost the aesthetic benefit of contiguous and chronological ordering with 2/3.

So the future value is ambiguous, but there's a non-zero current cost in creating a gap: it prevents the types from being specified as a simple array, and it's slightly more surprising for implementers (requires explanation).

Alright, I'll revert that and fix the test vectors now. Thanks everyone for the comments!

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 a pull request may close this issue.

4 participants