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

SIP-020 bitwise operators to Clarity 2 #106

Merged
merged 11 commits into from
Mar 21, 2023
305 changes: 305 additions & 0 deletions sips/sip-020/sip-020-bitwise-ops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
# Preamble

SIP Number: 020

Title: Bitwise Operations in Clarity

Authors: Cyle Witruk <https://github.com/cylewitruk>, Brice Dobry
<https://github.com/obycode>

Consideration: Technical, Governance

Type: Consensus

Status: Activation-in-Progress

Created: 12 November 2022

License: CC0-1.0

Sign-off: Jason Schrader <[email protected]>, Jesse Wiley <[email protected]>

Layer: Consensus (hard fork)

Discussions-To: https://github.com/stacksgov/sips

# Abstract

This SIP adds bitwise operations to the Clarity language which could simplify
the implementation of smart contracts that require manipulation of bits. The
changes include the addition of the following operations:

- Bitwise Xor (`bit-xor`)
- Bitwise And (`bit-and`)
- Bitwise Or (`bit-or`)
- Bitwise Not (`bit-not`)
- Binary Left Shift (`bit-shift-left`)
- Binary Right Shift (`bit-shift-right`)

# License and Copyright

This SIP is made available under the terms of the Creative Commons CC0 1.0
Universal license, available at
https://creativecommons.org/publicdomain/zero/1.0/ This SIP's copyright is held
by the Stacks Open Internet Foundation.

# Introduction

Bitwise operations are common in other programming languages. Common algorithms,
including many used in encryption, or the ability to set and check flags in a
bit field for example, would be much more difficult to implement without the use
of these operations. When executing a contract using these operations, the
common hardware on which miners and nodes are likely to be running can all
perform these operations very efficiently -- these are typically single cycle
operations. Note that the addition of these bitwise operations commits the
Clarity VM to using 2's complement to represent integers.

# Specification

## Bitwise Xor (`bit-xor`)

`(bit-xor i1 i2...)`

- **Inputs:** int, ... | uint, ...
- **Output:** int | uint

Returns the result of bitwise exclusive or'ing a variable number of integer
inputs.

| Bit in i1 | Bit in i2 | Bit in Output |
| --------- | --------- | ------------- |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |

The following example uses only 8 bits to make it easier to follow. Actual
Clarity values are 128 bits.

```
(bit-xor 17 30) ;; Return 15
;; Binary representation:
;; i1 (17): 00010001
;; i2 (30): 00011110
;; Output (15): 00001111
```

### Examples

```
(bit-xor 1 2) ;; Returns 3
(bit-xor 120 280) ;; Returns 352
(bit-xor -128 64) ;; Returns -64
(bit-xor u24 u4) ;; Returns u28
(bit-xor 1 2 4 -1) ;; Returns -8
```

## Bitwise And (`bit-and`)

`(bit-and i1 i2...)`

- **Inputs:** int, ... | uint, ...
- **Output:** int | uint

Returns the result of bitwise and'ing a variable number of integer inputs.

| Bit in i1 | Bit in i2 | Bit in Output |
| --------- | --------- | ------------- |
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |

The following example uses only 8 bits to make it easier to follow. Actual
Clarity values are 128 bits.

```
(bit-and 17 30) ;; Return 16
;; Binary representation:
;; i1 (17): 00010001
;; i2 (30): 00011110
;; Output (16): 00010000
```

### Examples

```
(bit-and 24 16) ;; Returns 16
(bit-and 28 24 -1) ;; Returns 24
(bit-and u24 u16) ;; Returns u16
(bit-and -128 -64) ;; Returns -128
(bit-and 28 24 -1) ;; Returns 24
```

## Bitwise Or (`bit-or`)

`(bit-or i1 i2...)`

- **Inputs:** int, ... | uint, ...
- **Outputs:** int | uint

Returns the result of bitwise inclusive or'ing a variable number of integer
inputs.

| Bit in i1 | Bit in i2 | Bit in Output |
| --------- | --------- | ------------- |
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 1 |

The following example uses only 8 bits to make it easier to follow. Actual
Clarity values are 128 bits.

```
(bit-or 17 30) ;; Return 31
;; Binary representation:
;; i1 (17): 00010001
;; i2 (30): 00011110
;; Output (31): 00011111
```

### Examples

```
(bit-or 4 8) ;; Returns 12
(bit-or 1 2 4) ;; Returns 7
(bit-or 64 -32 -16) ;; Returns -16
(bit-or u2 u4 u32) ;; Returns u38
```

## Bitwise Not (`bit-not`)

`(bit-not i1)`

- **Inputs:** int | uint
- **Output:** int | uint

Returns the one's compliment (sometimes also called the bitwise compliment or
not operator) of `i1`, effectively reversing the bits in `i1`.

In other words, every bit that is `1` in `ì1` will be `0` in the result.
Conversely, every bit that is `0` in `i1` will be `1` in the result.

| Bit in i1 | Bit in Output |
| --------- | ------------- |
| 0 | 1 |
| 1 | 0 |

The following example uses only 8 bits to make it easier to follow. Actual
Clarity values are 128 bits.

```
(bit-not u41) ;; Return u214
;; Binary representation:
;; i1 (41): 00101001
;; Output (214): 11010110
```

### Examples

```
(bit-not 3) ;; Returns -4
(bit-not u128) ;; Returns u340282366920938463463374607431768211327
(bit-not 128) ;; Returns -129
(bit-not -128) ;; Returns 127
```

## Bitwise Left Shift (`bit-shift-left`)

`(bit-shift-left i1 shamt)`

- **Inputs:** int, uint | uint, uint
- **Outputs:** int | uint

Shifts all bits in `i1` to the left by the number of places specified in `shamt`
modulo 128 (the bit width of Clarity integers). New bits are filled with zeros.

Note that there is a deliberate choice made to ignore arithmetic overflow for
this operation. In use cases where overflow should be detected, developers
should use `*`, `/`, and `pow` instead of the shift operators.

The following example uses only 8 bits to make it easier to follow. Actual
Clarity values are 128 bits.

```
(bit-shift-left 6 u3) ;; Return 48
;; Binary representation:
;; Input (6): 00000110
;; Output (48): 00110000
```

### Examples

```
(bit-shift-left 16 u2) ;; Returns 64
(bit-shift-left -64 u1) ;; Returns -128
(bit-shift-left u4 u2) ;; Returns u16
(bit-shift-left 123 u9999999999) ;; Returns -170141183460469231731687303715884105728 (== 123 bit-shift-left 127)
(bit-shift-left u123 u9999999999) ;; Returns u170141183460469231731687303715884105728 (== u123 bit-shift-left 127)
(bit-shift-left -1 u7) ;; Returns -128
(bit-shift-left -1 u128) ;; Returns -1
```

## Bitwise Right Shift (`bit-shift-right`)

`(bit-shift-right i1 shamt)`

- **Inputs:** int, uint | uint, uint
- **Output:** int | uint

Shifts all the bits in `i1` to the right by the number of places specified in
`shamt` modulo 128 (the bit width of Clarity integers). When `i1` is a `uint`
(unsigned), new bits are filled with zeros. When `i1` is an `int` (signed), the
sign is preserved, meaning that new bits are filled with the value of the
previous sign-bit.

Note that there is a deliberate choice made to ignore arithmetic overflow for
this operation. In use cases where overflow should be detected, developers
should use `*`, `/`, and `pow` instead of the shift operators.

The following example uses only 8 bits to make it easier to follow. Actual
Clarity values are 128 bits.

```
(bit-shift-right u170 u1) ;; Return u85
;; Binary representation:
;; Input (u170): 10101010
;; Output (u85): 01010101
```

### Examples

```
(bit-shift-right 2 u1) ;; Returns 1
(bit-shift-right 128 u2) ;; Returns 32
(bit-shift-right -64 u1) ;; Returns -32
(bit-shift-right u128 u2) ;; Returns u32
(bit-shift-right 123 u9999999999) ;; Returns 0
(bit-shift-right u123 u9999999999) ;; Returns u0
(bit-shift-right -128 u7) ;; Returns -1
(bit-shift-right -256 u1) ;; Returns -128
(bit-shift-right 5 u2) ;; Returns 1
(bit-shift-right -5 u2) ;; Returns -2
```

# Related work

Not applicable

# Backwards Compatibility

Because this SIP introduces new Clarity operators, it is a consensus-breaking
change. A contract that uses one of these new operators would be invalid before
this SIP is activated, and valid after it is activated.

# Activation

This SIP will be a rider on SIP-015. It will be considered activated if and only
if SIP-015 (and Stacks 2.1) is activated.

# Reference Implementations

- https://github.com/stacks-network/stacks-blockchain/pull/3389
- See also discussions in
https://github.com/stacks-network/stacks-blockchain/pull/3382