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

Create an AML-compliant freezable rune standard #4213

Open
joshdoman opened this issue Feb 8, 2025 · 15 comments
Open

Create an AML-compliant freezable rune standard #4213

joshdoman opened this issue Feb 8, 2025 · 15 comments

Comments

@joshdoman
Copy link

A forum for continued discussion of the merits of PR #4209, "An AML-Compliant Freezable Runes Standard."

This PR attempts to implement a simple freezable runes standard that would be compliant with anti-money laundering (AML) regulations. The ability to freeze and unfreeze balances is the one missing feature that entities subject to AML need in order to issue securities on Bitcoin using the runes token standard.

The case for securities on Bitcoin
More and more public companies are adopting a Bitcoin Treasury strategy. These companies want to raise capital in bitcoin, but their shares can't trade directly against bitcoin in a permissionless way. If, however, their shares could settle on Bitcoin, they could easily raise bitcoin by issuing new shares, and bitcoin holders could easily invest and divest, without moving through fiat rails or doing KYC. These entities, though, are subject to AML and KYC regulations, meaning they must know who they are issuing securities to, and they must have the ability to freeze balances, if subject to a court order. This applies to stablecoin issuers as well.

I acknowledge that the idea of freezable runes runs counter to the core Bitcoin ethos. If someone doesn’t like the idea of owning assets that can be frozen, they shouldn’t own stock in any public company, but that shouldn’t preclude others from doing so. Companies are already subject to the jurisdiction of their host country, and to invest in their stock is to accept that jurisdiction.

If done properly, a feature that enables freezable runes would pose no risk to existing rune holders, as long as all users know which runes can be frozen, old versions of ord recognize freezable rune etchings as cenotaphs, and anyone can clearly see onchain whether a freezable rune is frozen or not.

If that’s all it takes for AML-compliant assets to be issued on Bitcoin, I think an official freezable runes standard is worth exploring. The runes standard is already the most transparent and easy to use token standard on Bitcoin, and I think it should be the standard of choice for AML-compliant assets as well.

Proposed Specification
Described below is an overview of how the implemented freezable runes standard works, but it is not a formal specification:

Etchings may contain a “Freezer” tag, which identifies the name of the rune that can freeze and unfreeze balances.

The “Freezer” tag is an even tag, so older versions of ord would register the etching as a cenotaph.

Runes are frozen and unfrozen by a freeze edict:

struct FreezeEdict {
  rune_id: Option<RuneId>,
  outpoints: Vec<OutPointId>,
}

An edict freezing runes is integer encoded following Freeze tags.
An edict unfreezing runes is integer encoded following Unfreeze tags.

Outpoint IDs are encoded as the block height, the transaction index, and the output of the outpoint to be frozen (or unfrozen).

struct OutPointId {
  block: u64,
  tx: u32,
  output: u32,
}

The rune_id is encoded as None if the first integer following the Freeze or Unfreeze tag is zero. Otherwise, the first two integers encode the Rune ID.

Freeze and unfreeze edicts are processed after input runes are unallocated, but before transfer edicts are processed.

A freeze (or unfreeze) edict may only freeze (or unfreeze) rune_id if the freezer rune, specified when rune_id was etched, is unallocated.

If rune_id is None, a freeze (or unfreeze) edict may freeze (or unfreeze) all runes that are freezable by unallocated runes.

A freeze (or unfreeze) edict freezes (or unfreezes) balances at all outpoints authorized to be frozen (or unfrozen).

A frozen balance is burned if the outpoint is spent while the balance is frozen. This prevents users from trading frozen balances, which would violate AML regulations.

Request for Feedback

I’d appreciate honest feedback on this proposal. Here are a few points I'm especially curious about:

  • Technical Details: Are there any potential pitfalls or edge cases in the way freeze/unfreeze edicts are processed? I'd appreciate suggestions on how to simplify or improve the implementation.
  • Client Compatibility: Do you see any issues with older clients treating freezable rune etchings as cenotaphs? The goal is to prevent users on older clients from accidentally buying frozen runes.
  • Balancing AML Compliance and Bitcoin's Ethos: I know this idea might raise concerns since it introduces a regulatory mechanism into a traditionally permissionless system. How do you feel about the trade-offs here?
  • User Risk: The current design burns frozen balances if they're spent. It’s harsh, but it’s simple, and I think it’s necessary to properly comply. In the worst case, if a balance is accidentally frozen and then burned, the issuer could issue a new balance to make the user whole. Do you think this is the right approach, or would you suggest a better one?

I’d be interested to know if the maintainers are open to exploring this type of proposal. The goal is to enable AML-compliant assets without affecting those who prefer non-freezable tokens.

Thanks for taking the time to review this - I’m looking forward to your honest feedback!

@casey replied:

First let me say that just in terms of quality, this PR is awesome. We very rarely get PRs for large feature which are as well written and have as comprehensive testing. So kudos for that!

I have a few qualms which I think prevent merging this PR. Some of them are technical:

  • Someone who holds runes could continually watch the mempool for freeze
    transactions, and avoid them by broadcasting a higher-fee transaction that
    moves the runes.
  • The fact that frozen rune, when spent, is burned, is problematic. Wallets
    would have to actively watch both the mempool and chain for freeze
    transactions, and lock outputs which were frozen to avoid having them burned.
    Worse, if the wallet has in-mempool transactions which move the runes, and
    then detect a freeze transaction, they have no recourse to avoid those runes
    being burned. (Technically, if the transaction has inputs other than the one
    being frozen they could RBF a conflicting transaction which spends only the
    non-frozen input, but that might not be the case if the transaction only has
    the input with the rune being frozen.
  • As an alternative to freezing, an issuer could send runes into a 2-of-2
    multisig, with one key being held by the issuer, and one key being held by
    the owner. This would allow any kind of freezing scheme, wouldn't suffer from
    accidentally burning frozen runes, and wouldn't require dedicated freeze and
    unfreeze transaction. To freeze, the issuer would simply refuse to cosign
    transactions. The 2-of-2 multisig could implemented using MuSig, which
    wouldn't incur any additional overhead compared to a single signature.
  • If an admin key is stolen, an attacker could freeze all runes and demand a
    ransom.
  • If we add a new even key, a transaction could be made which is considered a
    cenotaph by unupgraded versions of ord and which includes an admin token. If
    a freeze transaction is later issued with that admin token, old versions of
    ord will not see it, since to them, the transaction didn't contain the
    required admin token, but new versions would see it as a valid freeze
    transaction. Old version of ord might then accidentally spend the frozen
    runes, thus burning them.

Some are less technical:

  • I don't think that freezing is really in the spirit of runes. Runes attempt
    to be as permissionless as Bitcoin, and I'm not sure that freezing is a good
    fit for the protocol.
  • I'm not sure that if we add this, we'll see adoption of runes that wouldn't
    occur otherwise. There hasn't been, as far as I know, any real adoption of
    digital stock issuance, or other things which would require freezing.
  • If we add this, some people might use it, for example people issuing
    securities or stablecoins. However, if we don't add it, then those people
    possibly would have gone ahead with their projects anyways, so perhaps not
    giving them the option would be better.
    For example, lets say that at some point in the future, some company wants to
    issue a stablecoin via a rune. If this feature exists, they may make the rune
    freezable. If this feature doesn't exist, they might issue it anyways but
    without the ability to freeze the rune. I would strongly prefer the latter.
  • Even if this is opt-in on a per-rune basis, users might not realize that a
    particular rune is freezable. Making it so that no Runes are freezable would
    mean that users don't need to check or think about it, and there's no
    possibility for confusion.
  • Some people will probably use this in an irresponsible way, to issue a rune
    with a seemingly low team issuance, but then freeze large portions of the
    circulating supply so they can sell their issuance at a high price.

I think this can't be merged at the moment, so I'm going to close it. Although thank you for writing such a comprehensive PR.

I think it would be a good idea to open an issue, where this can be discussed further, although I personally would probably be against the feature, unless all or most of the issues above are resolved.

In particular, I'm curious if there are any projects which would use this. If somehow not adding this prevents the formation of issuance of real securities on Bitcoin, then I would certainly be more in favor.

@joshdoman
Copy link
Author

Thanks again @casey for the detailed response! You make good points, and I agree that the risk of accidental burning needs to be addressed. I think I came up with a relatively simple solution, which I'm in the process of implementing:

If an outpoint holding a frozen balance is spent, and a new transaction creates an outpoint with an identical script_pubkey and contains an unfreeze edict authorized to unfreeze the balance, the balance is unfrozen and teleported to that unspent outpoint.

This eliminates the risk of a frozen balance getting burned and ensures that only the original owner can move it once it gets unfrozen. It also cleanly separates the issuer role from the freezer role. Before, the expectation was that the issuer re-issue the rune if it got accidentally burned, but that's not necessarily a reasonable expectation.

I can think of two potential concerns with this approach:

  1. This solution implies key reuse, which could have security and privacy implications. Avoiding key reuse would require deterministically deriving a new spending path that only the original spender can access. This might be possible for single sig wallets, but it becomes increasingly difficult to implement for more complicated spending paths.
  2. The ord client must maintain an index of every script_pubkey controlling a spent outpoint with a frozen balance. To my knowledge, ord only indexes unspent outpoints at present, so this could add expensive overhead in the long-run, which in the worst case scales with the set of spent outpoints, rather than the UTXO set. Storage concerns could be mitigated by letting users turn off the indexing of freezable runes, or even specify which freezable runes they want to index.

I'll write a separate comment responding to your other points, but I wanted to first put this idea on paper. Would this approach address your technical concerns about frozen runes getting accidentally burned? Appreciate any feedback you might have.

@casey
Copy link
Collaborator

casey commented Feb 8, 2025

I missed the part about re-issuing burned runes in your original post. And actually, I think the feature can be made way simpler if that's just the way that it always works. So instead of the admin rune being able to freeze balances, it can just nuke them, with the expectation that they hold runes in reserve which they can use to make someone whole if and when whatever issue is going is resolved.

Instead of having a happy path (runes are frozen, not accidentally burned, and unfrozen) and a sad path (runes are frozen, accidentally burned, issuer must issue new runes) just make the sad path the only path.

It also actually seems better from a user education point of view, since it's scarier, so people will probably be more wary of nukable runes, but functionally identical to freezing.

That being said, I still on balance don't think it's a good idea, primarily because of demand. Like if there were 10 real companies that were going to issue stock on runes tomorrow but couldn't because they couldn't freeze runes, then I would definitely be more persuaded, but I'm not sure that's the case. I would really want to exhaust avenues that didn't involve complicating the protocol, like issuance into 2-2 cosigning arrangements, or just having them rely on chainalysis to track tainted assets.

@joshdoman
Copy link
Author

@casey Below are my initial thoughts in response to the points you brought up.

Someone who holds runes could continually watch the mempool for freeze transactions, and avoid them by broadcasting a higher-fee transaction that moves the runes.

This is true, but this problem in theory exists on every blockchain where freezable assets, like USDT and USDC, exist. The difference, of course, is that with Bitcoin's 10-minute block times, there's more time to broadcast a transaction to avoid a freeze. You'd then have a cat and mouse game between the balance holder and the freezer, until one gives up.

In practice, I don't think this is too much of a problem, because private mempools exist. A freezer can always accelerate their transaction with the mempool.space accelerator or send it directly to a miner, through a service like Marathon's Slipstream. The threat of doing so would probably be enough to dissuade a balance holder from trying to getting ahead of a freeze transaction, because it'll definitely be futile in the end, and simply a waste of money. If so, freeze transactions could be issued in the public mempool, and only a tiny minority would need to be accelerated or sent to a private one.

The fact that frozen rune, when spent, is burned, is problematic. Wallets would have to actively watch both the mempool and chain for freeze transactions, and lock outputs which were frozen to avoid having them burned...

This is absolutely a valid point, and I think the solution I proposed above would resolve it. Let me know what you think.

As an alternative to freezing, an issuer could send runes into a 2-of-2 multisig, with one key being held by the issuer, and one key being held by the owner.

This could theoretically work, but I don't think users would race to adopt it. Transfers wouldn't be permissionless, which is the main benefit of issuing assets natively on Bitcoin, and users could lose unrelated balances (bitcoin, runes, inscriptions, and otherwise), if they're held on an outpoint that gets frozen or if the owner loses their key or simply goes offline.

A freezable token standard that is permissionless by default is much more likely to be successful. I don't think stablecoins, for example, like USDT or USDC, would be nearly as successful on other chains if every transfer required the issuer's sign off.

If an admin key is stolen, an attacker could freeze all runes and demand a ransom.

This risk is unavoidable and exists with every freezable digital asset today, and it hasn't proved to be an issue. Users can perform their own due diligence and decide whether or not to trust the security practices of the owner of the admin key. What is more important, I think, is that users are made aware that a rune is freezable, before they buy it. Adding a flag in the ord server interface is important for this reason.

If we add a new even key, a transaction could be made which is considered a cenotaph by unupgraded versions of ord and which includes an admin token. If a freeze transaction is later issued with that admin token, old versions of ord will not see it, since to them, the transaction didn't contain the required admin token, but new versions would see it as a valid freeze transaction. Old version of ord might then accidentally spend the frozen runes, thus burning them.

Is the concern that a user on old versions of ord would unknowingly try sending a freezable rune to their ord wallet?

The fix I proposed in the comment above would resolve the risk of the rune getting accidentally burned. The only other risk I see is that the outpoint containing the freezable rune gets spent by accident, because it does not get locked. I don't think there's a way to resolve this, other than educating users not to try to receive freezable runes in an old version of ord. I think that's a manageable disclosure, just like it was disclosed when ord was launched not to send inscriptions to incompatible wallets.

I don't think that freezing is really in the spirit of runes. Runes attempt to be as permissionless as Bitcoin, and I'm not sure that freezing is a good fit for the protocol.

Understood. I suppose it comes down to what Runes is trying to be. Runes is sort of synonymous with memecoins and trustless fungible tokens today, and perhaps a freezable token standard for "real world" asset issuers should be called something else. I tend to think it should all be in one protocol, so that all fungible assets can be managed in a single wallet and marketplace, and protocols like lightning can easily be built on top.

I'm not sure that if we add this, we'll see adoption of runes that wouldn't occur otherwise. There hasn't been, as far as I know, any real adoption of digital stock issuance, or other things which would require freezing.

There's a lot of talk by Blackrock and others about finally getting permission to tokenize securities under the new administration, but they've been largely prevented from doing so until now. There is growing adoption, though, of assets that require freezing. The most obvious is centrally issued stablecoins, like USDT and USDC, and the other big category has been yield-bearing stablecoins and tokenized treasury bonds, which are securities. Neither use case is particularly aligned with the Bitcoin ethos, in my opinion, but they're an indicator of what features are needed for bitcoin-aligned securities to get issued onchain.

I do think there's much stronger product market fit for stock issuance on Bitcoin than on Ethereum or other chains, so I'm not dismayed that you don't see companies pushing for it there. Companies don't care about DeFi, but they care about their ability to raise capital and attract investors. If you're a bitcoin-aligned company on a bitcoin treasury strategy, the only way to properly do that is to issue stock natively on bitcoin, so it can be traded permissionlessly.

Another use case that needs freezing that might appeal to bitcoiners is regulated levered bitcoin products, like a levered bitcoin ETF. I can imagine there are bitcoiners who would buy a product like that, if it could be purchased permissionlessly onchain.

If we add this, some people might use it, for example people issuing securities or stablecoins. However, if we don't add it, then those people possibly would have gone ahead with their projects anyways, so perhaps not giving them the option would be better.

For certain use cases, like the issuance of a regulated public company's stock, it is simply impossible to issue it onchain without a freeze mechanism if you're going to comply with the law. The US banned bearer stocks and bonds (the kind of certificates you could hold in a safe at home) back in the 1980s, and I don't see that changing anytime soon. Either the freeze mechanism is obscure, like in Taproot Assets, where the issuer suddenly stops recognizing an asset offchain, or the freeze mechanism is transparent and onchain. I prefer the latter, and I think others would too.

That being said, you're correct that there could be projects that are not subject to AML rules, that decide to add freezability anyway, out of an abundance of caution. Perhaps they would go ahead and launch anyway if this feature didn't exist, or perhaps not. It's hard to say. This hasn't proven to be an issue on other chains, and users wisely tend to avoid projects that could give the creators too much power and control than is strictly necessary.

At the same time, project creators don't want their tokens to be labeled securities by the SEC. A centralized freeze function would make it clear that the project was centrally controlled and would increase the risk that the SEC goes after them for creating an unregistered security. I suspect this is why you don't see many projects with freeze features on other chains, except for those tokens that absolutely need it.

Even if this is opt-in on a per-rune basis, users might not realize that a particular rune is freezable. Making it so that no Runes are freezable would mean that users don't need to check or think about it, and there's no possibility for confusion.

That's true and very valid. On other chains, wallets and marketplaces like Uniswap tend to mark new and unknown projects with a flag, indicating that interacting with the contract could be risky. I suspect the same would happen here, which is why I made the Freezer tag an even tag, so freezable runes wouldn't even show up in wallets and marketplaces running old versions of ord. It would be incumbent on the user to vet the token that they're buying. Adding a label on the rune's page in ordinals.com helps with this.

Some people will probably use this in an irresponsible way, to issue a rune with a seemingly low team issuance, but then freeze large portions of the circulating supply so they can sell their issuance at a high price.

Perhaps, though, I don't recall many stories like this on other chains. Users would wise up to that scheme pretty quickly and learn that they need to vet all freezable projects. Personally, I wouldn't touch a rune that could be frozen unless I knew the issuer and felt that they were trustworthy and capable of securing the admin keys.

@casey
Copy link
Collaborator

casey commented Feb 9, 2025

All good points!

Is the concern that a user on old versions of ord would unknowingly try sending a freezable rune to their ord wallet?

No, it's actually a little bit trickier.

Let's say that we introduce a new even tag, FOO, which is recognized by ord 0.30 but not ord 0.29.

The holder of an admin token performs a transaction with input A containing an admin token which uses the even tag FOO in the transaction's runestone, and creates output B which receives the input admin token.

Now, according to ord 0.30, output B contains an admin token, but according to ord 0.29, it does not. The holder of output B now creates a freeze transaction freezing output C, which contains the freezable rune.

If the holder of output C is running ord 0.29, they will not see that it's frozen, because the freeze transaction did not contain an admin token. They spend output C, and it's burned. (Or, alternatively, if we use burning instead of freezing, then if they send output C to someone running ord 0.29, they will see it as containing runes, when in fact it they've been burned.

@joshdoman
Copy link
Author

joshdoman commented Feb 10, 2025

Instead of having a happy path (runes are frozen, not accidentally burned, and unfrozen) and a sad path (runes are frozen, accidentally burned, issuer must issue new runes) just make the sad path the only path.

It also actually seems better from a user education point of view, since it's scarier, so people will probably be more wary of nukable runes, but functionally identical to freezing.

That was my original thinking, and I think you're right. It would be a mistake to overcomplicate things. Making it somewhat scary encourages users to check if a rune is frozen before spending it and to use wallets that are aware of frozen runes.

There is one aspect, though, that still bothers me. The issuer is expected to maintain a balance in reserve, allowing them to reissue accidentally burned frozen runes. What happens though if that balance is depleted? Or if someone wants to issue a freezable rune with a provably limited supply?

Here's a simple change that fixes the problem:

A frozen balance is declared lost if the outpoint is spent while the balance is frozen.

The lost runes of rune_id will be collected by the first transaction with an unfreeze edict authorized to unfreeze rune_id. Collected runes are added to the transaction's unallocated balance. This provides a way for the freezer to return lost runes that would otherwise be unfrozen.

I think this is intuitive for users to understand. Lost runes exist but don't have an explicit UTXO, and they can only be collected by the freezer. It's simple to implement, requires minimal extra overhead, and gives the freezer complete flexibility over how, when, and if to reissue the runes. It also eliminates the risk of the issuer running out of runes in its reserves and lets issuers delegate freezing and unfreezing to a 3rd party, if they desire.

I think this is the most natural solution, and I went ahead and implemented it in my branch of freezable-runes, if you'd like to check it out. The one drawback I can think of is that it introduces a way for runes to be teleported from one outpoint to another via valid freeze and unfreeze edicts. This could be an issue if a new even tag like FOO is created, like you suggest, which could make an updated version of ord recognize a freeze edict that an older version of ord does not recognize as valid. There might then be balances that exist in the new version of ord that don't exist in the old one.

I think this is resolvable if issuers are required to explicitly endorse new even tags, via an in-protocol message. Unupgraded versions could then freeze all balances, forcing users to upgrade or cease trading. Upgraded versions of ord would also then know if it's safe to recognize the freezable rune.

Let's say that we introduce a new even tag, FOO, which is recognized by ord 0.30 but not ord 0.29.

The holder of an admin token performs a transaction with input A containing an admin token which uses the even tag FOO in the transaction's runestone, and creates output B which receives the input admin token.

This is a really interesting scenario that I admit I hadn't considered before you mentioned it. I think the proposal I made above, forcing freezable rune issuers to explicitly recognize new even tags, could resolve it in-protocol, but even as it stands, I think this scenario is manageable.

If I'm an issuer subject to AML regs, it is extremely important to me that if ordered by regulators to freeze an address, it's recognized as frozen on all wallets and marketplaces. Otherwise, I could be accused of failing to comply with the law. Blame would be placed on me if the asset were traded to a buyer who had no way of knowing it was frozen.

A simple solution, therefore, as an issuer is to be careful to avoid using new even tags, to avoid issuing freeze edicts from a freezer rune unrecognized by older versions of ord. A better solution, though, is for issuers to clearly state which versions of ord / which even tags are legally supported. Only balances indexed using a version of ord that recognizes only those even tags would then be legally recognized in court.

Could an issuer opt-in to new even tags? I think so, but it should require explicit endorsement of the new even tag onchain. This would alert older versions of ord that it is unsafe to send or receive the issuer's rune. Internally, ord could then mark all balances as frozen, locking those outpoints and preventing users and marketplaces from purchasing a frozen or non-existent rune. This would formalize an implicit process that already exists on other chains when those chains decide to hard fork.

What about users running upgraded versions of ord? I think the solution is for ord to explicitly check which even tags are internally supported and to only display freezable runes that explicitly support those tags. This is fairly easy to implement and guarantees user safety. If a new tag is popular, users will demand that an issuer support it, so that they can run a single version of ord. Marketplaces might even stop supporting freezable runes that don't endorse the new tag, which could put economic pressure on issuers to endorse it.

Thanks for raising this concern! Do you have any feedback on this approach? If a tag endorsement mechanism is the way to go, you could potentially extend it to let issuers endorse odd tags as well as even tags, forcing users to upgrade when those tags are introduced, though I can't think off the top of my head why an issuer would want to do that.

@joshdoman
Copy link
Author

Thinking about this more, you'd probably want to give users a way to override an even tag endorsement, so that they could still see and transfer their balance without having to upgrade to a version of ord they might not want to use. Here's how the flow might work:

  1. The freezer decides to endorse a new even tag and issues the endorsement in a runestone.
  2. Without the endorsement, versions of ord that recognize the even tag index the rune in the background but show all balances as frozen. Once the endorsement is indexed, the hold is lifted, and ord shows the balances like normal.
  3. Versions of ord that do not recognize the even tag immediately show all balances as frozen when the endorsement is indexed. The user can then override this in settings if they want to access their balance, by refusing to index all endorsements or endorsements for this specific rune. Alternatively, there could be an "allow-frozen" param in the wallet that lets users transfer a frozen balance.

The goal first and foremost is to clearly and unambiguously signal to users when their version of ord no longer produces the official ledger and to indicate to users which even tags need to be recognized. Users can then decide how they want to proceed, understanding the risks.

Summaring (for myself):

  1. If the freezer endorses a new even tag, users on old versions of ord risk spending or buying runes that have been frozen, because their indexer fails to index a valid freeze edict.
  2. If the freezer does not endorse a new even tag, users on new versions of ord risk spending or buying runes that have been collected after being lost, because their indexer incorrectly thinks a freeze edict is valid.

Question 1: Should endorsements be reversible, or should they be permanent?

Reversible endorsements may make sense if there's a new even tag that an issuer wants to opt out of, after trying it. This might happen if a new even tag has unexpected, or catastrophic side effects, and the issuer wants to discourage holders from using it, because their balance will be burned. This gives maximum flexibility to issuers, which could increase uptake of new even tags. If issuers can't easily reverse an endorsement, they'll be more reticent to adopt it, which isn't good for the protocol as a whole.

On the other hand, if a user thinks it's safe to use a new even tag, and they're not actively indexing the chain for reversed endorsements, they might use a new feature that causes their balance to get burned. The issuer could always issue them a new balance out of their reserves, but what if the reserves are insufficient? This would create a case for a dedicated "minter" role, which can unburn balances and mint new supply. Regardless, issuers would need to actively determine when to reissue burned runes, which could be a significant financial and operational burden.

Personally, I'd lean toward non-reversibility. Bitcoin soft forks, for instance, are implicitly assumed to be non-reversible. If there was any chance a soft fork could get reversed, few would use its features, out of fear their bitcoin could one day get stolen. I think the same applies to the recognition of even tags.

Question 2: Should endorsements be applied to transactions only after the endorsement is made, or should they be retroactive?

I tend to think it's safe to make endorsements retroactive, because users already have to trust the freezer to issue freeze edicts responsibly. What matters is that there is an in-protocol way to index and construct the official ledger, if a new even tag is recognized, or if it's later revoked.

Question 3: Instead of requiring issuers to explicitly endorse each even tag, what if unrecognized even tags didn't produce a cenotaph for freezable runes?

This would violate a core principle of the current rune specification, that unrecognized even tags produce cenotaphs, but it might actually be the simplest and most elegant solution. It would eliminate the risk of new even tags creating divergent ledgers in different versions of ord, and it would eliminate the need to build an explicit tag endorsement mechanism, that would require issuers to occasionally make endorsements. Issuers would look favorably on this, I think, because they don't have to keep up with protocol developments and can simply focus on minting, freezing, and unfreezing.

It would take a little work to implement, but I don't think it would be too bad. You'd first decipher two artifacts from the OP_RETURN, one where unrecognized even tags produced a cenotaph, and one where they didn't. If neither produces a cenotaph, nothing changes, but if one produces a cenotaph and the other doesn't, you'd index regular runes using the first and freezable runes using the second.

@casey How would you feel about this as a solution?

@joshdoman
Copy link
Author

Instead of requiring issuers to explicitly endorse each even tag, what if unrecognized even tags didn't produce a cenotaph for freezable runes?

Actually, now that I think of it, the only rune that we need to do this for is the freezer rune. Freezable runes can be treated identically to regular runes with respect to even tags. It's the rune that has the authority to freeze and unfreeze that is causing the problems.

In short, make sure the freezer rune can't get burned by a new even tag. This would prevent different versions of ord from producing divergent ledgers, where each shows one or more balances the other lacks.

End users likely won't own freezer runes, so the downsides of doing this seem somewhat limited.

@joshdoman
Copy link
Author

Ok, I think I've landed on an even simpler solution that doesn't preclude freezer runes from getting burned due to unrecognized even tags:

Show all balances as frozen if a freezer rune is burned due to an unrecognized even tag.

Once a freezer rune is burned due to an unrecognized even tag, running an old version of ord becomes risky, because you might discard a valid freeze edict. Freezing all balances notifies the user to stop trading until they update their version of ord. There could still be an override mechanism for users who don't want to upgrade, but those users would know they have to proceed with caution.

In effect, burning a freezer rune by including an unrecognized even tag would be equivalent to an irreversible endorsement of that tag by the issuer. I think that's intuitive and a fair compromise, and it's fairly lightweight to implement.

@casey
Copy link
Collaborator

casey commented Feb 10, 2025

I think the idea of frozen runes being removed from supply and being reissuable by the freezer makes sense.

There might then be balances that exist in the new version of ord that don't exist in the old one.

I'm actually less worried about this than the other way around, since it can't be used to trick someone into believing they've received runes when in fact they haven't.

A better solution, though, is for issuers to clearly state which versions of ord / which even tags are legally supported. Only balances indexed using a version of ord that recognizes only those even tags would then be legally recognized in court.

This works, but it's a not great if everyone is looking to issuers to see which versions of ord they endorse.

Show all balances as frozen if a freezer rune is burned due to an unrecognized even tag.

This isn't a terrible idea, and probably the safest option.

But, keep in mind, all the issues we're discussing are mostly details of the implementation. The question of whether this is necessary at all, and if it is, if it's worth the trade-offs to permissionless, and would it encourage people to use it who otherwise wouldn't, is the more important question.

@joshdoman
Copy link
Author

joshdoman commented Feb 10, 2025

But, keep in mind, all the issues we're discussing are mostly details of the implementation. The question of whether this is necessary at all, and if it is, if it's worth the trade-offs to permissionless, and would it encourage people to use it who otherwise wouldn't, is the more important question.

Understood. Those questions are very important but not technical in nature, and the answer might change over time. So I think it's worth nailing down the technical implementation anyway. That way, we can have those conversations.

I really appreciate the feedback! I'm glad that freezable runes seem at least technically possible to implement safely. Whether to actually do so is a different question, which can only be answered through feedback from potential issuers and users of ord.

You'd probably want to turn freezable runes off by default, by adding an index-freezable-runes setting that defaults to false. This might resolve most user concerns, but I recognize it might not make sense to add to ord in the first place, if there isn't demand from issuers, or if it goes against the ord ethos.

I tend to think that issuers would jump on this if they knew it existed, given their incentives, but I need to test that hypothesis.

@joshdoman
Copy link
Author

@casey @raphjaph I think there's an even cleaner way to resolve the version compatibility concerns regarding freezing:

Create a new class of fungible tokens: Non-Upgradeable Runes.

This wouldn't be too difficult to implement. You could add a new flag in runestone/flags.rs which guarantees the token is burned if a flag or even tag is present that would be unknown at the time the runes protocol was launched (flags greater than 2, even tags greater than 22).

Alternatively, you could implement an even tag in runestone/tags.rs that restricts flags and even tags to those below specific values.

In other words, the creator of the token commits publicly to a rule, forbidding the token from being transferred by edict in a runestone that would be considered a cenotaph by the creator, at the time of the token's creation. This provides strict backwards compatibility that explicitly reflects how a fungible token whose balances must always be known is expected to work.

Essentially, owners of the rune would not be given the ability to "opt-in" to new backwards-incompatible features, as they are able to do explicitly today via flags and even tags. This is particularly helpful for a governance token, like a freezer rune, since owners of the fungible freezable token must always know where it exists onchain.

To a certain extent, the turbo field that is already present could be used to implement this feature. Runes with the turbo field turned off could be made explicitly non-upgradeable, while runes with the turbo field on are. The only problem with this approach is that runes have been etched with the turbo field off, without strict non-upgradeability of the name space and balance being the intention. If this was applied retroactively, users, wallets, and marketplaces might choose not to upgrade their version of ord.

Do you think this non-upgradeable rune primitive warrants an issue and a PR? If there's positive community feedback, I can take a stab at speccing it out and building one.

@joshdoman
Copy link
Author

I might add that this thread has gotten me thinking deeply about the nature of proper abstractions on Bitcoin. I think there's a useful parallel to how the best protocols get built on EVM.

It is extremely rare and hard to do, but a select few non-upgradeable, immutable smart contracts have been built on EVM, which provide extremely powerful primitives that meet the needs of a large number of users, for a very specific use case.

The most famous of these contracts are the ones built by Uniswap. Starting with Uniswap v2, the Uniswap protocol team has taken great effort to build the absolute thinnest smart contract / protocol possible for use cases where you want to programmatically swap between fungible ERC-20 tokens on EVM. The smart contract / protocol is explicitly immutable, both at the interface level and internally, and with the exception of a 25bps fee switch, which is presently off, there is no governance or even upgradeability at the smart contract layer. In short, anyone running the smart contract locally will know exactly what the output state will be for a given set of inputs interacting with the "official" contract address, provided they know the current state.

This is almost unheard of on Ethereum, because of how hard it is to do, and how little incentive there is for teams to throw away the right to make changes, without deploying a new contract to a new address. At best, teams will adhere to an interface, but that says nothing about the code that runs inside.

I think this is the only workable model for the development of primitives on Bitcoin. Runes and inscriptions are already explicitly immutable by design, both in terms of the "interface" and in terms of the internal implementation, which is only specified in the initial implementation deployed by ord that gains general acceptance. This is critical so that a generally accepted social contract can develop, which recognizes the owner of the asset.

This is impossible without strong guarantees about the ability of users to find the tape that the Turing machine runs on and identify the code that they should run on the machine. Inscribing the data on Bitcoin's tape solves the first problem, if the use case warrants the ability to natively interact with bitcoin-the-asset. Writing a well-specified, tested, and well-maintained version of ord solves the latter. People only download new versions of ord because they trust the core developers to maintain the inner workings and adhere to the interfaces that have been built, exactly as originally specified.

This implies a fundamental design principle. Owners should never need to run a new version of ord. This is already implicit in Bitcoin / Bitcoin Core, and it implies that it should be considered unacceptable to freeze all of a token's balances in an old version of ord, because the whereabouts of its governance token, in this case, the freezer rune, are unknown.

@joshdoman
Copy link
Author

A few follow up thoughts,

In a sense, just like Bitcoin Core and its core developers, ord and the core developers of ord provide the immutable interface, specified implementation, and the backwards-compatible guarantees that are necessary to facilitate the adoption of a new socially-enforced contract, where the data lives on the Bitcoin tape.

Before ord, such strong contract guarantees seemed impossible to be run on the Bitcoin "tape" without the Turing Completeness that EVM provides. Protocols could be built that seals the time a piece of data was created by committing to it on Bitcoin, like RGB or Taproot Assets, but this isn't sufficient for a bottoms-up social contract to gain adoption. Without strict guarantees about the availability of the data that runs through the protocol's machine, users can't assume that other users can run it, or that it will be runnable indefinitely into the future.

This suggests that EVM and Turing complete smart contract were never needed in the first place. Everything that Vitalik wanted to enable by building a Turing complete blockchain can be replicated on Bitcoin, with careful enough consideration. Not every use case will warrant L1 blockspace, but legitimate use cases proven out by EVM can provide a guide to what primitives can persist amid high demand for block space on Bitcoin.

@joshdoman
Copy link
Author

Finally,

In a sense, a non-upgradeable token standard may actually be the base primitive. The ability to let the "owners" of a fungible token opt-in to reserve the name space and balance of a different token standard, running on a different protocol's machine, is by no means a universal requirement for all fungible tokens, and there's a case that can be made that it shouldn't be the default.

As it stands today, it is the default, and while it's a powerful, cool, and potentially very useful feature for creative fungible tokens and future applications, it might encourage the creation of a fungible token standard that leads to dark patterns, which should not in any way be called a "rune," or any derivation thereof. This rule can only enforced by making it explicitly inaccessible in rune-only wallets, and discouraging the use of the word "rune" in the new standard's name.

This is what I unintentionally did when I forked ord and implemented freezable-runes, and safeguards should be put in place to prevent that from happening in the future.

Making a non-upgradeable token standard the default, rather than a "Rune" could go a long way toward making this happen. At the very least, it provides support to the idea of enabling a non-upgradeable rune.

Ideally, though, it would be a generic meaningless primitive, like "ERC-20," which becomes the default for all fungible tokens on Bitcoin. This ensures that creators of fungible tokens must actively decide whether it requires the "upgrade" feature. As a user, I may then prefer wallets where non-upgradeable fungible tokens is the default. This puts pressure on creators to avoid adding the "upgrade" flag, unless they have a legitimate use case that well and truly needs it.

This could be a massive improvement compared to how fungible tokens work on EVM chains. Nothing in the ERC-20 interface indicates whether a token is upgradeable or not, so upgradeability must be presumed to be the default.

At the very least, I formally propose that a freezable fungible token standard must be freezable by a non-upgradeable rune.

@joshdoman
Copy link
Author

Ok, that may have been a bit harsh.

Who am I to say?

What others may require?

Who am I to declare, what powers may be bestowed?

For that is a form of power, itself, is it not?


TBC

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

No branches or pull requests

2 participants