-
Notifications
You must be signed in to change notification settings - Fork 479
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
Fix BuiltinByteString literal construction #6505
Comments
|
That's doable in Haskell, but I don't think it's possible in Plinth |
To be in-line with what all the other smart contract languages expose, Plinth should allow the creation of byte string literals in three ways:
// both are valid ByteArray definitions:
#[102, 111, 111]
#[0x66, 0x6f, 0x6f] In Plinth we can expose something like this: import PlutusTx.Prelude qualified as P
import qualified Data.ByteString as BS
import Data.Word (Word8)
builtinByteArray :: [Word8] -> BuiltinByteString
builtinByteArray = P.toBuiltin . BS.pack
ourByteArray :: BuiltinByteString
ourByteArray = builtinByteArray [
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef,
0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00
]
"foo" In Plinth and Plutarch this is the default IsString instance. ourByteString :: BuiltinByteString
ourByteString = "foo"
In Aiken, this is a double-quoted byte string prefixed with a
In Plutarch this is: someHexLiteral :: ClosedTerm PByteString
someHexLiteral = phexByteStr "666f6f" In Plinth we can expose something like this: import Data.Word (Word8)
import Data.Char (toLower)
import PlutusLedgerApi.V2 (BuiltinByteString)
import PlutusTx.Prelude qualified as P
import qualified Data.ByteString as BS
toBuiltinHexString :: String -> BuiltinByteString
toBuiltinHexString = P.toBuiltin . toHexString
toHexString :: String -> BS.ByteString
toHexString =
BS.pack . f
where
f "" = []
f [_] = error "UnevenLength"
f (x : y : rest) = (hexDigitToWord8 x * 16 + hexDigitToWord8 y) : f rest
hexDigitToWord8 :: HasCallStack => Char -> Word8
hexDigitToWord8 = f . toLower
where
f :: Char -> Word8
f '0' = 0
f '1' = 1
f '2' = 2
f '3' = 3
f '4' = 4
f '5' = 5
f '6' = 6
f '7' = 7
f '8' = 8
f '9' = 9
f 'a' = 10
f 'b' = 11
f 'c' = 12
f 'd' = 13
f 'e' = 14
f 'f' = 15
f c = error ("InvalidHexDigit " <> [c]) |
I feel strongly about this, so I'm retriaging it as a UX bug rather than merely a tech debt issue. |
The
IsString
instance of Haskell's ByteString converts a String to ByteString by:On the other hand, the
IsString
instance ofBuiltinByteString
uses UTF-8 encoding, which means it is only consistent withByteString
for Chars <= 127:This is also the case in Plinth, for instance
$$(compile [|| "\255" ||]) :: CompiledCode BuiltinByteString
results in#c3bf
. This is because GHC converts a String into aLiteral
containing a UTF-8 encodedByteString
. In this particular case, this is what the plugin sees:unpackCStringUtf8# "\195\191"
.This is a problem, not only because of the inconsistency between
ByteString
andBuiltinByteString
, but more importantly because it means there is no easy way to construct aBuiltinByteString
literal in Plinth, where some bytes are between 128 and 255. Thus, it is better to adopt theByteString
's behavior.To do so, we'll need to update both the
IsString
instance ofBuiltinByteString
, as well as updating the plugin.Updating the instance is easy, because the plugin does not compile the unfolding of the instance, but instead has special logic to handle it directly. So we can write any Haskell code in the instance, which means we can simply delegate to
ByteString
's instance.Regarding the plugin, we'll need to update the special logic that handles
BuiltinByteString
'sIsString
instance. There are two places, here and here. The first deals withstringToBuiltinByteString
, and the second deals withfromString @BuiltinByteString
.In both places, we currently use
stringExprContent
to extract the literal ByteString from theCoreExpr
. We need to update this extraction logic to detectunpackCStringUtf8# bs
. Instead of returningbs
, it should returnfromString @ByteString . Text.unpack . Text.decodeUtf8' $ bs
. Note that the extraction logic forBuiltinString
should stay the same.Property based tests should be added verifying that
fromString
forByteString
andBuiltinByteString
behave consistently, as well asString
andBuiltinString
.The text was updated successfully, but these errors were encountered: