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

Extend deserialiseFromRawBytesHex to produce error description #3304

Merged
merged 3 commits into from
May 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ readScript fp = do
error "Wrong script version."

toScriptHash :: String -> Hash ScriptData
toScriptHash str
= case deserialiseFromRawBytesHex (AsHash AsScriptData) (BSC.pack str) of
Just x -> x
Nothing -> error $ "Invalid datum hash: " ++ show str
toScriptHash str =
case deserialiseFromRawBytesHex (AsHash AsScriptData) (BSC.pack str) of
Right x -> x
cblp marked this conversation as resolved.
Show resolved Hide resolved
Left e -> error $ "Invalid datum hash: " ++ displayError e

preExecuteScript ::
ProtocolParameters
Expand Down
10 changes: 6 additions & 4 deletions cardano-api/src/Cardano/Api/Script.hs
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ import Cardano.Api.SerialiseJSON
import Cardano.Api.SerialiseRaw
import Cardano.Api.SerialiseTextEnvelope
import Cardano.Api.SerialiseUsing
import Cardano.Api.Utils (failEitherWith)

{- HLINT ignore "Use section" -}

Expand Down Expand Up @@ -1364,10 +1365,11 @@ parseScriptAfter lang =
_ -> fail "\"after\" script value not found"

parsePaymentKeyHash :: Text -> Aeson.Parser (Hash PaymentKey)
parsePaymentKeyHash txt =
case deserialiseFromRawBytesHex (AsHash AsPaymentKey) (Text.encodeUtf8 txt) of
Just payKeyHash -> return payKeyHash
Nothing -> fail $ "Error deserialising payment key hash: " <> Text.unpack txt
parsePaymentKeyHash =
failEitherWith
cblp marked this conversation as resolved.
Show resolved Hide resolved
(\e -> "Error deserialising payment key hash: " ++ displayError e)
. deserialiseFromRawBytesHex (AsHash AsPaymentKey)
. Text.encodeUtf8


-- ----------------------------------------------------------------------------
Expand Down
38 changes: 29 additions & 9 deletions cardano-api/src/Cardano/Api/SerialiseRaw.hs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}

-- | Raw binary serialisation
Expand All @@ -9,13 +10,13 @@ module Cardano.Api.SerialiseRaw
, serialiseToRawBytesHexText
) where

import Prelude
import Cardano.Prelude
import Prelude (String)

import Data.ByteString (ByteString)
import qualified Data.ByteString.Base16 as Base16
import Data.Text (Text)
import qualified Data.Text.Encoding as Text

import Cardano.Api.Error (Error, displayError)
import Cardano.Api.HasTypeProxy


Expand All @@ -31,9 +32,28 @@ serialiseToRawBytesHex = Base16.encode . serialiseToRawBytes
serialiseToRawBytesHexText :: SerialiseAsRawBytes a => a -> Text
serialiseToRawBytesHexText = Text.decodeUtf8 . serialiseToRawBytesHex

deserialiseFromRawBytesHex :: SerialiseAsRawBytes a
=> AsType a -> ByteString -> Maybe a
deserialiseFromRawBytesHex proxy hex =
case Base16.decode hex of
Right raw -> deserialiseFromRawBytes proxy raw
Left _msg -> Nothing
data RawBytesHexError
= RawBytesHexErrorBase16DecodeFail
ByteString -- ^ original input
String -- ^ error message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not do better than a String here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry? Too much indirection for my English.

Do you suggest having something more structured than a String in the second field of RawBytesHexErrorBase16DecodeFail or not?

| RawBytesHexErrorRawBytesDecodeFail
ByteString -- ^ original input
-- TODO(2022-01-26, cblp) TypeRep -- ^ output type proxy
deriving (Show)

instance Error RawBytesHexError where
displayError = \case
RawBytesHexErrorBase16DecodeFail input message ->
"Expected Base16-encoded bytestring, but got " ++ show input ++ "; "
++ message
RawBytesHexErrorRawBytesDecodeFail input ->
cblp marked this conversation as resolved.
Show resolved Hide resolved
"Failed to deserialise " ++ show input
-- TODO(2022-01-26, cblp) show expected output type

deserialiseFromRawBytesHex
:: SerialiseAsRawBytes a
=> AsType a -> ByteString -> Either RawBytesHexError a
deserialiseFromRawBytesHex proxy hex = do
raw <- first (RawBytesHexErrorBase16DecodeFail hex) $ Base16.decode hex
maybe (Left $ RawBytesHexErrorRawBytesDecodeFail hex) Right $
deserialiseFromRawBytes proxy raw
19 changes: 10 additions & 9 deletions cardano-api/src/Cardano/Api/TxBody.hs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ module Cardano.Api.TxBody (

import Prelude

import Control.Applicative (some)
import Control.Monad (guard)
import Data.Aeson (object, withObject, withText, (.:), (.:?), (.=))
import qualified Data.Aeson as Aeson
Expand Down Expand Up @@ -182,6 +183,7 @@ import Data.Word (Word16, Word32, Word64)
import GHC.Generics
import GHC.Records (HasField (..))
import qualified Text.Parsec as Parsec
import Text.Parsec ((<?>))
import qualified Text.Parsec.Language as Parsec
import qualified Text.Parsec.String as Parsec
import qualified Text.Parsec.Token as Parsec
Expand Down Expand Up @@ -240,8 +242,8 @@ import Cardano.Api.Address
import Cardano.Api.Certificate
import Cardano.Api.Eras
import Cardano.Api.Error
import Cardano.Api.HasTypeProxy
import Cardano.Api.Hash
import Cardano.Api.HasTypeProxy
import Cardano.Api.KeysByron
import Cardano.Api.KeysShelley
import Cardano.Api.NetworkId
Expand Down Expand Up @@ -430,10 +432,10 @@ instance FromJSONKey TxIn where

parseTxId :: Parsec.Parser TxId
parseTxId = do
str <- Parsec.many1 Parsec.hexDigit Parsec.<?> "transaction id (hexadecimal)"
case deserialiseFromRawBytesHex AsTxId (BSC.pack str) of
Just addr -> return addr
Nothing -> fail $ "Incorrect transaction id format:: " ++ show str
str <- some Parsec.hexDigit <?> "transaction id (hexadecimal)"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was there a reason to switch many1 to some? The Parsec version has an implementation specialised to parsers.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some is a less surprising name for the same thing.

failEitherWith
(\e -> "Incorrect transaction id format: " ++ displayError e) $
deserialiseFromRawBytesHex AsTxId $ BSC.pack str

parseTxIn :: Parsec.Parser TxIn
parseTxIn = TxIn <$> parseTxId <*> (Parsec.char '#' *> parseTxIx)
Expand Down Expand Up @@ -1486,10 +1488,9 @@ pattern TxOutDatumInTx s d <- TxOutDatumInTx' s _ d

parseHash :: SerialiseAsRawBytes (Hash a) => AsType (Hash a) -> Parsec.Parser (Hash a)
parseHash asType = do
str <- Parsec.many1 Parsec.hexDigit Parsec.<?> "hash"
case deserialiseFromRawBytesHex asType (BSC.pack str) of
Just sdh -> return sdh
Nothing -> fail $ "Failed to parse hash: " ++ show str
str <- some Parsec.hexDigit <?> "hash"
failEitherWith (\e -> "Failed to parse hash: " ++ displayError e) $
deserialiseFromRawBytesHex asType (BSC.pack str)

-- ----------------------------------------------------------------------------
-- Transaction fees
Expand Down
14 changes: 7 additions & 7 deletions cardano-api/src/Cardano/Api/Utils.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
{-# LANGUAGE LambdaCase #-}

-- | Internal utils for the other Api modules
--
module Cardano.Api.Utils
Expand All @@ -8,7 +6,8 @@ module Cardano.Api.Utils
, formatParsecError
, noInlineMaybeToStrictMaybe
, runParsecParser
, note
, failEither
, failEitherWith
) where

import Prelude
Expand Down Expand Up @@ -46,7 +45,8 @@ runParsecParser parser input =
Right txin -> pure txin
Left parseError -> fail $ formatParsecError parseError

note :: MonadFail m => String -> Maybe a -> m a
note msg = \case
Nothing -> fail msg
Just a -> pure a
failEither :: MonadFail m => Either String a -> m a
failEither = either fail pure

failEitherWith :: MonadFail m => (e -> String) -> Either e a -> m a
failEitherWith f = either (fail . f) pure
11 changes: 5 additions & 6 deletions cardano-api/src/Cardano/Api/Value.hs
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,18 @@ import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text

import qualified Cardano.Chain.Common as Byron

import qualified Cardano.Ledger.Coin as Shelley
import Cardano.Ledger.Crypto (StandardCrypto)
import qualified Cardano.Ledger.Mary.Value as Mary
import qualified Cardano.Ledger.ShelleyMA.Rules.Utxo as Shelley

import Cardano.Api.Error (displayError)
import Cardano.Api.HasTypeProxy
import Cardano.Api.Script
import Cardano.Api.SerialiseCBOR
import Cardano.Api.SerialiseRaw
import Cardano.Api.SerialiseUsing
import Cardano.Api.Utils (note)
import Cardano.Api.Utils (failEitherWith)


-- ----------------------------------------------------------------------------
Expand Down Expand Up @@ -361,11 +361,10 @@ instance FromJSON ValueNestedRep where
parsePid ("lovelace", q) = ValueNestedBundleAda <$> parseJSON q
parsePid (Aeson.toText -> pid, quantityBundleJson) = do
sHash <-
note ("Expected hex encoded PolicyId but got: " <> Text.unpack pid) $
failEitherWith
(\e -> "Failure when deserialising PolicyId: " ++ displayError e) $
deserialiseFromRawBytesHex AsScriptHash $ Text.encodeUtf8 pid
quantityBundle <- parseJSON quantityBundleJson
pure $ ValueNestedBundle (PolicyId sHash) quantityBundle

ValueNestedBundle (PolicyId sHash) <$> parseJSON quantityBundleJson

-- ----------------------------------------------------------------------------
-- Printing and pretty-printing
Expand Down
18 changes: 11 additions & 7 deletions cardano-api/src/Cardano/Api/ValueParser.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@ import Text.Parsec.Expr (Assoc (..), Operator (..), buildExpressionPar
import Text.Parsec.String (Parser)
import Text.ParserCombinators.Parsec.Combinator (many1)

import Cardano.Api.Error (displayError)
import Cardano.Api.SerialiseRaw
import Cardano.Api.Utils (note)
import Cardano.Api.Utils (failEitherWith)
import Cardano.Api.Value

-- | Parse a 'Value' from its string representation.
Expand Down Expand Up @@ -115,18 +116,21 @@ decimal = do
assetName :: Parser AssetName
assetName = do
hexText <- many hexDigit
note "AssetName deserisalisation failed" $
failEitherWith
(\e -> "AssetName deserisalisation failed: " ++ displayError e) $
deserialiseFromRawBytesHex AsAssetName $ BSC.pack hexText

-- | Policy ID parser.
policyId :: Parser PolicyId
policyId = do
hexText <- many1 hexDigit
case textToPolicyId hexText of
Just p -> pure p
Nothing ->
fail $ "expecting a 56 hex-encoded policy ID, but found only "
++ show (length hexText) ++ " hex digits"
failEitherWith
( \e ->
fail $
"expecting a 56-hex-digit policy ID, but found "
++ show (length hexText) ++ " hex digits; " ++ displayError e
)
(textToPolicyId hexText)
where
textToPolicyId =
fmap PolicyId
Expand Down
4 changes: 2 additions & 2 deletions cardano-cli/src/Cardano/CLI/Byron/Parsers.hs
Original file line number Diff line number Diff line change
Expand Up @@ -279,8 +279,8 @@ parseTxIdAtto :: Atto.Parser TxId
parseTxIdAtto = (<?> "Transaction ID (hexadecimal)") $ do
bstr <- Atto.takeWhile1 Char.isHexDigit
case deserialiseFromRawBytesHex AsTxId bstr of
Just addr -> return addr
Nothing -> fail $ "Incorrect transaction id format:: " ++ show bstr
Right addr -> return addr
Left e -> fail $ "Incorrect transaction id format: " ++ displayError e

parseTxIxAtto :: Atto.Parser TxIx
parseTxIxAtto = toEnum <$> Atto.decimal
Expand Down
7 changes: 3 additions & 4 deletions cardano-cli/src/Cardano/CLI/Shelley/Key.hs
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,9 @@ deserialiseInput asType acceptedFormats inputBs =
deserialiseHex :: SerialiseAsRawBytes a => DeserialiseInputResult a
deserialiseHex
| isValidHex inputBs =
maybe
(DeserialiseInputError InputInvalidError)
DeserialiseInputSuccess
(deserialiseFromRawBytesHex asType inputBs)
case deserialiseFromRawBytesHex asType inputBs of
Left _ -> DeserialiseInputError InputInvalidError
Right x -> DeserialiseInputSuccess x
| otherwise = DeserialiseInputErrorFormatMismatch

isValidHex :: ByteString -> Bool
Expand Down
Loading