1
1
{-# LANGUAGE DataKinds #-}
2
- {-# LANGUAGE NamedFieldPuns #-}
2
+ {-# LANGUAGE NumericUnderscores #-}
3
3
{-# LANGUAGE OverloadedLabels #-}
4
4
{-# LANGUAGE OverloadedLists #-}
5
5
{-# LANGUAGE OverloadedStrings #-}
6
6
{-# LANGUAGE ScopedTypeVariables #-}
7
7
{-# LANGUAGE TypeApplications #-}
8
+ {-# LANGUAGE TypeOperators #-}
8
9
9
10
module Cardano.Testnet.Test.Rpc.Transaction
10
11
( hprop_rpc_transaction
@@ -14,71 +15,144 @@ where
14
15
import Cardano.Api
15
16
import qualified Cardano.Api.Ledger as L
16
17
17
- import Cardano.CLI.Type.Output (QueryTipLocalStateOutput (.. ))
18
- import qualified Cardano.Ledger.Api as L
19
- import qualified Cardano.Ledger.Binary.Version as L
20
- import qualified Cardano.Ledger.Conway.Core as L
21
- import qualified Cardano.Ledger.Conway.PParams as L
22
- import qualified Cardano.Ledger.Plutus as L
18
+ import Cardano.Rpc.Client (Proto )
23
19
import qualified Cardano.Rpc.Client as Rpc
24
20
import qualified Cardano.Rpc.Proto.Api.UtxoRpc.Query as UtxoRpc
21
+ import qualified Cardano.Rpc.Proto.Api.UtxoRpc.Submit as UtxoRpc
25
22
import Cardano.Rpc.Server.Internal.UtxoRpc.Query ()
23
+ import Cardano.Rpc.Server.Internal.UtxoRpc.Type
26
24
import Cardano.Testnet
27
25
28
26
import Prelude
29
27
30
- import qualified Data.ByteString.Short as SBS
28
+ import Control.Monad
29
+ import Control.Monad.Fix
31
30
import Data.Default.Class
32
- import qualified Data.Map.Strict as M
31
+ import qualified Data.Text.Encoding as T
32
+ import GHC.Stack
33
33
import Lens.Micro
34
34
35
- import Testnet.Components.Query
36
- import Testnet.Process.Run
37
35
import Testnet.Property.Util (integrationRetryWorkspace )
38
- import Testnet.Start. Types
36
+ import Testnet.Types
39
37
40
38
import Hedgehog
41
39
import qualified Hedgehog as H
42
40
import qualified Hedgehog.Extras.Test.Base as H
43
41
import qualified Hedgehog.Extras.Test.TestWatchdog as H
44
42
43
+ import RIO (threadDelay )
44
+
45
45
hprop_rpc_transaction :: Property
46
46
hprop_rpc_transaction = integrationRetryWorkspace 2 " rpc-tx" $ \ tempAbsBasePath' -> H. runWithDefaultWatchdog_ $ do
47
- conf@ Conf {tempAbsPath} <- mkConf tempAbsBasePath'
48
- let tempAbsPath' = unTmpAbsPath tempAbsPath
49
-
50
- let ceo = ConwayEraOnwardsConway
47
+ conf <- mkConf tempAbsBasePath'
48
+ let (ceo, eraProxy) =
49
+ (conwayBasedEra, asType) :: era ~ ConwayEra => (ConwayEraOnwards era , AsType era )
51
50
sbe = convert ceo
52
- eraName = eraToString sbe
53
51
options = def{cardanoNodeEra = AnyShelleyBasedEra sbe, cardanoEnableRpc = True }
54
52
55
53
TestnetRuntime
56
- { testnetMagic
57
- , configurationFile
58
- , testnetNodes = node0@ TestnetNode {nodeSprocket} : _
54
+ { testnetNodes = node0 : _
55
+ , wallets = wallet0@ (PaymentKeyInfo _ addrTxt0) : (PaymentKeyInfo _ addrTxt1) : _
59
56
} <-
60
57
createAndRunTestnet options def conf
61
58
62
- execConfig <- mkExecConfig tempAbsPath' nodeSprocket testnetMagic
63
- epochStateView <- getEpochStateView configurationFile (nodeSocketPath node0)
64
- pparams <- unLedgerProtocolParameters <$> getProtocolParams epochStateView ceo
65
- -- H.noteShowPretty_ pparams
66
- utxos <- findAllUtxos epochStateView sbe
67
- H. noteShowPretty_ utxos
68
59
rpcSocket <- H. note . unFile $ nodeRpcSocketPath node0
69
60
61
+ -- prepare tx inputs and output address
62
+ H. noteShow_ addrTxt0
63
+ addr0 <- H. nothingFail $ deserialiseAddress (AsAddressInEra eraProxy) addrTxt0
64
+
65
+ H. noteShow_ addrTxt1
66
+ addr1 <- H. nothingFail $ deserialiseAddress (AsAddressInEra eraProxy) addrTxt1
67
+
68
+ -- read key witnesses
69
+ wit0 :: ShelleyWitnessSigningKey <-
70
+ H. leftFailM . H. evalIO $
71
+ readFileTextEnvelopeAnyOf
72
+ [FromSomeType asType WitnessGenesisUTxOKey ]
73
+ (signingKey $ paymentKeyInfoPair wallet0)
74
+
70
75
--------------
71
76
-- RPC queries
72
77
--------------
73
78
let rpcServer = Rpc. ServerUnix rpcSocket
74
79
(pparamsResponse, utxosResponse) <- H. noteShowM . H. evalIO . Rpc. withConnection def rpcServer $ \ conn -> do
75
80
pparams' <- do
76
- let req = Rpc. defMessage
81
+ let req = def
77
82
Rpc. nonStreaming conn (Rpc. rpc @ (Rpc. Protobuf UtxoRpc. QueryService " readParams" )) req
78
83
79
84
utxos' <- do
80
- let req = Rpc. defMessage
85
+ let req = def & # addresses . # items .~ [ T. encodeUtf8 addrTxt0]
81
86
Rpc. nonStreaming conn (Rpc. rpc @ (Rpc. Protobuf UtxoRpc. QueryService " readUtxos" )) req
82
87
pure (pparams', utxos')
83
88
84
- H. failure
89
+ pparams <- H. leftFail $ utxoRpcPParamsToProtocolParams (convert ceo) $ pparamsResponse ^. # values . # cardano
90
+
91
+ txOut0 : _ <- H. noteShow $ utxosResponse ^. # items
92
+ txIn0 <- txoRefToTxIn $ txOut0 ^. # txoRef
93
+
94
+ let outputCoin = txOut0 ^. # cardano . # coin . to fromIntegral
95
+ amount = 200_000_000
96
+ fee = 500
97
+ change = outputCoin - amount - fee
98
+ txOut = TxOut addr1 (lovelaceToTxOutValue sbe $ L. Coin amount) TxOutDatumNone ReferenceScriptNone
99
+ changeTxOut = TxOut addr0 (lovelaceToTxOutValue sbe $ L. Coin change) TxOutDatumNone ReferenceScriptNone
100
+ content =
101
+ defaultTxBodyContent sbe
102
+ & setTxIns [(txIn0, pure $ KeyWitness KeyWitnessForSpending )]
103
+ & setTxFee (TxFeeExplicit sbe 500 )
104
+ & setTxOuts [txOut, changeTxOut]
105
+ & setTxProtocolParams (pure . pure $ LedgerProtocolParameters pparams)
106
+
107
+ txBody <- H. leftFail $ createTransactionBody sbe content
108
+
109
+ let signedTx = signShelleyTransaction sbe txBody [wit0]
110
+ txId' <- H. noteShow . getTxId $ getTxBody signedTx
111
+
112
+ H. noteShowPretty_ utxosResponse
113
+
114
+ (utxos, submitResponse) <- H. noteShowM . H. evalIO . Rpc. withConnection def rpcServer $ \ conn -> do
115
+ submitResponse <-
116
+ Rpc. nonStreaming conn (Rpc. rpc @ (Rpc. Protobuf UtxoRpc. SubmitService " submitTx" )) $
117
+ def & # tx .~ [def & # raw .~ serialiseToCBOR signedTx]
118
+
119
+ fix $ \ loop -> do
120
+ resp <- Rpc. nonStreaming conn (Rpc. rpc @ (Rpc. Protobuf UtxoRpc. QueryService " readParams" )) def
121
+
122
+ let previousBlockNo = pparamsResponse ^. # ledgerTip . # height
123
+ currentBlockNo = resp ^. # ledgerTip . # height
124
+ -- wait for 2 blocks
125
+ when (previousBlockNo + 1 >= currentBlockNo) $ do
126
+ threadDelay 500_000
127
+ loop
128
+
129
+ utxos <-
130
+ Rpc. nonStreaming conn (Rpc. rpc @ (Rpc. Protobuf UtxoRpc. QueryService " readUtxos" )) $
131
+ def & # addresses . # items .~ [T. encodeUtf8 addrTxt1]
132
+ pure (utxos, submitResponse)
133
+
134
+ submittedTxIds <- forM (submitResponse ^. # results) $ \ res -> do
135
+ let mErr = res ^. # maybe'errorMessage
136
+ mTxId = res ^. # maybe'ref
137
+ case (mErr, mTxId) of
138
+ (Just err, Nothing ) -> H. noteShow_ err >> H. failure
139
+ (Nothing , Just txId'') ->
140
+ H. leftFail $ deserialiseFromRawBytes AsTxId txId''
141
+ _ -> do
142
+ H. note_ $ " Protocol error: " <> show res
143
+ H. failure
144
+
145
+ H. note_ " Ensure that submitted transaction ID is in the submitted transactions list"
146
+ [txId'] === submittedTxIds
147
+
148
+ H. note_ $ " Enxure that there are 2 UTXOs in the address " <> show addrTxt1
149
+ 2 === length (utxos ^. # items)
150
+
151
+ let outputsAmounts = map (^. # cardano . # coin) $ utxos ^. # items
152
+ H. note_ $ " Ensure that the output sent is one of the utxos for the address " <> show addrTxt1
153
+ H. assertWith outputsAmounts $ elem (fromIntegral amount)
154
+
155
+ txoRefToTxIn :: (HasCallStack , MonadTest m ) => Proto UtxoRpc. TxoRef -> m TxIn
156
+ txoRefToTxIn r = withFrozenCallStack $ do
157
+ txId' <- H. leftFail $ deserialiseFromRawBytes AsTxId $ r ^. # hash
158
+ pure $ TxIn txId' (TxIx . fromIntegral $ r ^. # index)
0 commit comments