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

CHIP-0014: Chialisp ASSERT_BEFORE_* conditions #59

Merged
merged 9 commits into from
May 4, 2023
159 changes: 159 additions & 0 deletions CHIPs/chip-0014.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
CHIP Number | 0014
:-------------|:----
Title | New Chialisp ASSERT Conditions
Description | Add new ASSERT conditions to Chialisp, and update mempool logic accordingly
Author | [Arvid Norberg](https://github.com/arvidn)
Editor | [Dan Perry](https://github.com/danieljperry)
Comments-URI | [CHIPs repo, PR #59](https://github.com/Chia-Network/chips/pull/59)
Status | Final
Category | Standards Track
Sub-Category | Chialisp
Created | 2023-02-22
Requires | None
Replaces | None
Superseded-By | None

## Abstract

The Chialisp language currently includes [conditions](https://docs.chia.net/conditions/ "Chialisp conditions") to assert that a block height or timestamp has been reached.
This CHIP introduces the opposite conditions, which assert that a block height or timestamp has _not_ been reached.
It also includes conditions to assert that another spend occurs in the same block or spendbundle, as well as conditions to assert that the current coin was confirmed at a specified block height or timestamp, and a condition to ensure that a coin spend is ephemeral. Finally, this CHIP introduces new mempool logic that will eject a pending coin spend if that spend is no longer valid.

## Motivation

Chialisp's current implementation contains four conditions that prevent a coin from being spent _before_ a certain block height or timestamp:

| Condition | Format | Valid if | Compares against | cmp |
| :---------------------- | :------------------------- | :----------------------------------------------------------------------------------------- | :------------------------- | :-- |
| ASSERT_SECONDS_RELATIVE | `(80 seconds_passed)` | The current block's timestamp >= coin's birth timestamp + `seconds_passed` | Current block | >= |
| ASSERT_SECONDS_ABSOLUTE | `(81 seconds)` | The current block's timestamp >= `seconds` | Current block | >= |
| ASSERT_HEIGHT_RELATIVE | `(82 block_height_passed)` | The previous transaction block's height >= the coin's birth height + `block_height_passed` | Previous transaction block | >= |
| ASSERT_HEIGHT_ABSOLUTE | `(83 block_height)` | The previous transaction block's height >= `block_height` | Previous transaction block | >= |

However, the opposite conditions do not exist. This CHIP presents a clean way to prevent a coin spend from being executed _after_ a block height or timestamp.

The primary benefit of the conditions introduced in this CHIP is that they enable expiring [Offers](https://chialisp.com/offers "Description of Chia Offers"), which obviates the need for users to cancel their Offers manually, e.g. due to price slippage. The current coin spend would no longer be allowed after a certain block height or timestamp, and the mempool would reject this spend. A new spend of the same coin could later be created with a new set of conditions in the solution.

The CLVM already allows the condition codes from this proposal to be used, but there are currently no mappings thereof. These condition codes are therefore currently treated as no-ops, which always pass. The primary technical challenge of this CHIP is to add logic to the mempool to reject any coin spends that use expired conditions.

## Backwards Compatibility

The new Chialisp conditions introduced in this CHIP are backwards compatible -- any calls that succeed after the CHIP has been implemented also would have been successful no-ops beforehand.

However, the Chialisp conditions to be added are not forward compatible -- before this CHIP has been implemented, the CLVM operators could have been called in an attempt to do something not specified in this CHIP. These calls would have been successful no-ops beforehand, but they will no longer succeed afterward.

Because of the forward incompatibility of the conditions to be added, this CHIP will require a soft fork of Chia's blockchain. As with all forks, there will be a risk of a chain split. The soft fork could also fail to be adopted. This might happen if an insufficient number of nodes have upgraded to include the changes introduced by this CHIP prior to the fork's block height.

## Rationale

Our previous recommendation to enable expiring Offers was to create a singleton that uses the `CREATE_COIN_ANNOUNCEMENT` condition each time it is spent. Any coins that were to be involved in an expiring offer would then use the `ASSERT_COIN_ANNOUNCEMENT` condition to assert that the singleton was being spent in the same block. After the singleton was spent, the Offer coins would automatically be invalidated.

However, we have since realized that this technique is inadequate in cases where high frequency trading is desired because it would require a coin spend to make Offers expire. It is also an inelegant solution because it requires multiple coins to be spent simultaneously when an Offer is accepted.

The design laid out in this CHIP does not rely on any coin spends in order to invalidate an Offer. It also only requires a single coin to be spent in order for an Offer to be accepted.

Chia's [standard transactions](https://chialisp.com/standard-transactions) use a delegated puzzle, which allows the solver to specify the puzzle and solution they would like to run when the coin is _spent_, rather than when it is _created_. The new conditions introduced in this CHIP are recommended to be used in the solution for a delegated puzzle. In cases where the new conditions are no longer valid, the coins can still be spent with a new solution.

## Specification

The soft fork will be activated at block `3 886 635`. It will add the following conditions to Chialisp:

### New conditions

| Condition | Format | Valid if | Compares against | cmp |
|:------------------------------ |:-------------------------- |:---------------------------------------------------------------------------------------------- | :------------------------- | :-- |
| ASSERT_CONCURRENT_SPEND | `(64 coin_id)` | This coin is spent in the same block as the coin with the specified `coin_id` | N/A | = |
| ASSERT_CONCURRENT_PUZZLE | `(65 puzzle_hash)` | This coin is spent in the same block as at least one coin with the specified `puzzle_hash` | N/A | = |
| ASSERT_MY_BIRTH_SECONDS | `(74 seconds)` | This coin's birth timestamp is the same as `seconds` | N/A | = |
| ASSERT_MY_BIRTH_HEIGHT | `(75 block_height)` | This coin's birth height is the same as `block_height` | N/A | = |
| ASSERT_EPHEMERAL | `(76)` | This coin was created in the same block as it is being spent | Current block | = |
| ASSERT_BEFORE_SECONDS_RELATIVE | `(84 seconds_passed)` | The previous transaction block's timestamp is < this coin's birth timestamp + `seconds_passed` | Previous transaction block | < |
| ASSERT_BEFORE_SECONDS_ABSOLUTE | `(85 seconds)` | The previous transaction block's timestamp is < `seconds` | Previous transaction block | < |
| ASSERT_BEFORE_HEIGHT_RELATIVE | `(86 block_height_passed)` | The previous transaction block's height is < this coin's birth height + `block_height_passed` | Previous transaction block | < |
| ASSERT_BEFORE_HEIGHT_ABSOLUTE | `(87 block_height)` | The previous transaction block's height is < `block_height` | Previous transaction block | < |

### Modified conditions

In addition, this CHIP will modify two existing conditions. The current implementation (prior to this CHIP) has a discrepancy between the `ASSERT_HEIGHT_*` the `ASSERT_SECONDS_*` checks. The timestamp conditions compare against the **current** block and the height conditions compare against the **previous** transaction block.

Comparing against the previous transaction block was deliberate, to prevent the farmer from having an incentive to manipulate the timestamp of the new block. The comparison against the current block in the `ASSERT_SECONDS_*` conditions was unintentional.

The two conditions with this discrepancy are:

| Current Condition | Format | Valid if | Compares against | cmp |
| :---------------------- | :------------------------- | :------------------------------------------------------------------------------ | :--------------- | :-- |
| ASSERT_SECONDS_RELATIVE | `(80 seconds_passed)` | The **current** block's timestamp >= coin's birth timestamp + `seconds_passed` | Current block | >= |
| ASSERT_SECONDS_ABSOLUTE | `(81 seconds)` | The **current** block's timestamp >= `seconds` | Current block | >= |

This CHIP's soft fork will include the following update to the behavior of these conditions:

| Updated Condition | Format | Valid if | Compares against | cmp |
| :---------------------- | :------------------------- | :------------------------------------------------------------------------------ | :--------------- | :-- |
| ASSERT_SECONDS_RELATIVE | `(80 seconds_passed)` | The **previous** block's timestamp >= coin's birth timestamp + `seconds_passed` | Previous block | >= |
| ASSERT_SECONDS_ABSOLUTE | `(81 seconds)` | The **previous** block's timestamp >= `seconds` | Previous block | >= |

These changes come with an additional advantage -- by changing the behavior to rely on the previous block's time stamp, the incentive for farmers to pick an inaccurate timestamp for the current block is also removed when these conditions exist in the mempool.

### Ephemeral coin spends

An ephemeral coin spend is a coin that is created and spent in the same block. The birth timestamp of a coin is the same as the timestamp of the block it was created in. The **current** implementation (prior to this CHIP) contains the following behaviors regarding ephemeral coin spends:
* `ASSERT_HEIGHT_RELATIVE 0` will fail because the previous transaction block's height is less than the current height plus zero.
* `ASSERT_SECONDS_RELATIVE 0` will succeed because the current block's time stamp is greater than or equal to the current block's timestamp plus zero.

If this CHIP is accepted:
* `ASSERT_HEIGHT_RELATIVE 0` will continue to fail; its functionality will not have changed.
* `ASSERT_SECONDS_RELATIVE 0` will fail because the previous block's time stamp is less than current block's timestamp plus zero.

Despite removing a farmer's incentive to modify a block's timestamp (as explained in the [Modified conditions](#modified-conditions) section), this incentive will still exist for ephemeral coin spends. This is because an ephemeral coin's birth timestamp is the same as the block's, and negative relative timestamp conditions could be used to depend on it. Because of this, two more changes will be made in this CHIP:
* Disallow relative time lock conditions on ephemeral coin spends (this applies when the time lock values are negative). This will also ensure that the current implementation of treating a negative value as a no-op remains valid.
* Add a new `ASSERT_EPHEMERAL` condition, which is listed in the [New conditions](#new-conditions) section. This condition will require a coin to have been created in the same block as it is being spent. See also the note concerning this condition in the [Security](#security) section.

## Reference Implementation

The following pull requests have been merged to the [Chia-Network/chia_rs](https://github.com/Chia-Network/chia_rs) GitHub repository as part of this CHIP:
* [131](https://github.com/Chia-Network/chia_rs/pull/131) - add a new `ENABLE_ASSERT_BEFORE` flag to enable support for the new `ASSERT` conditions
* [133](https://github.com/Chia-Network/chia_rs/pull/133) - add support for `ASSERT_CONCURRENT_SPEND`
* [134](https://github.com/Chia-Network/chia_rs/pull/134) - add support for `ASSERT_CONCURRENT_PUZZLE`
* [135](https://github.com/Chia-Network/chia_rs/pull/135) - add more checks for impossible constraints
* [140](https://github.com/Chia-Network/chia_rs/pull/140) - Fix edge cases in parsing `ASSERT_BEFORE_*`
* [144](https://github.com/Chia-Network/chia_rs/pull/144) - add support for `ASSERT_MY_BIRTH_HEIGHT` and `ASSERT_MY_BIRTH_SECONDS`
* [149](https://github.com/Chia-Network/chia_rs/pull/149) - Add `ASSERT_EPHEMERAL` condition
* [150](https://github.com/Chia-Network/chia_rs/pull/150) - assert not ephemeral (for relative timelocks)

The following pull requests have been merged to the [Chia-Network/chia-blockchain](https://github.com/Chia-Network/chia-blockchain) GitHub repository as part of this CHIP:
* [14625](https://github.com/Chia-Network/chia-blockchain/pull/14625) - Softfork2 infrastructure
* [14720](https://github.com/Chia-Network/chia-blockchain/pull/14720) - `ASSERT_MY_BIRTH_*` conditions
* [14733](https://github.com/Chia-Network/chia-blockchain/pull/14733) - `ASSERT_BEFORE_*` conditions

## Security

This CHIP comes with several security risks, all of which we feel are outweighed by the value added:

1. Chia's blockchain may already contain unspent coins with puzzles that contain one or more of the conditions from this CHIP. Any such coins will need to be spent before the fork point is reached, or they will become permanently unspendable. However, we feel that the risk of this happening is low, for two reasons:
* Before the fork point is reached, the conditions from this CHIP will be interpreted as no-ops, so they will always pass. Therefore, there is no reason for an existing coin to use any of these conditions, so it is unlikely that they exist.
* If such a coin does exist, it will still be spendable until the fork point. Developers of these coins will be given ample warning to spend them.

2. The mempool will require additional logic to remove any coin spends that are no longer valid. This means that the mempool will need to be re-calculated with each transaction block, thereby adding complexity. This new mempool logic will require extensive testing before it can be implemented on Chia's mainnet.

3. The new Chialisp conditions could be used in a coin's puzzle, rather than in its solution as intended. Any coins with puzzles that contain one of these new conditions will need to be spent while the condition is still valid. If the condition becomes invalid, the coin will be rendered unspendable. This could happen for any number of unintended reasons, including:
* If a coin will only be valid for one more block, a farmer could choose not to include that coin, thus permanently censoring it.
* A coin spend must include a sufficient blockchain fee for inclusion. If the provided fee is insufficient, the coin could become unspendable if one of the conditions from the puzzle expires.
* After a coin has been included in the blockchain, it could be removed in a re-org. In the case where a coin was spent on the final block where it remained valid, the re-org would then cause the coin to become unspendable.

In addition, coins that use the conditions from this CHIP in their puzzles could be created such that they are always invalid. For example:
* If the current block height is `100` and a coin is created with `(ASSERT_BEFORE_HEIGHT_ABSOLUTE 100)` in its puzzle, then at the time of the coin's creation, its last valid block height has already passed, so it will never be able to be spent.
* If a coin is created with a puzzle that contains contradicting conditions, such as `(ASSERT_BEFORE_HEIGHT_ABSOLUTE 100)` and `(ASSERT_HEIGHT_ABSOLUTE 100)`, then it can never be spent.

Finally, if the `ASSERT_EPHEMERAL` condition is used in a coin's puzzle, rather than its solution, a farmer may choose to include the creation -- but not the ephemeral spend -- of the coin in the new block. The coin will never be considered ephemeral again, so the `ASSERT_EPHEMERAL` condition will fail for all future attempts to spend the coin. Thus, the farmer will have forced the coin to be bricked.

In order to mitigate this risk, we will strongly recommend all Chialisp developers not to create any coins that include any of the conditions from this CHIP in their puzzles. The correct way to use these conditions is in the solution of a delegated puzzle. In this case, only the current spend of the coin could expire, but the coin itself would remain valid.

4. A coin spend that has expired could become spendable in a re-org. This could lead to unintended consequences for anyone monitoring the blockchain to determine when the coin spend becomes invalid. However, because re-orgs are rare in Chia, this is unlikely to lead to any security issues.


## Additional Assets

None

## Copyright
Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).