@@ -35,6 +35,7 @@ module Cardano.Api.Network.IPC.Internal
3535 , TxValidationErrorInCardanoMode
3636 , TxValidationError
3737 , submitTxToNodeLocal
38+ , TxSubmitResult (.. )
3839 , SubmitResult (.. )
3940
4041 -- *** Local state query
@@ -122,15 +123,18 @@ import Control.Concurrent.STM
122123 , putTMVar
123124 , takeTMVar
124125 , tryPutTMVar
126+ , tryTakeTMVar
125127 )
126- import Control.Exception (throwIO )
128+ import Control.Exception (SomeException , throwIO )
129+ import Control.Exception.Safe (tryAny )
127130import Control.Monad (void )
128131import Control.Monad.IO.Class
129132import Control.Tracer (nullTracer )
130133import Data.Aeson (ToJSON , object , toJSON , (.=) )
131134import Data.ByteString.Lazy qualified as LBS
132135import Data.Void (Void )
133136import GHC.Exts (IsList (.. ))
137+ import GHC.Stack (HasCallStack )
134138import Network.Mux qualified as Net
135139import Network.Mux.Trace (nullTracers )
136140
@@ -621,22 +625,56 @@ queryNodeLocalState connctInfo mpoint query = do
621625 pure $ Net.Query. SendMsgDone ()
622626 }
623627
628+ -- | The result of submitting a transaction via 'submitTxToNodeLocal'.
629+ data TxSubmitResult
630+ = -- | The transaction was accepted into the node's mempool.
631+ TxSubmitSuccess
632+ | -- | The node rejected the transaction due to a validation error.
633+ TxSubmitFail TxValidationErrorInCardanoMode
634+ | -- | An exception escaped the underlying connection machinery. This covers
635+ -- network-level errors (e.g. socket missing, bearer closed mid-protocol,
636+ -- handshake failure) but may also include unexpected exceptions from the
637+ -- protocol handlers.
638+ --
639+ -- Note: if the protocol handler wrote a result to the internal TMVar before
640+ -- the exception was thrown (e.g. the node responded but the bearer then
641+ -- closed), that result takes precedence and 'TxSubmitSuccess' or
642+ -- 'TxSubmitFail' is returned instead.
643+ TxSubmitError SomeException
644+ deriving Show
645+
624646submitTxToNodeLocal
625- :: MonadIO m
647+ :: ( HasCallStack , MonadIO m )
626648 => LocalNodeConnectInfo
627649 -> TxInMode
628- -> m (Net.Tx. SubmitResult TxValidationErrorInCardanoMode )
629- submitTxToNodeLocal connctInfo tx = do
630- resultVar <- liftIO newEmptyTMVarIO
631- connectToLocalNode
632- connctInfo
633- LocalNodeClientProtocols
634- { localChainSyncClient = NoLocalChainSyncClient
635- , localTxSubmissionClient = Just (localTxSubmissionClientSingle resultVar)
636- , localStateQueryClient = Nothing
637- , localTxMonitoringClient = Nothing
638- }
639- liftIO $ atomically (takeTMVar resultVar)
650+ -> m TxSubmitResult
651+ submitTxToNodeLocal connctInfo tx = liftIO $ do
652+ resultVar <- newEmptyTMVarIO
653+ result <-
654+ tryAny $
655+ connectToLocalNode
656+ connctInfo
657+ LocalNodeClientProtocols
658+ { localChainSyncClient = NoLocalChainSyncClient
659+ , localTxSubmissionClient = Just (localTxSubmissionClientSingle resultVar)
660+ , localStateQueryClient = Nothing
661+ , localTxMonitoringClient = Nothing
662+ }
663+ case result of
664+ Left e -> do
665+ -- The connection threw an exception, but the protocol handler may have
666+ -- already written a result (e.g. node responded then bearer closed).
667+ -- Prefer the protocol result if available; fall back to the exception.
668+ mResult <- atomically (tryTakeTMVar resultVar)
669+ pure $ case mResult of
670+ Just Net.Tx. SubmitSuccess -> TxSubmitSuccess
671+ Just (Net.Tx. SubmitFail reason) -> TxSubmitFail reason
672+ Nothing -> TxSubmitError e
673+ Right () -> do
674+ submitResult <- atomically (takeTMVar resultVar)
675+ pure $ case submitResult of
676+ Net.Tx. SubmitSuccess -> TxSubmitSuccess
677+ Net.Tx. SubmitFail reason -> TxSubmitFail reason
640678 where
641679 localTxSubmissionClientSingle
642680 :: ()
0 commit comments