-
Notifications
You must be signed in to change notification settings - Fork 321
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
CIP-0058: integers vs. byteStrings for cryptographic applications #794
Comments
@Ryun1 @Crypto2099 - the submitter @marcinbugaj seems to raise a valid question and has invited @michaelpj @kozross @perturbing @kwxm to continue the discussion from IntersectMBO/plutus#4733 (comment) which makes sense because there are over 200 comments in that pull request already. Marcin: as mentioned in that comment I wouldn't think of this issue as a "ticket" since it's beyond the CIP repository admins' capabilities to resolve. Instead we can watch the discussion that develops among the subject matter experts here and hopefully identify if & when a pull request might be appropriate for CIP-0058. |
I'm not sure that the point of CIP-0058 was to enable cryptographic applications, but it's perfectly valid to bring the point up. Yo make a good point that the separation between integers and bytestrings makes it difficult to mix arithmetic and bitwise operations. I'm somewhat doubtful that many cryptographic operations could be efficiently implemented in Plutus Core even if we were to provide new builtins that were suitable for both arithmetic and bitwise operations. Plutus Core is interpreted and it's functional, and both of those aspects will slow things down: you can't do in-place modifications of bytestrings, for example: every time you modify a single bit you have to copy the entire bytestring, so if you're doing hundreds or thousands of such operations it'll be slow. I guess we'd have to try it and see. Anyway, now is a good time to bring this up, before we do any more work on CIP-0058. Part of the idea of CIPs is that they're open for members of the community to discuss and contribute to, so your input is very welcome. |
This was definitely one of the major use cases. The presence of proper bitwise operations would make infeasible things feasible, even if not entirely efficient. |
Copying my old comments from IntersectMBO/plutus#4733 (comment)
|
You could just implement every single cryptographic operation in the interpreter directly, |
The CIP explains why we do not implement these operations on integers, see the section "No metaphor-mixing between numbers and bits". |
Metaphor-mixing is bad, but that doesn't preclude making conversion as cheap as possible. |
I don't mind
|
There exist modular arithmetic algorithms that utilize negative numbers. For example Algorithm 2.22 from book ''Guide by elliptic curve cryptography" by Hankerson et al. |
The point is you would represent them two's complement as is done in other languages. You probably want the wrapping on addition/subtraction too. |
To avoid bias in the the discussion (and also to attract more interested parties) I'm changing the title a bit... please feel free to correct further if the new title doesn't wholly reflect what is currently being debated. |
I think that's a little bit of a red herring. When you're doing modular arithmetic you're working in the ring ℤ_n for some n and it's natural to represent an element of that ring by some integer, which is usually normalised to lie between 0 and n-1, so in particular it's positive. However, when you're looking at a power a^k with a in ℤ_n, k really is an integer, not an element of the ring, and negative powers are perfectly reasonable for invertible elements of the ring. |
While bitwise operations on integers are useful in some contexts, bytestrings may be more suitable in others. For example, bytestrings have a natural notion of length, but the size of an integer in bytes is a bit more lax. Suppose you have two integers of different lengths and you want to perform a bitwise There are similar issues for shifts and rotations: in particular, what happens if you rotate the bits in an integer (which have no fixed length)? After rotating by a certain number of bits you might end up with one or more zero bits at the start, and presumably they'd just be dropped since there's no way of representing an integer with leading zeros. This would mean that in general if you take an integer n and rotate it by i bits and then by j bits, you'd probably end up with something very different from what you'd get by rotating n by i+j bits, which seems undesirable. Note that in There was also a mention of zero-cost conversions between bytestrings and integers, but is this really possible? Again I'm thinking about leading zeros in bytestrings: if you convert a bytestring to an integer and then back again you might end up with something shorter than the thing you started with, so maybe we'd need a width argument as well, as we currently have for Also, if we're dealing with signed integers, what happens to the signs during shifts/rotations/xor/etc? There are standard ways to do that for fixed-width integers, but it may be more tricky for the unbounded integers that we have in Plutus Core. I'm not saying that we shouldn't think about bitwise operations for integers, just that things may not be as easy as they appear at first sight. One of the use cases in CIP-0058 was concise data structures based on bytestrings, and those might not be so easy to implement if we had to represent them using integers instead of bytestrings. Maybe there's no one-size-fits-all solution and we'd need separate sets of operations for bytestrings and integers to accommodate the needs of different application areas. That would need a whole new CIP though, and it probably wouldn't get implemented any time soon. |
The point is more so you'd have to handle negative numbers yourself. Another option is to add integer arithmetic operations to bytestrings. |
I agree with what @kwxm said about this, these negative integers are semantics, it is taking the inverse and the exponentiation with the positive exponent (also see this CIP). |
this would likely require the introduction of fixed-width integers, as currently we only have integers of arbitrary length, plenty of cryptographic algorithms use and abuse rollover too.
since we have endianness requirements, 'zero cost' conversions aren't generally possible in the conversion. |
Even if we didn't, 'zero cost' conversions would be impossible due to incompatible representations between |
Not necessarily true. I looked into this at the time and you can use the same representation as integers for bytestrings. |
We assume little endian. |
At least as far as I could tell in GHC 9.0 and onwards, |
We would use |
That's a change that is quite unlikely to get approved. |
I don't see why the SPOs would disapprove of this. |
libgmp representation is |
It's worse than this - you also have a pinned-versus-unpinned conversion, which cannot be done safely in general (as I have specified). Furthermore, The idea of a zero-cost conversion between (Plutus representations of) |
At this point enough argument has been provided for the claim that zero-cost conversion is not doable :) Furthermore I wish we could agree on the fact that CIP58 is not useful for cryptographic application. Those has been mentioned in the first paragraph of CIP58 as an area of application for bitwise ops. Yet, the goal is not fulfilled. Arithmetic ops can be performed on integers. Bitwise ops on bytestrings. Conversion is not cheap and it's possible that using bitwise ops to divide by power of 2 is more expensive than using normal division. |
CIP-58 is due to be replaced in any case at this point. The conversion primitives that have been merged recently have already deviated from CIP-58 to the point of mutual incompatibility. |
As long as you cannot do bitwise and arithmetic operations on the same type (or without non-zero cost conversion) the 'problem' is still there. |
Pinnedness is not relevant, the point is you wouldn’t use ByteString at all. |
I think sacrificing proper support for big endian platforms is worth it. The best solution would be to add support for something like WASM scripts but this seems unlikely due to bureaucracy. |
Note also that we don't necessarily want to fix ourselves to the |
Also, one of the use cases that we have to test the viability of these operations is an implementation of Ed25519 using them. If we can't make that efficient enough to fit within a reasonable amount of budget then that's a problem for sure, and if we can then I think we should be able to do most other things too. |
Bitwise operations can be performed on byte strings only in CIP-0058. On the other hand the aritmetic operations can be performed on integers only. In cryptographic algorithms arithmetic and bitwise operations interleave frequently. As a consequence one has to switch between integers and bytestrings via
integerToByteString
/byteStringToInteger
frequently. That conversion may have significant cost and diminish the advantage of using bitwise operations after all. MoreoverintegerToByteString
does not work for negative numbers which requires special handling in cryptographic algorithms, a.k.a. costs you dearly.I'd assume that a good indicator of a success for this CIP is if divisions and multiplications by powers of 2 are significantly less expensive that just naive divisions. For the CIP in its current form it implies:
byteStringToInteger . (bitshift n) . integerToByteString
is faster than naitve division by2^n
. I haven't measured it but from Matrix discussion with Plutus Core devs it follows that it may not be the case.I have doubts if bitwise operations on byte strings is an optimal choice for cryptographic applications. Perhaps it's worth revising the design choice by allowing bitwise operations on Plutus integers directly. libgmp which is used as runtime for Haskell and Plutus integers has direct support for bitwise operations on integers. Moreover, most of the programming languages, including Haskell, equip integers with arithmetic and bitwise operations. I think that Plutus should not stand out from the crowd especially if performance, which is the sole purpose of the CIP, would suffer.
It was sugessted that ideally conversion from Integer to
BuiltinByteString
should be a type zero-cost casting/coercion only.integerToByteString
Plutus implementation must convert form libgmp's internal representation (see _mp_d) toBuiltinByteString
. It's not difficult to see that it cannot be realized as zero cost coercion.I'd like to emphesize that I cannot see rationale nor benefits behind doing bitwise operations on
BuiltinByteString
other than indulging type lovers (excuse me the joke). My take on the issue is that functionsintegerToByteString
/byteStringToInteger
are good and needed but bitwise operations should be performed on integers directly.The text was updated successfully, but these errors were encountered: