Skip to content

Commit

Permalink
[ADP-3359] Report unaccepted-era transactions as HTTP 403 errors (#4595)
Browse files Browse the repository at this point in the history
- [x] Remove an `error` call when not-in-a-recent-era  tx is posted
- [x] Add a 403 HTTP error in case the exception above is triggered

ADP-3359
  • Loading branch information
jonathanknowles authored May 22, 2024
2 parents bfcc0c0 + bf3471a commit 1a0ea67
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 27 deletions.
44 changes: 33 additions & 11 deletions lib/api/src/Cardano/Wallet/Api/Http/Server/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ import Cardano.Wallet.Api.Types.Error
, ApiErrorNotEnoughMoneyShortfall (..)
, ApiErrorSharedWalletNoSuchCosigner (..)
, ApiErrorTxOutputLovelaceInsufficient (..)
, ApiErrorUnsupportedEra (..)
)
import Cardano.Wallet.Primitive.Ledger.Convert
( Convert (toWallet)
Expand Down Expand Up @@ -198,6 +199,7 @@ import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.Foldable as F
import qualified Data.List as L
import qualified Data.Set as Set
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import qualified Internal.Cardano.Write.Tx as Write
Expand Down Expand Up @@ -626,18 +628,38 @@ instance IsServerError ErrRemoveTx where
instance IsServerError ErrPostTx where
toServerError = \case
ErrPostTxValidationError err ->
apiError err500 CreatedInvalidTransaction $ mconcat
[ "The submitted transaction was rejected by the local "
, "node. Here's an error message that may help with "
, "debugging:\n", err
]
apiError err500 CreatedInvalidTransaction
$ mconcat
[ "The submitted transaction was rejected by the local "
, "node. Here's an error message that may help with "
, "debugging:\n"
, err
]
ErrPostTxMempoolFull ->
apiError err425
{errBody = "Mempool is full, please try resubmitting again later."}
MempoolIsFull $ mconcat
[ "The submitted transaction was rejected by the Cardano node "
, "because its mempool was full."
]
apiError
err425
{ errBody =
"Mempool is full, please try resubmitting again later."
}
MempoolIsFull
$ mconcat
[ "The submitted transaction was rejected by the Cardano "
, "node because its mempool was full."
]
e@(ErrPostTxEraUnsupported unsupported) ->
apiError err403 error' $ toText e
where
error' =
UnsupportedEra
$ ApiErrorUnsupportedEra
{ unsupportedEra = toApiEra unsupported
, supportedEras =
Set.fromList
( toApiEra
. Write.toAnyCardanoEra
<$> [minBound .. maxBound]
)
}

instance IsServerError ErrSubmitTransaction where
toServerError = \case
Expand Down
15 changes: 15 additions & 0 deletions lib/api/src/Cardano/Wallet/Api/Types/Error.hs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ module Cardano.Wallet.Api.Types.Error
, ApiErrorNotEnoughMoneyShortfall (..)
, ApiErrorMissingWitnessesInTransaction (..)
, ApiErrorNoSuchPool (..)
, ApiErrorUnsupportedEra (..)
)
where

Expand Down Expand Up @@ -76,6 +77,9 @@ import Data.Data
import Data.Maybe
( fromMaybe
)
import Data.Set
( Set
)
import Data.Text
( Text
)
Expand Down Expand Up @@ -217,6 +221,7 @@ data ApiErrorInfo
| BlockHeaderNotFound
| TranslationByronTxOutInContext
| BalanceTxInlinePlutusV3ScriptNotSupportedInBabbage
| UnsupportedEra !ApiErrorUnsupportedEra

deriving (Eq, Generic, Show, Data, Typeable)
deriving anyclass NFData
Expand All @@ -235,6 +240,16 @@ apiErrorInfoOptions = defaultSumTypeOptions
}
}

data ApiErrorUnsupportedEra = ApiErrorUnsupportedEra
{ unsupportedEra :: !ApiEra
-- ^ The unsupported era (as specified by the caller).
, supportedEras :: !(Set ApiEra)
-- ^ The set of eras that we currently support.
}
deriving (Data, Eq, Generic, Show, Typeable)
deriving (FromJSON, ToJSON) via DefaultRecord ApiErrorUnsupportedEra
deriving anyclass NFData

data ApiErrorSharedWalletNoSuchCosigner = ApiErrorSharedWalletNoSuchCosigner
{ cosignerIndex
:: !ApiCosignerIndex
Expand Down
14 changes: 11 additions & 3 deletions lib/network-layer/src/Cardano/Wallet/Network.hs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ import qualified Internal.Cardano.Write.Tx as Write
{-----------------------------------------------------------------------------
NetworkLayer
------------------------------------------------------------------------------}

-- | Interface for network capabilities.
data NetworkLayer m block = NetworkLayer
{ chainSync
Expand Down Expand Up @@ -134,8 +135,7 @@ data NetworkLayer m block = NetworkLayer
-- only change once per epoch.
, currentProtocolParametersInRecentEras
:: m (MaybeInRecentEra Write.PParams)
-- ^ Get the last known protocol parameters for recent eras.

-- ^ Get the last known protocol parameters for recent eras.
, currentSlottingParameters
:: m SlottingParameters
-- ^ Get the last known slotting parameters. In principle, these can
Expand Down Expand Up @@ -193,6 +193,7 @@ instance Functor m => Functor (NetworkLayer m) where
{-----------------------------------------------------------------------------
ChainFollower
------------------------------------------------------------------------------}

-- | A collection of callbacks to use with the 'chainSync' function.
data ChainFollower m point tip blocks = ChainFollower
{ checkpointPolicy :: Integer -> CheckpointPolicy
Expand Down Expand Up @@ -275,14 +276,21 @@ mapChainFollower fpoint12 fpoint21 ftip fblocks cf =
-------------------------------------------------------------------------------}

-- | Error while trying to send a transaction
data ErrPostTx = ErrPostTxValidationError Text | ErrPostTxMempoolFull
data ErrPostTx
= ErrPostTxValidationError Text
| ErrPostTxMempoolFull
| ErrPostTxEraUnsupported AnyCardanoEra
deriving (Generic, Show, Eq)

instance ToText ErrPostTx where
toText = \case
ErrPostTxValidationError msg -> msg
ErrPostTxMempoolFull ->
"mempool was full and refused posted transaction"
ErrPostTxEraUnsupported unsupported ->
"Submitted transaction was in "
<> T.pack (show unsupported)
<> " era, which is not supported"

-- | Error while trying to retrieve a block
newtype ErrFetchBlock = ErrNoBlockAt Read.ChainPoint
Expand Down
18 changes: 11 additions & 7 deletions lib/network-layer/src/Cardano/Wallet/Network/Implementation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ import Cardano.Wallet.Primitive.Ledger.Byron
( byronCodecConfig
)
import Cardano.Wallet.Primitive.Ledger.Shelley
( nodeToClientVersions
( UnsealException (..)
, nodeToClientVersions
, toCardanoEra
, unsealShelleyTx
)
Expand Down Expand Up @@ -630,12 +631,15 @@ withNodeNetworkLayerBase
_postTx txSubmissionQueue readCurrentEra tx = do
liftIO $ traceWith tr $ MsgPostTx tx
preferredEra <- liftIO readCurrentEra
let cmd =
CmdSubmitTx . toConsensusGenTx
$ unsealShelleyTx preferredEra tx
liftIO (send txSubmissionQueue cmd) >>= \case
SubmitSuccess -> pure ()
SubmitFail e -> throwE $ ErrPostTxValidationError $ T.pack $ show e
case unsealShelleyTx preferredEra tx of
Left (UnsealedTxInUnsupportedEra era) ->
throwE $ ErrPostTxEraUnsupported era
Right tx' -> do
let cmd = CmdSubmitTx . toConsensusGenTx $ tx'
liftIO (send txSubmissionQueue cmd) >>= \case
SubmitSuccess -> pure ()
SubmitFail e ->
throwE $ ErrPostTxValidationError $ T.pack $ show e

_stakeDistribution queue coin = do
liftIO $ traceWith tr $ MsgWillQueryRewardsForStake coin
Expand Down
16 changes: 10 additions & 6 deletions lib/primitive/lib/Cardano/Wallet/Primitive/Ledger/Shelley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ module Cardano.Wallet.Primitive.Ledger.Shelley
, interval0
, interval1
, numberOfTransactionsInBlock

-- * Errors
, UnsealException (..)
) where

import Prelude
Expand Down Expand Up @@ -1018,21 +1021,22 @@ rewardAccountFromAddress (W.Address bytes) = refToAccount . ref =<< parseAddr by
refToAccount (SL.StakeRefPtr _) = Nothing
refToAccount SL.StakeRefNull = Nothing

newtype UnsealException = UnsealedTxInUnsupportedEra AnyCardanoEra

-- | Converts 'SealedTx' to something that can be submitted with the
-- 'Cardano.Api' local tx submission client.
unsealShelleyTx
:: AnyCardanoEra
-- ^ Preferred latest era (see 'ideallyNoLaterThan')
-> W.SealedTx
-> TxInMode
-> Either UnsealException TxInMode
unsealShelleyTx era wtx = case W.cardanoTxIdeallyNoLaterThan era wtx of
Cardano.InAnyCardanoEra BabbageEra tx ->
TxInMode ShelleyBasedEraBabbage tx
Right $ TxInMode ShelleyBasedEraBabbage tx
Cardano.InAnyCardanoEra ConwayEra tx ->
TxInMode ShelleyBasedEraConway tx
_ -> error $
"unsealShelleyTx: Creating transactions in era " <> show era
<> " is not supported anymore."
Right $ TxInMode ShelleyBasedEraConway tx
Cardano.InAnyCardanoEra unsupportedEra _ ->
Left $ UnsealedTxInUnsupportedEra $ AnyCardanoEra unsupportedEra

instance (forall era. IsCardanoEra era => Show (thing era)) =>
Show (InAnyCardanoEra thing) where
Expand Down
5 changes: 5 additions & 0 deletions lib/unit/test/unit/Cardano/Wallet/Api/TypesSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ import Cardano.Wallet.Api.Types.Error
, ApiErrorNotEnoughMoneyShortfall (..)
, ApiErrorSharedWalletNoSuchCosigner (..)
, ApiErrorTxOutputLovelaceInsufficient (..)
, ApiErrorUnsupportedEra (..)
)
import Cardano.Wallet.Api.Types.RestorationMode
( ApiRestorationMode
Expand Down Expand Up @@ -2471,6 +2472,10 @@ instance Arbitrary ApiErrorMissingWitnessesInTransaction where
arbitrary = genericArbitrary
shrink = genericShrink

instance Arbitrary ApiErrorUnsupportedEra where
arbitrary = genericArbitrary
shrink = genericShrink

instance Arbitrary ApiErrorSharedWalletNoSuchCosigner where
arbitrary = genericArbitrary
shrink = genericShrink
Expand Down
21 changes: 21 additions & 0 deletions specifications/api/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5557,6 +5557,26 @@ x-errBlockHeaderNotFound: &errBlockHeaderNotFound
type: string
enum: ['block_header_not_found']

x-errUnsupportedEra: &errUnsupportedEra
<<: *responsesErr
title: unsupported_era
properties:
message:
type: string
code:
type: string
enum: ['unsupported_era']
info:
type: object
required:
- unsupported_era
- supported_eras
properties:
unsupported_era: *ApiEra
supported_eras:
type: array
items: *ApiEra

x-responsesErr400: &responsesErr400
400:
description: Bad Request
Expand Down Expand Up @@ -6015,6 +6035,7 @@ x-responsesPostTransaction: &responsesPostTransaction
- <<: *errTransactionIsTooBig
- <<: *errNoRootKey
- <<: *errWrongEncryptionPassphrase
- <<: *errUnsupportedEra
<<: *responsesErr404WalletNotFound
<<: *responsesErr406
<<: *responsesErr415UnsupportedMediaType
Expand Down

0 comments on commit 1a0ea67

Please sign in to comment.