Making a comment here as placeholder that we need to address activation strategy in the CHIP.
While nonstandard, it is possible that transactions generated before activation will contain āvalid-lookingā token outputs, and since the ruleset doesnāt exist before activation, they can be āwrongā in a wide variety of ways - including but not limited to duplicious Category IDs, invalid-when-summed amounts, nonsense NFT capabilities, nonexistent genesis, and so on.
Declaring these pre-activation outputs as invalid might be simple as a thought experiment, but incurs technical debt in practice - weāll need a separate pass checking UTXO height to determine the validity of all token transactions. Not idealā¦
⦠but these should not be a big deal in practice even if we adopt the ruleset as is! These āfakeā UTXOs can simply be declared āvalid if exist at activationā. These may lead to shenanigans involving any categories that use txids that exist pre-activation, but there is a clean way around it: implementers (of wallets, services, smart contract providers etc.) mostly need to be aware to do a de novo two-step genesis for any tokens they generate, shenanigans are only applicable to actual users if they venture into directly using pre-activation UTXOs for genesis.
Does this mean we donāt actually need to do much? Yes, but we do also need to address this point in the specs, lest people get confused about what the best practices are.
Just to elaborate: there are a bunch of corner cases here, all somewhat related to the way this works.
What do you do if you saved a scriptPubKey to your UTXO db as a node some time ago, and it has the prefix-byte. Now some new tx, post-activation, wishes to spend that UTXO. So you deserialize the coin and lo and behold it looks it has the PREFIX_BYTE.
What do you do if the SPK passes muster and deserializes correctly as a [tokenData + SPK] byte blob? (Has ok capabilities, positive amount, short-enough commitment, etc). Now there is a āfakeā token that can be spent⦠which is what @im_uname is discussing aboveā¦
This has implications for token amounts. Itās possible for a category-id to exceed INT64_MAX if someone makes a bogus token from pre-activation that has the same category-id as a real token from post-activation⦠Now your inputs can sum up to >INT64_MAX. This is a caveat for node implementors to worry aboutā¦
The other case is what happens if the TXO fails to deserialize because while it used to just be an opaque byte blob that containes SPK bytes, now there are new rules about SPKās with the prefix having to follow the new token binary format (commitment length, etc). So maybe the TXO doesnāt deserialize correctly as a token⦠but you thought it was a token!
Do you deserialize it anyway and just throw all the bytes (including the PREFIX) into scriptPubKey (as it was when it was created, really)⦠? (Note this would be an unspendable TXO, but still the behavior needs to be specified).
I say this only as a corner case because one can imagine some node software assuming āillegalā PREFIX_BYTE containing SPKās are impossible if they come from the internal node DB, and if that assumption doesnāt hold one can imagine software crashing when it hits the impossible condition it thought was impossibleā¦
There are also other caveats with respect to activation⦠that are subtle ⦠which I can go into later.
Great points, thanks for bringing it up @im_uname and @cculianu! That definitely needs to be addressed in the spec.
One useful observation: any locking bytecode with a PREFIX_TOKEN (0xd0) is currently provably unspendable. Thatās not true for all occurrences of 0xd0 in any locking bytecode, but because 0xd0 is an unknown opcode, and an OP_IF/OP_ENDIF block canāt cross from unlocking to locking bytecode (also ā push-only enforcement since Nov 2019), we know that 0xd0 can not be the first byte in any successful locking bytecode evaluation (and this has been the case since Satoshi).
So until the upgrade, all outputs with a 0xd0 prefix are practically equivalent to OP_RETURN outputs, and can reasonably be pruned from the UTXO set at upgrade time. (In fact, implementations could reasonably prune lots of other provably unspendable UTXOs from their databases, but in most other cases that probably wouldnāt be worth the effort, makes UTXO commitments harder to standardize, etc.)
After that, there shouldnāt be any need to keep track of āfake tokenā outputs. While it still requires some activation logic, at least node implementations donāt have to pay a cost after the upgrade block.
One caveat with this strategy (and if we go with it, should be in the spec) any token transactions prepared in advance of the upgrade should use locktime to ensure the transaction isnāt included in a pre-upgrade block. (Even if the transaction is broadcasted after the new rules are expected to be in effect, itās still possible for a backdated re-org to burn those funds.) Of course, creating tokens doesnāt require significant funds, so many users might not care if their token-creating dust gets burned by a malicious re-org (especially people creating tokens in the first few blocks, many will just be upgrade lock-in transactions). But worth mentioning for completeness.
Related, I think this is also the right strategy for handling āimproperly encoded token prefixesā after activation. If PREFIX_TOKEN is followed by an invalid token encoding, we canāt really assume the locking bytecode was intending to encode tokens (and, e.g., attempt to somehow slice off the invalid token prefix to allow the BCH dust to be spent). Instead, itās sending dust to a provably unspendable output, and can just be treated like any OP_RETURN output. (The transaction would be non-standard anyway due to the unrecognized contract type, so in practice this would only happen if a miner deliberately mined the nonstandard transaction.)
You canāt ājust pruneā things from UTXO following a certain rule thatās later slated to be spendable again - the āpruning because unspendableā needs an activation in itself, else you get a consensus failure.
@bitjson Thanks for replying. Yes, they are unspendable, and yes we already do nuke other unspendables (at least in BCHN) from the DB ā namely the following two rules exist at least in the BCHN codebase:
anything beginning with OP_RETURN is just pruned and ignored completely as if it doesnāt exist.
any script that happened to end up in the DB and be over 10kb is treated similarly
Since they are unspendable now, and while I do concur in principle with @im_uname that pruning TXOs is a bad look, I could be convinced to prune them.
Still this means the node now has to keep track of activation height for this upgrade, so as to disallow TXOs with the PREFIX_BYTE created before the upgrade from wrecking havoc. Which sounds easy right? But⦠nodes (at least BCHN) doesnāt normally operate upgrades based on height (with some rare exceptions from the Satoshi days). It actually uses āMTPā ⦠and using MTP it can only answer the question: āIs this upgrade active nowā? It cannot (easily) answer the question: āWas the upgrade active at height X?ā. It just doesnāt think of the blockchain in those terms⦠So it would require some coding to get right in BCHN at least. Meh.
At any rate ā I think we can all agree on these points:
Creation of new TXOs that have the PREFIX_BYTE but that donāt deserialize correctly as token data should be disallowed going forward post-activation. I think the V2 spec says this subtly in 1 small paragraph but perhaps this should be emphasized? We should make this a consensus rule post-activation. In fact, in my implementation I already coded it as such. This just saves us some headaches to have that as a rule⦠IMHO. (Of course we could not have that as a rule but itās just cleaner to have it if you ask me⦠and anyway the spec already declares this, soā¦)
We still have to specify how to handle legacy TXOs (if any??) that happen to have PFX-BYTE (whether they deserialize as valid tokens or not). My preference is to āallowā them at the present time⦠but I donāt have a strong preference here. We can also just disallow them and do a height check when spending them (we already store TXO confirm height in the DB anyway because reasons). But⦠like I said earlier⦠asking the question āWas upgrade X active at height H?ā is non-trivial to answer in BCHN codebase at least so it would be a headacheā¦!
In the case of TXOs with PFX-BYTE but that donāt really encode a token, all of this is an implementation detail at the end of the day really⦠I guessā¦
In the case of a hypothetical pre-upgrade-generated TXO with PFX-BYTE but that does ālookā like a token ā we definitely need to decide now what the spec should be for that! (whether it be YOLO allow and not care or harder-to-implement forbid⦠either work).
I have a bunch of other notes and caveats and landmines I noticed while implementing that I will summarize later for other implementations to take heed⦠so as to avoid subtle bugs, etc⦠but none of them are super critical just āstuff to look out forāā¦
the problem here is it doesnāt matter if you or BCHN or all node teams can be āconvinced to prune themā, you need a coordinated activation in order for that to be clean - and said activation, unlike consensus rules, also isnāt enforceable - one will have to devise some clever strategy to enforce them.
Without such an activation, when consensus rules make them spendable again youāll have unknown proportions of the network pruning the TXOs and some parts that havenāt, risking network fracturing and consensus failure. Itās not just a ābad lookā.
To prevent such a disaster from happening one will still need to have consensus rules preventing those TXOs from getting spent (you mentioned it ) rendering the pruning redundant except for saving a tiny bit of space.
As we discussed elsewhere in more detail, my current take is YOLO-allowing doesnāt actually do harm as long as the spec is clear about how people should handle their tokens downstream.
Without such an activation, when consensus rules make them spendable again youāll have unknown proportions of the network pruning the TXOs and some parts that havenāt, risking network fracturing and consensus failure. Itās not just a ābad lookā
As we discussed elsewhere in more detail, my current take is YOLO-allowing doesnāt actually do harm as long as the spec is clear about how people should handle their tokens downstream.
Yeah I think so long as we are āstrictā about it in that we only allow TXOs that parse correctly and are ālegalā in some abstract sense (correct capability byte, <= 40 byte commitment, amount >0 if pure fungible, etc), then I think thatās āsafeā in a way. Just have to be careful when summing up category IDās to catch overflow (but thatās an implementation detail for nodes)⦠sure. Wouldnāt be catastrophic.
And I predict the following: not a single TXO will be intentionally mined in this way. I would be surprised if more than a handful⦠or even more than 0 will appear between now and activation time. So long as we are very clear in the spec how to handle this, there is little incentive for āgriefersā to do this to us.
If we donāt anticipate it happening, and we have bugs in our code⦠then yes, griefers may do this to us. But just having a plan for this I think is enough to avoid potential attacks from BCH hatersā¦
Another thing we should probably add to the spec: A special rule for coinbase txns.
I propose the following consensus rule post-activation:
A coinbase txn should not be allowed to generate any vouts with PREFIX_TOKEN.
This would avoid the situation where miners can endlessly mint for CategoryID 0x0000000000000000000000000000000000000000000000000000000000000000 which would be both fairly useless and also annoying.
It is implicitly disallowed because coinbase inputs have prevout index 0xFFFFFFFF, but we only consider inputs with prevout index 0 as genesis candidates. Jason has this line:
Note: coinbase transactions have only one input with an outpoint index of 4294967295 , so they must never include a token prefix in any output."
Agreed, it should be explicitly stated itās disallowed.
Oh yeah duh⦠coinbase has prevoutN == 0xffffffff and so it can never be genesis.
So yeah ā it canāt be genesis. But definitely think it should be explicitly stated that coinbase txn blanket forbidden to have token PFX_BYTE in its outs at byte position zero ā as a blanket thing to explicitly point outā¦
Notably, the CHIP now includes ~3,500 full-transaction test vectors validating all aspects of the upgrade; these should make developing and validating new implementations much easier.
First CashTokens testnet
The first public CashTokens testnet has been very successful ā a test activation occurred on Sun Sep 04 2022 00:06:40 UTC, forking from testnet4. Since then, thousands of test transactions have been successfully created and mined.
Thank you to @cculianu for spearheading BCHNās CashTokens implementation and preparing a testnet release, and thank you to @im_uname for helping to start and maintain this testnet.
Weāll continue operating this testnet until the next coordinated activation test in mid October. To connect a BCHN node:
(Optional) If you have a testnet4 node running already, you can copy the whole data directory (e.g. name the copy ctnet). To run simultaneously with testnet4, youāll need to update your configuration with alternative ports, e.g. bitcoin.conf:
Be sure to open/forward the port on your router or firewall so other nodes can connect to you.
Run the CashTokens-enabled BCHN client using this iterationās activation time: -upgrade9activationtime=1662250000 (that is Sun Sep 04 2022 00:06:40 UTC)
Using RPC or the debug UI window, force your node to follow this testnet fork: first invalidateblock 00000000df3ee02e8912e2fd1205ed9366ea74bdaebf207b5e02601589de82d4, then: reconsiderblock 00000000a27de85e9f7799175aa1adb5df4095d1a277299135a479caf2bd40e9. If youāre still having trouble connecting, try manually adding a peer: t4fork.c3-soft.com:28333 or testnet4.imaginary.cash:28333.
If you want to join a mining pool, @cculianuās node (t4fork.c3-soft.com) is accepting non-standard transactions, stratum server at: t4fork.c3-soft.com:3333.
Work on higher-level token standards
Iāve started another topic for discussion of higher-level, application-specific token standards:
Iāll be focusing on these higher-level token standards throughout this month.
Next CashTokens testnet
We will reset the CashTokens Testnet by testing another activation on testnet4 in mid October. The goal is to have as many node implementations and software teams participate as possible.
Iāll share the updated connection information here soon, but weāre tentatively targeting an activation time of 1666094400 ā Tuesday, October 18, 2022 at 12 UTC.
Stakeholder review
The CHIP is nearing a final draft, and we have several implementations and a public testnet.
Following the CHIP process, Iāll be collecting reviews and feedback from stakeholders over the next month. The goal is to reach near-unanimous approval from all stakeholders, so Iāll work with the other contributors to address any outstanding concerns we identify during this process.
If we can demonstrate widespread approval across the Bitcoin Cash ecosystem, all stakeholders will consider the CHIP ālocked-inā for the May 2023 upgrade.
Creating āchipnetā
Staging ālocked-inā CHIPs on a long-running testnet could allow for more thorough testing, improve business certainty around upgrades, and even allow for May product launches following upgrades. (More discussion in this topic: Staging CHIPs on Testnet.)
So: the plan is to fork from testnet4 on November 15th at 12 UTC as part of ālock-in dayā. Weāre tentatively calling the new network chipnet, and the goal is to activate CHIPs on chipnet exactly 6 months before they activate on mainnet.
If chipnet proves valuable this year, it may be an ideal network for the ecosystem to coalesce around for upgrade staging and product testing.
Discussion & feedback
Reviews and feedback are deeply appreciated, either here or in the CHIP issue tracker. You can also join the discussion in CashToken Devs on Telegram.
I have two questions about the two new Token-Aware CashAddress types. My assumption, which may be incorrect, is that pure BCH transactions can occur with the new CashAddress types:
Will an observer of the BCH blockchain data be able to distinguish non-token BCH transactions sent to these addresses from current CashAddress transactions (either in an obvious or subtle way)?
The CHIP says, āToken-aware wallet software ā wallet software which supports management of tokens ā should use these CashAddress version byte values in newly created addresses.ā Does this mean that wallet developers who want users to be able to use CashTokens are expected/encouraged to set all receiving addresses to the new CashAddress versions regardless of whether a particular transaction will involve CashTokens?
I am asking these questions because I want to know the possible impact on transaction uniformity, fungibility, and privacy outside of the CashToken ecosystem.
No ā the addresses encode the exact same information, the only difference is a single bit that indicates whether or not the receiver āunderstandsā tokens. In fact, you canāt be confident that the receiving address understands tokens even if it receives them ā it could be token spam designed to throw off chain analysis. So information is only leaked when the receiver spends tokens.
As written, thatās the recommendation, but weāre considering removing that sentence: itās only a sensible recommendation for token-only applications. (E.g. the receiver wants some amount of a stablecoin, but the user has a non-token-aware wallet. If the receiver uses an address recognized by the wallet, the user may be confused into sending the equivalent stablecoin value in BCH rather than the requested token; creating more work for both parties in correcting the situation.) Iāll reference this post in the issue, thanks!
Iāll never rewrite any of the git history (so commit hashes wonāt change either), but I agree that itās nice to have tags as a sort of documentation. Makes it easy to click through to specific versions in many git interfaces.