Skip to content

Commit

Permalink
Merge #3304
Browse files Browse the repository at this point in the history
3304: Extend deserialiseFromRawBytesHex to produce error description r=cblp a=cblp



Co-authored-by: Yuriy Syrovetskiy <yuriy.syrovetskiy@iohk.io>
  • Loading branch information
iohk-bors[bot] and cblp authored Mar 24, 2022
2 parents ed9932c + 5dacedb commit 6f9a580
Show file tree
Hide file tree
Showing 10 changed files with 138 additions and 116 deletions.
8 changes: 4 additions & 4 deletions bench/tx-generator/src/Cardano/Benchmarking/PlutusExample.hs
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,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
Left e -> error $ "Invalid datum hash: " ++ displayError e

preExecuteScript ::
ProtocolParameters
Expand Down
42 changes: 18 additions & 24 deletions cardano-api/src/Cardano/Api/Script.hs
Original file line number Diff line number Diff line change
Expand Up @@ -97,47 +97,38 @@ module Cardano.Api.Script (

import Prelude

import Control.Applicative
import Control.Monad
import Data.Aeson (Value (..), object, (.:), (.=))
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Encoding as Aeson
import qualified Data.Aeson.Types as Aeson
import qualified Data.ByteString.Lazy as LBS
import Data.ByteString.Short (ShortByteString)
import qualified Data.ByteString.Short as SBS
import Data.Foldable (toList)
import Data.Scientific (toBoundedInteger)
import qualified Data.Sequence.Strict as Seq
import Data.String (IsString)
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.Encoding as Text
import Data.Type.Equality (TestEquality (..), (:~:) (Refl))
import Data.Typeable (Typeable)
import Numeric.Natural (Natural)

import Data.Aeson (Value (..), object, (.:), (.=))
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Encoding as Aeson
import qualified Data.Aeson.Types as Aeson
import qualified Data.Sequence.Strict as Seq
import Data.Vector (Vector)
import qualified Data.Vector as Vector

import Control.Applicative
import Control.Monad
import Numeric.Natural (Natural)

import qualified Cardano.Binary as CBOR

import qualified Cardano.Crypto.Hash.Class as Crypto

import Cardano.Slotting.Slot (SlotNo)

import qualified Cardano.Ledger.Alonzo.Language as Alonzo
import qualified Cardano.Ledger.Alonzo.Scripts as Alonzo
import qualified Cardano.Ledger.Core as Ledger
import qualified Cardano.Ledger.Era as Ledger

import qualified Cardano.Ledger.Keys as Shelley
import qualified Cardano.Ledger.Shelley.Scripts as Shelley
import qualified Cardano.Ledger.ShelleyMA.Timelocks as Timelock
import Cardano.Slotting.Slot (SlotNo)
import Ouroboros.Consensus.Shelley.Eras (StandardCrypto)

import qualified Cardano.Ledger.Alonzo.Language as Alonzo
import qualified Cardano.Ledger.Alonzo.Scripts as Alonzo

import qualified Plutus.V1.Ledger.Examples as Plutus

import Cardano.Api.Eras
Expand All @@ -150,6 +141,8 @@ import Cardano.Api.SerialiseJSON
import Cardano.Api.SerialiseRaw
import Cardano.Api.SerialiseTextEnvelope
import Cardano.Api.SerialiseUsing
import Cardano.Api.Utils (failEitherWith)
import Cardano.Api.Error (displayError)

{- HLINT ignore "Use section" -}

Expand Down Expand Up @@ -1301,7 +1294,8 @@ 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
(\e -> "Error deserialising payment key hash: " ++ displayError e)
. deserialiseFromRawBytesHex (AsHash AsPaymentKey)
. Text.encodeUtf8
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
| 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 ->
"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
17 changes: 9 additions & 8 deletions cardano-api/src/Cardano/Api/TxBody.hs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,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 @@ -170,6 +171,7 @@ import qualified Data.Text as Text
import Data.Type.Equality (TestEquality (..), (:~:) (Refl))
import Data.Word (Word16, Word32, Word64)
import GHC.Generics
import Text.Parsec ((<?>))
import qualified Text.Parsec as Parsec
import qualified Text.Parsec.Language as Parsec
import qualified Text.Parsec.String as Parsec
Expand Down Expand Up @@ -412,10 +414,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)"
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 @@ -1169,10 +1171,9 @@ pattern TxOutDatum s d <- TxOutDatum' 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 @@ -72,18 +72,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 @@ -359,11 +359,10 @@ instance FromJSON ValueNestedRep where
parsePid ("lovelace", q) = ValueNestedBundleAda <$> parseJSON q
parsePid (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 @@ -278,8 +278,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

0 comments on commit 6f9a580

Please sign in to comment.