Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 142 additions & 0 deletions kip-0017.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
```
  KIP: 17
  Layer: Consensus, Script Engine
  Title: Covenants and Improved Scripting Capabilities
  Authors: Ori Newman <[email protected]>
  Status: Draft
```

## Abstract

This proposal is a continuation of KIP 10 [1], introducing full covenant support to Kaspa by extending the scripting language with introspection opcodes to all transaction fields, along with byte-string manipulation primitives. These additions enable scripts to inspect and constrain properties of the spending transaction, allowing validation of stateful transitions encoded implicitly or explicitly in UTXOs.

## Specification

### 1. New Opcodes

The following new opcodes are introduced to enhance script functionality:

#### Transaction Level Introspection Opcodes:

1. `OpTxVersion` (0xb2): Returns the version of the transaction.
2. `OpTxLockTime` (0xb5): Returns the locktime field of the transaction.
3. `OpTxSubnetId` (0xb6): Returns the subnetwork ID of the transaction.
4. `OpTxGas` (0xb7): Returns the gas field of the transaction.
5. `OpTxPayloadLen` (0xc4): Returns the payload length of the transaction.
6. `OpTxPayloadSubstr` (0xb8): Returns the payload substring of the transaction from `start` to `end`. Returns error for invalid ranges, or when `end-start > MAX_SCRIPT_ELEMENT_SIZE = 520`.

#### Input/Output Introspection Opcodes:

1. `OpTxInputSpkLen(idx)`: (0xc5) Returns the script public key length of the specified input.
2. `OpTxInputSpkSubstr(idx, start, end)` (0xc6): Returns the script public key substring of the specified input from `start` to `end`. Returns error for invalid ranges, or when `end-start > MAX_SCRIPT_ELEMENT_SIZE = 520`.
3. `OpTxOutputSpkLen(idx)`: (0xc7) Returns the script public key length of the specified output.
4. `OpTxOutputSpkSubstr(idx, start, end)`: (0xc8) Returns the script public key substring of the specified output from `start` to `end`. Returns error for invalid ranges, or when `end-start > MAX_SCRIPT_ELEMENT_SIZE = 520`.
5. `OpTxInputScriptSigLen(idx)` (0xc9): Returns the script signature length of the input.
6. `OpTxInputScriptSigSubstr(idx, start, end)`: (0xca) Returns the script signature substring of the input from `start` to `end`. Returns error for invalid ranges, or when `end-start > MAX_SCRIPT_ELEMENT_SIZE = 520`.
7. `OpOutpointTxId(idx)` (0xba): Returns the transaction ID of the outpoint.
8. `OpOutpointIndex` (0xbb): Returns the index of the outpoint.
9. `OpTxInputSeq(idx)` (0xbd): Returns the sequence number of the input.
10. `OpTxInputIsCoinbase(idx)` (0xc1): Returns whether the input is a coinbase.

##### Note on Substring Opcodes

On all substring related opcodes, we consider the first byte to be at position 0, the start position is inclusive, and the end position is exclusive.

#### Other Opcodes:

1. `OpCat(str1, str2)`: (0x7e) Concatenates two byte strings. Returns error if the resulting string exceeds `MAX_SCRIPT_ELEMENT_SIZE = 520` [2].
2. `OpSubstr(str, start, end)`: (0x7f) Returns the substring of a byte string (from start (inclusive) until end (exclusive)). Returns error for invalid ranges, or when `end-start > MAX_SCRIPT_ELEMENT_SIZE = 520`.
3. `OpBlake2bWithKey(data, key)`: (0xa7) Returns the Blake2b hash of the data with the key used for domain separation.
4. `OpInvert(str)` (0x83): Returns the bitwise NOT of a byte string.
5. `OpAnd(str1, str2)` (0x84): Returns the bitwise AND of two byte strings. Returns error if the two strings are of different lengths.
6. `OpOr(str1, str2)` (0x85): Returns the bitwise OR of two byte strings. Returns error if the two strings are of different lengths.
7. `OpXor(str1, str2)` (0x86): Returns the bitwise XOR of two byte strings. Returns error if the two strings are of different lengths.
8. `OpMul` (0x95): Returns the product of two numbers. Returns error on overflow.
9. `OpDiv` (0x96): Returns the quotient of two numbers. Returns error on division by zero.
10. `OpMod` (0x97): Returns the remainder of two numbers. Returns error on modulo by zero.

### 2. Activation

The features introduced in this KIP are activated based on DAA score:

1. Prior to activation:
- New opcodes are treated as invalid
2. After activation:
- All new opcodes become available

## Motivation

The combination of introspection opcodes and OP_CAT/OP_SUBSTR can be used to implictly attach state to UTXOs and enforce correct state transitions.

For example, we can implement a simple covenant that requires us to increase a counter stored in the transaction payload every time the UTXO is spent, and forces us to send the new transaction to the same scriptPubKey:

Given siganture script of the form: `<PREV_TX_REST> <PREV_TX_PAYLOAD>`, the scriptPubKey will be:

```
OpDup OpRot OpRot OpCat
"TransactionID" OpBlake2bWithDomain
OpTxInputIndex OpOutpointTxId OpEqualVerify
Op1Add
0 OpTxPayloadLen OpTxPayloadSubstr
OpEqualVerify
OpTxInputIndex OpTxInputSpk 0 OpOutputSpk
OpEqualVerify
OpTxOutputCount 1 OpEqual
```

This script can be written in pseudocode as:

```
validate(prev_tx,tx){
return hash(prev_tx) == tx.inputs[curr_idx].prev_tx_id
AND tx.outputs[0].script_pub_key == prev_tx.inputs[curr_idx].script_pub_key
AND len(tx.outputs) == 1
AND tx.payload == prev_tx.payload + 1;
}
```

In general, once we can encode some state transition function δ in script, we can validate that `δ(prev_payload,tx) = new_payload`, where δ can also introduce some constraints on `tx` using introspection opcodes (checking its signautre, enforcing a locktime, etc).

### Note on Transaction Encoding

Many covenant constructions require the spender to provide a full copy of the previous transaction as witness data in order to validate that
`tx.inputs[idx].prev_tx_id = hash(prev_tx)`.

As a result, the transaction ID hashing algorithm implicitly defines a canonical transaction encoding that scripts can rely on. Covenant scripts may reconstruct or inspect this encoding to verify that a provided previous transaction matches the referenced outpoint.

A reference implementation of the transaction ID hashing and encoding logic is provided in [3].

## Use Cases

Covenants enable a variety of use cases, including but not limited to:

1. Fungible and non-fungible tokens [4].
2. Smart Vaults for enhanced security [5].
3. Congestion control mechanisms [6].
4. With the help of ZK opcodes, implement L1-L2 trustless bridges.

See [7] for more use cases.

## Backwards Compatibility

This proposal requires a hard fork, as it introduces new opcodes to the scripting language. Older software will require an update to support these new features. Existing scripts and addresses remain valid, but cannot use the new functionality without being updated.

## Reference Implementation

https://github.com/kaspanet/rusty-kaspa/pull/797

## References

1. KIP 10: [kip-0010.md](kip-0010.md)

2. `OP_CAT` BIP: https://github.com/bip420/bip420

3. Reference implemenation of transaction ID hashing: https://github.com/kaspanet/rusty-kaspa/blob/2ccc9e4ca9aa184c62c4fb14dbb463834264e01d/consensus/core/src/hashing/tx.rs#L40

4. Cat Protocol for covenants based tokens: https://catprotocol.org/

5. Vaults: https://utxos.org/uses/vaults/

6. Congestion Control: https://utxos.org/uses/scaling/

7. More covenants use cases: https://utxos.org/uses/, https://github.com/sCrypt-Inc/awesome-op-cat#op_cat-use-cases, https://covenants.info/use-cases/