From 5b6d8abc3d9395e2e0877a96822bfb272ae5d7b9 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Thu, 16 May 2024 12:14:04 +1200 Subject: [PATCH 01/14] Initial draft --- CIP-XXXX/CIP-XXXX.md | 62 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 CIP-XXXX/CIP-XXXX.md diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md new file mode 100644 index 000000000..fee0ae69a --- /dev/null +++ b/CIP-XXXX/CIP-XXXX.md @@ -0,0 +1,62 @@ +--- +CIP: ???? +Title: Bitwise operations +Category: Plutus +Status: Proposed +Authors: + - Koz Ross +Implementors: + - Koz Ross +Discussions: + - https://github.com/cardano-foundation/CIPs/pull/? +Created: 2024-05-16 +License: Apache-2.0 +--- + +## Abstract + + +## Motivation: why is this CIP necessary? + + +## Specification + + +## Rationale: how does this CIP achieve its goals? + + +## Path to Active + +### Acceptance Criteria + +We consider the following criteria to be essential for acceptance: + +* A proof-of-concept implementation of the operations specified in this + document, outside of the Plutus source tree. The implementation must be in + GHC Haskell, without relying on the FFI. +* The proof-of-concept implementation must have tests, demonstrating that it + behaves as the specification requires. +* The proof-of-concept implementation must demonstrate that it will + successfully build, and pass its tests, using all GHC versions currently + usable to build Plutus (8.10, 9.2 and 9.6 at the time of writing), across + all [Tier 1][tier-1] platforms. + +Ideally, the implementation should also demonstrate its performance +characteristics by well-designed benchmarks. + +### Implementation Plan + +MLabs has begun the implementation of the [proof-of-concept][impl] as required in +the acceptance criteria. Upon completion, we will send a pull request to +Plutus with the implementation of the primitives for Plutus Core, mirroring +the proof-of-concept. + +## Copyright + +This CIP is licensed under [Apache-2.0](http://www.apache.org/licenses/LICENSE-2.0). + +[tier-1]: https://gitlab.haskell.org/ghc/ghc/-/wikis/platforms#tier-1-platforms +[impl]: https://github.com/mlabs-haskell/plutus-integer-bytestring/tree/koz/milestone-2 From affca65dc5be01ab3e7c39be2d5e4524a19ac1a8 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Thu, 16 May 2024 13:34:13 +1200 Subject: [PATCH 02/14] Semantics --- CIP-XXXX/CIP-XXXX.md | 111 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md index fee0ae69a..3ebecfbf5 100644 --- a/CIP-XXXX/CIP-XXXX.md +++ b/CIP-XXXX/CIP-XXXX.md @@ -20,7 +20,115 @@ License: Apache-2.0 ## Specification - + +We describe the proposed operations in several stages. First, we give an +overview of the proposed operations' signatures and costings; second, we +describe the semantics of each proposed operation in detail, as well as some +examples. Lastly, we provide laws that any implementation of the proposed +operations should obey. + +Throughout, we make use of the [bit indexing scheme][bit-indexing-scheme] +described in a prior CIP. We also use the notation $x[i]$ to refer to the value +of at bit index $i$ of $x$, and the notation $x\\{i\\}$ to refer to the byte at +byte index $i$ of $x$. + +### Operation semantics + +Our proposed operations will have the following signatures: + +* ``bitwiseShift :: BuiltinByteString -> BuiltinInteger -> BuiltinByteString`` +* ``bitwiseRotate :: BuiltinByteStirng -> BuiltinInteger -> BuiltinByteString`` +* ``countSetBits :: BuiltinByteString -> BuiltinInteger`` +* ``findFirstSetBit :: BuiltinByteString -> BuiltinInteger`` + +We assume the following costing, for both memory and execution time: + +| Operation | Cost | +|-----------|------| +|`bitwiseShift`| Linear in the `BuiltinByteString` argument | +|`bitwiseRotate` | Linear in the `BuiltinByteString` argument | +|`countSetBits` | Linear in the argument | +|`findFirstSetBit` | Linear in the argument | + +#### `bitwiseShift` + +`bitwiseShift` takes two arguments; we name and describe them below. + +1. The `BuiltinByteString` to be shifted. This is the _data argument_. +2. The shift, whose sign indicates direction and whose magnitude indicates the + size of the shift. This is the _shift argument_, and has type + `BuiltinInteger`. + +Let $b$ refer to the data argument, whose length in bytes is $n$, and let $i$ +refer to the shift argument. Let the result of `bitwiseShift` called with $b$ +and $i$ be $b_r$, also of length $n$. + +For all $j \in 0, 1, \ldots 8 \cdot n - 1$, we have + +$$ +b_r[j] = \begin{cases} + b[j - i] & \text{if } j - i \in 0, 1, \ldots 8 \cdot n - 1\\ + 0 & \text{otherwise} \\ + \end{cases} +$$ + +Some examples of the intended behaviour of `bitwiseShift` follow. For +brevity, we write `BuiltinByteString` literals as lists of hexadecimal values. + +TODO: Examples + +#### `bitwiseRotate` + +`bitwiseRotate` takes two arguments; we name and describe them below. + +1. The `BuiltinByteString` to be rotated. This is the _data argument_. +2. The rotation, whose sign indicates direction and whose magnitude indicates + the size of the rotation. This is the _rotation argument_, and has type + `BuiltinInteger`. + +Let $b$ refer to the data argument, whose length in bytes is $n$, and let $i$ +refer to the rotation argument. Let the result of `bitwiseRotate` called with $b$ +and $i$ be $b_r$, also of length $n$. + +For all $j \in 0, 1, \ldots 8 \cdot n - 1$, we have $b_r = b[j - i \mod 8 \cdot n]$. + +Some examples of the intended behaviour of `bitwiseRotate` follow. For +brevity, we write `BuiltinByteString` literals as lists of hexadecimal values. + +TODO: Examples + +#### `countSetBits` + +Let $b$ refer to `countSetBits`' only argument, whose length in bytes is $n$, +and let $r$ be the result of calling `countSetBits` on $b$. Then we have + +$$ +r = \sum_{i=0}^{8 \cdot n - 1} b[i] +$$ + +Some examples of the intended behaviour of `countSetBits` follow. For +brevity, we write `BuiltinByteString` literals as lists of hexadecimal values. + +TODO: Examples + +#### `findFirstSetBit` + +Let $b$ refer to `findFirstSetBit`'s only argument, whose length in bytes is $n$, +and let $r$ be the result of calling `findFirstSetBit` on $b$. Then we have the +following: + +1. $r \in -1, 0, 1, \ldots, 8 \cdot n - 1$ +2. If for all $i \in 0, 1, \ldots n - 1$, $b\\{i\\} = \texttt{0x00}$, then $r = -1$; + otherwise, $r > -1$. +3. If $r > -1$, then $b[r] = 1$, and for all $i \in 0, 1, \ldots, r - 1$, $b[i] + = 0$. + +Some examples of the intended behaviour of `findFirstSetBit` follow. For +brevity, we write `BuiltinByteString` literals as lists of hexadecimal values. + +TODO: Examples + +### Laws ## Rationale: how does this CIP achieve its goals? + +We describe the semantics of a set of bitwise operations for Plutus +`BuiltinByteString`s. Specifically, we provide descriptions for: + +* Bit shifts and rotations +* Counting the number of set bits (`popcount`) +* Finding the first set bit + +We base our work on similar operations described in [CIP-58][cip-58], but use +the bit indexing scheme from [the logical operations cip][logic-cip] for the +semantics. This is intended as follow-on work from both of these. ## Motivation: why is this CIP necessary? - + +TODO: Lead-in + +To demonstrate why these operations would be useful, we re-use the cases +provided in the [logical operations CIP][logic-cip], and show why the operations +we describe would be beneficial. + +### Case 1: integer set + +For integer sets, the [previous description][integer-set] lacks two important, +and useful, operations: + +* Given an integer set, return its cardinality; and +* Given an integer set, return its minimal member (or specify it is empty). + +These operations have a range of uses. The first corresponds to the notion of +[Hamming weight][hamming-weight], which can be used for operations ranging from +representing boards in chess games to exponentiation by squaring to succinct +data structures. Together with bitwise XOR, it can also compute the [Hamming +distance][hamming-distance]. The second operation also has a [range of +uses][ffs-uses], ranging from succinct priority queues to integer normalization. +It is also useful for [rank-select dictionaries][rank-select-dictionary], a +succinct structure that can act as the basis of a range of others, such as +dictionaries, multisets and trees of different arity. + +In all of the above, these operations need to be implemented efficiently to be +useful. While we could use only bit reading to perform all of these, it is +extremely inefficient: given an input of length $n$, assuming that any bit +distribution is equally probable, we need $\Theta(8 \cdot n)$ time in the +average case. While it is +impossible to do both of these operations in sub-linear time in general, the +large constant factors this imposes (as well as the overhead of looping over +_bit_ indexes) is a cost we can ill afford on-chain, especially if the goal is +to use these operations as 'building blocks' for something like a data +structure. + +### Case 2: hashing + +In our [previously-described][hashing] case, we stated what operations we would +need for the Argon2 family of hashes specifically. However, Argon2 has a +specific advantage in that the number of operations it requires are both +relatively few, and the most complex of which (BLAKE2b hashing) already exists +in Plutus Core as a primitive. However, other hash functions (and indeed, many +other cryptographic primitives) rely on two other important instructions: bit +shifts and bit rotations. As an example, consider SHA512, which is an important +component in several cryptographic protocols (including Ed25519 signature +verification): its implementation requires both shifts and rotations to work. + +Like with Case 1, we can theoretically simulate both rotations and shifts using +a combination of bit reads and bit writes to an empty `BuiltinByteString`. +However, the cost of this is extreme: we would need to produce a list of +index-value pairs of length equal to the Hamming weight of the input, only to +then immediately discard it! To put this into some perspective, for an 8-byte +input, performing a rotation involves allocating an expected 32 index-value +pairs, using _significantly_ more memory than the result. On-chain, we can't +really afford this cost, especially in an operation intended to be used as part +of larger constructions (as would be necessary here). ## Specification @@ -302,10 +368,19 @@ readBit bs i = False ``` ## Rationale: how does this CIP achieve its goals? - +TODO: Add note about how we can implement this using only reads and writes, but +why it won't end well. + +TODO: Talk about why we need all four, especially popcount and ffs. + +TODO: Compare with CIP-58, although we're basically identical anyway. + +TODO: Mention that our shifts and rotates line up with what Data.Bits does. + +TODO: Talk about the -1 versus bitlen choice for ffs and why we chose the first. + +TODO: Why did we choose to omit clz? ## Path to Active @@ -343,3 +418,10 @@ This CIP is licensed under [Apache-2.0](http://www.apache.org/licenses/LICENSE-2 [monoid-homomorphism]: https://en.wikipedia.org/wiki/Monoid#Monoid_homomorphisms [logic-cip]: https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-XXX/CIP-XXX.md [include-exclude]: https://en.wikipedia.org/wiki/Inclusion%E2%80%93exclusion_principle +[cip-58]: https://github.com/cardano-foundation/CIPs/tree/master/CIP-0058 +[integer-set]: https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-XXX/CIP-XXX.md#case-1-integer-set +[hamming-weight]: https://en.wikipedia.org/wiki/Hamming_weight#History_and_usage +[hamming-distance]: https://en.wikipedia.org/wiki/Hamming_distance +[ffs-uses]: https://en.wikipedia.org/wiki/Find_first_set#Applications +[rank-select-dictionary]: https://en.wikipedia.org/wiki/Succinct_data_structure#Succinct_indexable_dictionaries +[hashing]: https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-XXX/CIP-XXX.md#case-2-hashing From 9bae3623833784fbbda7e3c5390fb98ad8a6fc95 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Tue, 21 May 2024 09:44:28 +1200 Subject: [PATCH 07/14] Correct references to CIP-122 --- CIP-XXXX/CIP-XXXX.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md index a17f0bfdb..cfdcbedd8 100644 --- a/CIP-XXXX/CIP-XXXX.md +++ b/CIP-XXXX/CIP-XXXX.md @@ -31,7 +31,7 @@ semantics. This is intended as follow-on work from both of these. TODO: Lead-in To demonstrate why these operations would be useful, we re-use the cases -provided in the [logical operations CIP][logic-cip], and show why the operations +provided in the [CIP-122][logic-cip], and show why the operations we describe would be beneficial. ### Case 1: integer set @@ -416,7 +416,7 @@ This CIP is licensed under [Apache-2.0](http://www.apache.org/licenses/LICENSE-2 [impl]: https://github.com/mlabs-haskell/plutus-integer-bytestring/tree/koz/milestone-2 [bit-indexing-scheme]: https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-XXX/CIP-XXX.md#bit-indexing-scheme [monoid-homomorphism]: https://en.wikipedia.org/wiki/Monoid#Monoid_homomorphisms -[logic-cip]: https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-XXX/CIP-XXX.md +[logic-cip]: https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-0122/CIP-0122.md [include-exclude]: https://en.wikipedia.org/wiki/Inclusion%E2%80%93exclusion_principle [cip-58]: https://github.com/cardano-foundation/CIPs/tree/master/CIP-0058 [integer-set]: https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-XXX/CIP-XXX.md#case-1-integer-set From 1bef8c871f362769e0776c09858f35f6ffe93283 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Tue, 21 May 2024 13:48:17 +1200 Subject: [PATCH 08/14] More intro stuff --- CIP-XXXX/CIP-XXXX.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md index cfdcbedd8..abf5b1adb 100644 --- a/CIP-XXXX/CIP-XXXX.md +++ b/CIP-XXXX/CIP-XXXX.md @@ -28,9 +28,16 @@ semantics. This is intended as follow-on work from both of these. ## Motivation: why is this CIP necessary? -TODO: Lead-in - -To demonstrate why these operations would be useful, we re-use the cases +Bitwise operations, both over fixed-width and variable-width blocks of bits, +have a range of uses. Indeed, we have already proposed [CIP-122][logic-cip], +with some example cases, and a range of primitive operations on +`BuiltinByteString`s designed to allow bitwise operations in the service of +those example cases, as well as many others. These operations form a core of +functionality, which is important and necessary, but not complete. We believe +that the operations we describe in this CIP form a useful 'completion' of the +work in CIP-122, based on similar work done in the [earlier CIP-58][cip-58]. + +To demonstrate why our proposed operations are useful, we re-use the cases provided in the [CIP-122][logic-cip], and show why the operations we describe would be beneficial. @@ -94,9 +101,9 @@ examples. Lastly, we provide laws that any implementation of the proposed operations should obey. Throughout, we make use of the [bit indexing scheme][bit-indexing-scheme] -described in a prior CIP. We also use the notation $x[i]$ to refer to the value +described in a CIP-122. We also re-use the notation $x[i]$ to refer to the value of at bit index $i$ of $x$, and the notation $x\\{i\\}$ to refer to the byte at -byte index $i$ of $x$. +byte index $i$ of $x$: both are specified in CIP-122. ### Operation semantics @@ -322,6 +329,13 @@ countSetBits "" = 0 countSetBits (x <> y) = countSetBits x + countSetBits y ``` +`countSetBits` also demonstrates that `bitwiseRotate` indeed preserves the +number of set (and thus clear) bits: + +```haskell +countSetBits bs = countSetBits (bitwiseRotate bs i) +``` + There is also a relationship between the result of `countSetBits` on a given argument and its complement: From 73b3adaa19c6e28ac9783c489853b233ee46b5d5 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Wed, 22 May 2024 10:28:49 +1200 Subject: [PATCH 09/14] Justification --- CIP-XXXX/CIP-XXXX.md | 131 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 124 insertions(+), 7 deletions(-) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md index abf5b1adb..71739b76d 100644 --- a/CIP-XXXX/CIP-XXXX.md +++ b/CIP-XXXX/CIP-XXXX.md @@ -383,18 +383,134 @@ readBit bs i = False ## Rationale: how does this CIP achieve its goals? -TODO: Add note about how we can implement this using only reads and writes, but -why it won't end well. +Our four operations, along with their semantics, fulfil the requirements of both +our cases, and are law-abiding, familiar, consistent and straightforward to +implement. Furthermore, they relate directly to operations provided by +[CIP-122][logic-cip], as well as being identical to the equivalent operations in +[CIP-58][cip-58]. At the same time, some alternative choices could have been +made: + +* Not implementing these operations at all, instead requiring higher-level APIs + to implement them atop CIP-122 primitives; +* Providing only some of these four operations; +* Having `findFirstSetBit` return the bit length for an all-zero argument, + instead of `-1`; +* Including a way of finding the _last_ set bit as well. + +We discuss our choices with regards to all the above, and specify why we made +the choices that we did. + +While we chose to provide all of these operations as primitives, we could +instead have required higher-level APIs to provide these on the basis +of [CIP-122][logic-cip] bit reads and writes. This is indeed possible: + +* `bitwiseShift` and `bitwiseRotate` is a loop over all bits in the data argument, + which is used to construct a change list that sets appropriate bits (with the + right offset), which then gets applied to a `BuiltinByteString` of the same + length as the data argument, but full of zero bytes. +* `countSetBits` is a fold over all bit indexes, incrementing an accumulator + every time a set bit is found. +* `findFirstSetBit` is a fold over all bit indexes, returning the first index + with a set bit, or (-1) if none can be found. + +However, especially for `bitwiseShift` and `bitwiseRotate`, this comes at +significant cost. For shifts and rotations, we have to construct a change list +argument whose length is proportional to the Hamming weight of the data +argument. Assuming that all inputs are equally likely, this is proportional to +the bit length of the data argument: such a change list is likely significantly +larger than the result of the shift or rotation, making the memory cost of such +operations far higher than it needs to be. Given the constraints on memory and +execution time on-chain, at minimum, these two operations would have to be +primitives to make them viable at all: Case 2-style implementations would have +intolerably large memory costs otherwise, as such algorithms often make heavy +use of both shifts and rotations. + +The case for `countSetBits` and `findFirstSetBit` is less clear here, as they +wouldn't require nearly as much of a memory cost if implemented atop CIP-122 +primitives using folds. However, the cost would still be significant: +`countSetBits` requires looping over every bit index in the argument, and +`findFirstSetBit` (again, assuming any bit distribution in an argument is +equally probable) requires looping over about half this many. This would make +these operations unreasonable even over smaller inputs, which would make +applications like rank-select dictionaries (which expect these operations to be +fast and low-cost) unworkable. As succinct data structures are foremost in our +minds when considering the current primitives, we believe it is important that +`countSetBits` and `findFirstSetBit` are as efficient as they could be, hence +their inclusion. + +When designing our operations, we tried to keep them familiar to Haskellers, +namely by having them behave like the similar operations from the `Bits` and +`FiniteBits` type classes. In particular, +`bitwiseShift` and `bitwiseRotate` act similarly given the same shift (or +rotation) argument, as positive values shift left and negative ones shift +right. The only exception is the choice for `findFirstSetBit` to return `-1` +when no set bits are found, which runs counter to the way `countTrailingZeros` +from `FiniteBits` works, which instead returns the bit length. This is also +different to how this operations [works in hardware][ctz]. Indeed, having +`findFirstSetBit` work this way would not only be more familiar, it would also +provide additional laws: -TODO: Talk about why we need all four, especially popcount and ffs. +```haskell +-- Not possible under our current definition +-- bitLen is the bit length of the argument +0 <= popcount bs <= bitLen bs - findFirstSet bs +``` -TODO: Compare with CIP-58, although we're basically identical anyway. +Under both definitions, the intent is the same: if the argument doesn't contain +any set bits, produce an invalid index. The difference is that we choose to +produce a _negative_ invalid index, whereas the consensus is to produce a +_non-negative_ invalid index instead. However, one task that is likely to come +up frequently when using `findFirstSetBit` is checking whether the index we were +given was valid or not (essentially, whether the argument has any nonzero bytes +in it). Under our definition, all that would be required is -TODO: Mention that our shifts and rotates line up with what Data.Bits does. +```haskell +let found = findFirstSetBit bs + in if found < 0 + then weMissed + else validIndex +``` -TODO: Talk about the -1 versus bitlen choice for ffs and why we chose the first. +This is a cheap operation in Plutus Core, requiring only comparing against a +constant. However, if we used the more widely-used definition, we would instead +have to do this: + +```haskell +let found = findFirstSetBit bs + bitLen = 8 * sizeOfByteString bs + in if found >= bitLen + then weMissed + else validIndex +``` -TODO: Why did we choose to omit clz? +This requires us to do considerably more work (finding the length of the +argument, multiplying by 8, then compare against that result), and is also much +more prone to error: users have to remember to use a `>=` comparison, as well as +to multiply the argument length by 8. This is less of an issue with +implementations of this operation in other languages, as their equivalent +operations are designed for fixed-width arguments (indeed, `FiniteBits` +_requires_ this), which makes their bit length a constant. In our case, this +isn't as simple, as `BuiltinByteString`s have variable length, which would make +the cost described above unavoidable. Our solution is both more efficient and +less error-prone: all a user needs to remember is that invalid indexes from +`findFirstSetBit` are negative. On this basis, we decided to vary from +conventional approaches. + +One notable omission from our operators is the equivalent of counting _leading_ +zeroes: namely, an operation that would find the _last_ set bit. Typically, both +a count of leading _and_ trailing zeroes is provided in both hardware and +software: this is the case for `FiniteBits`, as well as [most hardware +implementations][ctz]. To relate this to our cases, specifically Case 1, this +would allow us to efficiently find the _largest_ element in an integer set. The +reason we omit this operation is because, when compared with counting trailing +zeroes, it is far less useful: while it can be used for computing fast integer +square roots, it lacks many other uses. Counting trailing zeroes, on the other +hand, is essential for rank-select dictionaries (specifically for the `select` +operation), and also enables a range of other uses, which we have already +mentioned. In order to limit the number of new primitives, we decided that +counting leading zeroes can be omitted for now. However, our design doesn't +preclude such an operation from being added later if a use case for it is found +to be useful. ## Path to Active @@ -439,3 +555,4 @@ This CIP is licensed under [Apache-2.0](http://www.apache.org/licenses/LICENSE-2 [ffs-uses]: https://en.wikipedia.org/wiki/Find_first_set#Applications [rank-select-dictionary]: https://en.wikipedia.org/wiki/Succinct_data_structure#Succinct_indexable_dictionaries [hashing]: https://github.com/mlabs-haskell/CIPs/blob/koz/logic-ops/CIP-XXX/CIP-XXX.md#case-2-hashing +[ctz]: https://en.wikipedia.org/wiki/Find_first_set#Hardware_support From 7978c3ea2950a381358b849c01e924870d7654fb Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Thu, 23 May 2024 09:15:25 +1200 Subject: [PATCH 10/14] Link to discussion, fix YAML issues --- CIP-XXXX/CIP-XXXX.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md index 71739b76d..cf0388eb0 100644 --- a/CIP-XXXX/CIP-XXXX.md +++ b/CIP-XXXX/CIP-XXXX.md @@ -1,5 +1,5 @@ --- -CIP: ???? +CIP: ? Title: Bitwise operations Category: Plutus Status: Proposed @@ -8,7 +8,7 @@ Authors: Implementors: - Koz Ross Discussions: - - https://github.com/cardano-foundation/CIPs/pull/? + - https://github.com/cardano-foundation/CIPs/pull/825 Created: 2024-05-16 License: Apache-2.0 --- From 3340b631d61948e82a6d681b641aa3ac8274bb09 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Mon, 10 Jun 2024 13:36:12 +1200 Subject: [PATCH 11/14] Fix formatting, add more laws --- CIP-XXXX/CIP-XXXX.md | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md index cf0388eb0..f410af3b2 100644 --- a/CIP-XXXX/CIP-XXXX.md +++ b/CIP-XXXX/CIP-XXXX.md @@ -1,5 +1,5 @@ --- -CIP: ? +CIP: Title: Bitwise operations Category: Plutus Status: Proposed @@ -117,11 +117,9 @@ Our proposed operations will have the following signatures: We assume the following costing, for both memory and execution time: | Operation | Execution time cost | Memory cost | -|-----------|------| -|`bitwiseShift`| Linear in the `BuiltinByteString` argument | As execution time -| -|`bitwiseRotate` | Linear in the `BuiltinByteString` argument | As execution -time | +|-----------|---------------------|-------------| +|`bitwiseShift`| Linear in the `BuiltinByteString` argument | As execution time| +|`bitwiseRotate` | Linear in the `BuiltinByteString` argument | As execution time | |`countSetBits` | Linear in the argument | Constant | |`findFirstSetBit` | Linear in the argument | Constant | @@ -180,7 +178,8 @@ Let $b$ refer to the data argument, whose length in bytes is $n$, and let $i$ refer to the rotation argument. Let the result of `bitwiseRotate` called with $b$ and $i$ be $b_r$, also of length $n$. -For all $j \in 0, 1, \ldots 8 \cdot n - 1$, we have $b_r = b[j - i \mod 8 \cdot n]$. +For all $j \in 0, 1, \ldots 8 \cdot n - 1$, we have +$b_r = b[(j - i) \text{ } \mathrm{mod} \text { } (8 \cdot n)]$. Some examples of the intended behaviour of `bitwiseRotate` follow. For brevity, we write `BuiltinByteString` literals as lists of hexadecimal values. @@ -352,8 +351,8 @@ countSetBits (bitwiseLogicalXor False x y) = countSetBits (bitwiseLogicalOr False x y) - countSetBits (bitwiseLogicalAnd False x y) ``` -Lastly, `countSetBits` has a relationship to bitwise XOR, regardless of -semantics: +Lastly, `countSetBits` has a relationship to bitwise XOR, regardless +of semantics: ```haskell countSetBits (bitwiseLogicalXor semantics x x) = 0 @@ -361,12 +360,20 @@ countSetBits (bitwiseLogicalXor semantics x x) = 0 #### `findFirstSetBit` -`BuiltinByteString`s consisting entirely of zero bytes (including the empty -`BuiltinByteString`, by vacuous truth) always give a `-1` result with -`findFirstSetBit`: +`BuiltinByteString`s where every byte is the same (provided they are not empty) +have the same first bit as a singleton of that same byte: + +```haskell +-- 0 <= w8 <= 255, n >= 1 +findFirstSetBit (replicateByteString n w8) = +findFirstSetBit (replicateByteString 1 w8) +``` + +Additionally, `findFirstSet` has a relationship to bitwise XOR, regardless of +semantics: ```haskell -findFirstSetBit (replicateByteString n 0x00) = -1 +findFirstSetBit (bitwiseLogicalXor semantics x x) = -1 ``` Any result of a `findFirstSetBit` operation that isn't `-1` gives a valid bit From 41fb6801737e4356f878452259fca09612c3a0c5 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Wed, 12 Jun 2024 07:37:01 +1200 Subject: [PATCH 12/14] CIP number for CIP-0123 Co-authored-by: Robert Phair --- CIP-XXXX/CIP-XXXX.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md index f410af3b2..b0e2e4be6 100644 --- a/CIP-XXXX/CIP-XXXX.md +++ b/CIP-XXXX/CIP-XXXX.md @@ -1,5 +1,5 @@ --- -CIP: +CIP: 123 Title: Bitwise operations Category: Plutus Status: Proposed From 37e5774c2a81be54a37940c2ea474a4d7856e7c9 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Wed, 12 Jun 2024 07:37:12 +1200 Subject: [PATCH 13/14] More specific title Co-authored-by: Robert Phair --- CIP-XXXX/CIP-XXXX.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-XXXX/CIP-XXXX.md index b0e2e4be6..ebcd6a22f 100644 --- a/CIP-XXXX/CIP-XXXX.md +++ b/CIP-XXXX/CIP-XXXX.md @@ -1,6 +1,6 @@ --- CIP: 123 -Title: Bitwise operations +Title: Bitwise operations over BuiltinByteString Category: Plutus Status: Proposed Authors: From 703aa7be224cc6692036aa6d55727c634f282de0 Mon Sep 17 00:00:00 2001 From: Koz Ross Date: Wed, 12 Jun 2024 07:38:58 +1200 Subject: [PATCH 14/14] Match file structure for new CIP number --- CIP-XXXX/CIP-XXXX.md => CIP-0123/README.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename CIP-XXXX/CIP-XXXX.md => CIP-0123/README.md (100%) diff --git a/CIP-XXXX/CIP-XXXX.md b/CIP-0123/README.md similarity index 100% rename from CIP-XXXX/CIP-XXXX.md rename to CIP-0123/README.md