Skip to content

Latest commit

 

History

History
305 lines (223 loc) · 8.18 KB

sip-020-bitwise-ops.md

File metadata and controls

305 lines (223 loc) · 8.18 KB

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: Ratified

Created: 12 November 2022

License: CC0-1.0

Sign-off: Jason Schrader [email protected], Jesse Wiley [email protected], Jude Nelson [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