Skip to content

Commit d0319f3

Browse files
committed
Write documentation on how to query and submit txs through IPC
1 parent efe0b3d commit d0319f3

File tree

2 files changed

+175
-2
lines changed

2 files changed

+175
-2
lines changed

cardano-api/cardano-api.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ library
8484
Cardano.Api.Internal.GenesisParameters
8585
Cardano.Api.Internal.Governance.Metadata.Validation
8686
Cardano.Api.Internal.IO
87+
Cardano.Api.Internal.IPC
8788
Cardano.Api.Internal.LedgerState
8889
Cardano.Api.Internal.Modes
8990
Cardano.Api.Internal.Orphans
@@ -219,7 +220,6 @@ library
219220
Cardano.Api.Internal.IO.Compat
220221
Cardano.Api.Internal.IO.Compat.Posix
221222
Cardano.Api.Internal.IO.Compat.Win32
222-
Cardano.Api.Internal.IPC
223223
Cardano.Api.Internal.IPC.Monad
224224
Cardano.Api.Internal.IPC.Version
225225
Cardano.Api.Internal.InMode

cardano-api/src/Cardano/Api/Internal/IPC.hs

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,181 @@
1212
{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
1313

1414
-- | Node IPC protocols
15+
--
16+
-- This module provides the client side of the node-to-client IPC protocol
17+
-- used to communicate with a local Cardano node. This can be used to
18+
-- query the node for information, to submit transactions, and even to
19+
-- get historical information aobut the chain by using the
20+
-- 'ChainSync' protocol.
1521
module Cardano.Api.Internal.IPC
16-
( -- * Node interaction
22+
( -- * Examples
23+
24+
-- | In this section, we show two examples: one for querying the node
25+
-- and obtain some basic information, and another one for submitting a
26+
-- transaction to the node.
27+
--
28+
-- To find out about how to create a transaction, see the documentation
29+
-- in "Cardano.Api.Internal.Tx.Body".
30+
--
31+
-- For the following examples we will use the following qualified imports
32+
-- from @cardano-api@:
33+
--
34+
-- @
35+
-- import qualified Cardano.Api as Api
36+
-- import qualified Cardano.Api.Consensus as Consensus
37+
-- import qualified Cardano.Api.Network as Network
38+
-- import qualified Cardano.Api.Shelley as Shelley
39+
-- @
40+
--
41+
-- We will also use the following explicit import from @base@:
42+
--
43+
-- @
44+
-- import Control.Monad.Except (runExceptT)
45+
-- @
46+
--
47+
-- And we will assume we are working on top of the @IO@ monad and that
48+
-- we have unqualified access to the @Prelude@ module.
49+
50+
-- ** Constructing connection information
51+
52+
-- | Independently of whether we want to query the node or submit transactions,
53+
-- the first thing we need to do is to gather the connection information.
54+
--
55+
-- We need three pieces of information:
56+
--
57+
-- * The number of slots per epoch for the network the node is connected to.
58+
-- We can obtain this information by looking for the @epochLength@ key in
59+
-- the @shelley-genesis.json@ file that the node is using to connect to the
60+
-- network. For the preview network, this is currently @86_400@.
61+
-- * In the case we are connecting to a testnet, we also need to find out
62+
-- the magic number for the network the node is connected to. This can be
63+
-- obtained by looking for the @networkMagic@ key in the @shelley-genesis.json@
64+
-- file that the node is using to connect to the network. For the preview
65+
-- network, the magic number is currently @2@.
66+
-- * The path to the socket file of the node. This can be set when starting the
67+
-- node by using the @--socket-path@ parameter. By default it can usually be
68+
-- found in the @db@ subfolder of the node's working directory.
69+
--
70+
-- Then, we gather all the information into a 'LocalNodeConnectInfo' value.
71+
--
72+
-- For example, let's assume we are connecting to the preview network, and that the
73+
-- socket file is located at @\/home\/user\/cardano-node\/db\/node.socket@. We could
74+
-- then create the 'LocalNodeConnectInfo' value as follows:
75+
--
76+
-- @
77+
-- let connectionInfo =
78+
-- Api.LocalNodeConnectInfo
79+
-- { Api.localConsensusModeParams = Api.CardanoModeParams (Api.EpochSlots 86_400)
80+
-- , Api.localNodeNetworkId = Api.Testnet (Api.NetworkMagic 2)
81+
-- , Api.localNodeSocketPath = Api.File "\/home\/user\/cardano-node\/db\/node.socket"
82+
-- }
83+
-- @
84+
85+
-- ** Querying the node for the UTxO set
86+
87+
-- | Let's imagine we want to obtain the set of transaction outputs that
88+
-- are currently unspent in the network (UTxO set).
89+
90+
-- *** Obtaining the current era
91+
92+
-- | Depending on the type of query we want to perform, we usually need
93+
-- to know what era the node is currently in. We can hardcode this
94+
-- information by using one of the constructors of the 'ShelleyBasedEra' type.
95+
-- But we can also obtain this information from the node, as follows:
96+
--
97+
-- @
98+
-- eEra <- runExceptT $ Api.queryNodeLocalState connectionInfo Network.VolatileTip Api.QueryCurrentEra
99+
-- @
100+
--
101+
-- Here, 'VolatileTip' means we want to get the information out of the most recent node that the
102+
-- node is aware of. The disadvantage is that the information we get may potentially be rolled back
103+
-- and stop being valid. We need to account for this. Alternatively, we can use 'ImmutableTip' to
104+
-- obtain the information from the most recent block that is assumed by the consensus algorithm
105+
-- to be final, and that won't be rolled back. But this information is not so recent, in mainnet
106+
-- this is about 36 hours in the past.
107+
--
108+
-- 'QueryCurrentEra' is the constructor of the query we want to run. In this case, we want to
109+
-- obtain the current era of the node.
110+
--
111+
-- The result of the query is an 'ExceptT' monad, which we can run by using the 'runExceptT'
112+
-- function, which in turn gives us a @eEra@ value of type @Either AcquiringFailure AnyCardanoEra@.
113+
--
114+
-- This is an example of how to unwrap this value into a 'ShelleyBasedEra' based era, assuming the node
115+
-- is not still running Byron:
116+
--
117+
-- @
118+
-- Api.AnyShelleyBasedEra sbe :: Api.AnyShelleyBasedEra <- case eEra of
119+
-- Right (Api.AnyCardanoEra era) ->
120+
-- Api.caseByronOrShelleyBasedEra
121+
-- (error "Error, we are in Byron era")
122+
-- (return . Api.AnyShelleyBasedEra)
123+
-- era
124+
-- Left Shelley.AFPointTooOld -> error "Error, point queried in the chain is too old!"
125+
-- Left Shelley.AFPointNotOnChain -> error "Error, point queried is not on chain!"
126+
-- @
127+
--
128+
-- 'AFPointToolOld' and 'AFPointNotOnChain' should not happen either with 'VolatileTip' or 'ImmutableTip'.
129+
130+
-- *** Obtaining the UTxO set
131+
132+
-- | Now that we know the current era, we can query the node for the UTxO set similarly
133+
-- by using the 'QueryUTxO' query as follow:
134+
--
135+
-- @
136+
-- eUtxo <-
137+
-- runExceptT $
138+
-- Api.queryNodeLocalState
139+
-- connectionInfo
140+
-- Network.VolatileTip
141+
-- (Api.QueryInEra (Api.QueryInShelleyBasedEra sbe (Api.QueryUTxO Api.QueryUTxOWhole)))
142+
-- @
143+
--
144+
-- This time, @eUtxo@ has a nested type of @Either AcquiringFailure (Either EraMismatch (UTxO era))@.
145+
-- So we can unwrap it as follows:
146+
--
147+
-- @
148+
-- utxo <- case eUtxo of
149+
-- Right (Right (Api.UTxO utxo)) -> do
150+
-- return utxo
151+
-- Right (Left (Consensus.EraMismatch{Consensus.ledgerEraName, Consensus.otherEraName})) ->
152+
-- error
153+
-- ( "Error, we assumed era was "
154+
-- ++ show otherEraName
155+
-- ++ " but it was "
156+
-- ++ show ledgerEraName
157+
-- )
158+
-- Left Shelley.AFPointTooOld -> error "Error, point queried in the chain is too old!"
159+
-- Left Shelley.AFPointNotOnChain -> error "Error, point queried is not on chain!"
160+
-- @
161+
--
162+
-- The obtained @utxo@ is a standard @Map@ of type @Map TxIn (TxOut CtxUTxO era)@.
163+
164+
-- ** Submitting a transaction
165+
166+
-- | Let's assume we have a signed transaction of the right era that we want to submit to the node,
167+
-- and that it is in the variable @signedTx@ of type @Tx era@.
168+
--
169+
-- You can find out how to make such a transaction by looking at the documentation of the
170+
-- "Cardano.Api.Internal.Tx.Body" module.
171+
--
172+
-- We can send it to the node by using the 'submitTxToNodeLocal' function as follows:
173+
--
174+
-- @
175+
-- result <- Api.submitTxToNodeLocal connectionInfo (Api.TxInMode sbe signedTx)
176+
-- @
177+
--
178+
-- The result of the submission is a 'SubmitResult' value, which can be inspected as follows:
179+
--
180+
-- @
181+
-- case result of
182+
-- Api.SubmitSuccess -> putStrLn "Transaction submitted successfully!"
183+
-- Api.SubmitFail reason -> error $ "Error submitting transaction: " ++ show reason
184+
-- @
185+
--
186+
-- If the command succeeds, then the transaction will be on the node's mempool ready
187+
-- to be included in a block.
188+
189+
-- * Node interaction
17190

18191
-- | Operations that involve talking to a local Cardano node.
19192
connectToLocalNode

0 commit comments

Comments
 (0)