Skip to content

Commit 8300366

Browse files
Merge #4292
4292: tx-generator cleanups r=MarcFontaine a=MarcFontaine * Replace `IO` with `Monad m => m` in some types. * reintroduce `ToUTxOList`. * Remove legacy tx-set based `FundSet`. (Use `type FundSet = [ Fund ]` for now.) Co-authored-by: MarcFontaine <[email protected]>
2 parents 4334fbe + fa2c665 commit 8300366

File tree

12 files changed

+232
-388
lines changed

12 files changed

+232
-388
lines changed

bench/tx-generator/src/Cardano/Benchmarking/Compiler.hs

+9-7
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@ compileToScript = do
4848
emit . StartProtocol =<< askNixOption getNodeConfigFile
4949
genesisWallet <- newWallet "genesis_wallet"
5050
importGenesisFunds genesisWallet
51-
splitWallet <- splittingPhase genesisWallet
5251
collateralWallet <- addCollaterals genesisWallet
52+
splitWallet <- splittingPhase genesisWallet
5353
benchmarkingPhase splitWallet collateralWallet
5454

5555
initConstants :: Compiler ()
@@ -78,11 +78,13 @@ addCollaterals src = do
7878
isAnyPlutusMode >>= \case
7979
False -> return Nothing
8080
True -> do
81-
tx_fee <- askNixOption _nix_tx_fee
8281
safeCollateral <- _safeCollateral <$> evilFeeMagic
83-
collateralWallet <- newWallet "collaeral_wallet"
84-
emit $ CreateChange era src src LocalSocket (PayToAddr $ KeyName "pass-partout") (safeCollateral + tx_fee) 1
85-
emit $ CreateChange era src collateralWallet LocalSocket (PayToCollateral $ KeyName "pass-partout") safeCollateral 1
82+
collateralWallet <- newWallet "collateral_wallet"
83+
emit $ CreateChange era src LocalSocket
84+
(PayToAddr (KeyName "pass-partout") collateralWallet)
85+
(PayToAddr (KeyName "pass-partout") src)
86+
safeCollateral
87+
1
8688
return $ Just collateralWallet
8789

8890
splittingPhase :: SrcWallet -> Compiler DstWallet
@@ -99,7 +101,7 @@ splittingPhase srcWallet = do
99101
where
100102
createChange :: AnyCardanoEra -> SplitStep -> Compiler DstWallet
101103
createChange era (src, dst, value, count) = do
102-
emit $ CreateChange era src dst LocalSocket (PayToAddr $ KeyName "pass-partout") value count
104+
emit $ CreateChange era src LocalSocket (PayToAddr (KeyName "pass-partout") dst ) (PayToAddr (KeyName "pass-partout") src) value count
103105
delay
104106
return dst
105107

@@ -116,7 +118,7 @@ splittingPhase srcWallet = do
116118
<*> (ScriptDataNumber <$> askNixOption _nix_plutusRedeemer)
117119
<*> pure executionUnits
118120
ScriptSpec <$> askNixOption _nix_plutusScript <*> pure budget
119-
emit $ CreateChange era src dst LocalSocket (PayToScript scriptSpec) value count
121+
emit $ CreateChange era src LocalSocket (PayToScript scriptSpec dst) (PayToScript scriptSpec src) value count
120122
delay
121123
return dst
122124

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module Cardano.Benchmarking.Fifo
2+
where
3+
import Prelude
4+
5+
-- This is to be used single threaded behind an MVar.
6+
7+
data Fifo a = Fifo ![a] ![a]
8+
9+
emptyFifo :: Fifo a
10+
emptyFifo = Fifo [] []
11+
12+
-- Warning : bad complexity when used as a persistent data structure.
13+
toList :: Fifo a -> [a]
14+
toList (Fifo x y) = x ++ reverse y
15+
16+
insert :: Fifo a -> a -> Fifo a
17+
insert (Fifo x y) e = Fifo x $ e:y
18+
19+
remove :: Fifo a -> Maybe (Fifo a, a)
20+
remove fifo = case fifo of
21+
Fifo [] [] -> Nothing
22+
Fifo (h:t) y -> Just (Fifo t y, h)
23+
Fifo [] y -> case reverse y of
24+
(h:t) -> Just (Fifo t [], h)
25+
[] -> error "unreachable"
26+
27+
removeN :: Int -> Fifo a -> Maybe (Fifo a, [a])
28+
removeN 0 f = return (f, [])
29+
removeN n f = do
30+
(a, h) <- remove f
31+
(r, t) <- removeN (pred n) a
32+
return (r, h:t)

bench/tx-generator/src/Cardano/Benchmarking/FundSet.hs

+10-185
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
{-# Language DataKinds #-}
33
{-# Language FlexibleInstances #-}
44
{-# Language GADTs #-}
5-
{-# Language GeneralizedNewtypeDeriving #-}
65
{-# Language MultiParamTypeClasses #-}
76
{-# Language RankNTypes #-}
87
{-# Language TypeApplications #-}
@@ -12,12 +11,10 @@ module Cardano.Benchmarking.FundSet
1211
where
1312
import Prelude
1413

15-
import Data.IxSet.Typed as IxSet
16-
import Data.Proxy
17-
18-
import Control.Applicative ((<|>))
1914
import Cardano.Api as Api
2015

16+
import Cardano.Benchmarking.Fifo as Fifo
17+
2118
-- Outputs that are available for spending.
2219
-- When building a new TX they provide the TxIn parts.
2320

@@ -26,50 +23,27 @@ data FundInEra era = FundInEra {
2623
, _fundWitness :: Witness WitCtxTxIn era
2724
, _fundVal :: !(TxOutValue era)
2825
, _fundSigningKey :: !(Maybe (SigningKey PaymentKey))
29-
, _fundVariant :: !Variant
30-
, _fundValidity :: !Validity
3126
} deriving (Show)
3227

33-
data Variant
34-
= PlainOldFund
35-
-- maybe better use the script itself instead of the filePath
36-
| PlutusScriptFund
37-
-- A collateralFund is just a regular (PlainOldFund) on the chain,
38-
-- but tagged in the wallet so that it is not selected for spending.
39-
| CollateralFund
40-
deriving (Show, Eq, Ord)
41-
42-
data Validity
43-
= Confirmed
44-
| InFlight !Target !SeqNumber
45-
deriving (Show, Eq, Ord)
46-
47-
newtype Target = Target String
48-
deriving (Show, Eq, Ord)
49-
50-
newtype SeqNumber = SeqNumber Int
51-
deriving (Show, Eq, Ord, Enum)
52-
5328
newtype Fund = Fund {unFund :: InAnyCardanoEra FundInEra}
5429

55-
getFundVariant :: Fund -> Variant
56-
getFundVariant (Fund (InAnyCardanoEra _ a)) = _fundVariant a
30+
type FundSet = Fifo Fund
31+
32+
type FundSource m = m (Either String [Fund])
33+
type FundToStore m = Fund -> m ()
34+
type FundToStoreList m = [Fund] -> m ()
5735

5836
getFundTxIn :: Fund -> TxIn
5937
getFundTxIn (Fund (InAnyCardanoEra _ a)) = _fundTxIn a
6038

6139
getFundKey :: Fund -> Maybe (SigningKey PaymentKey)
6240
getFundKey (Fund (InAnyCardanoEra _ a)) = _fundSigningKey a
6341

64-
getFundValidity :: Fund -> Validity
65-
getFundValidity (Fund (InAnyCardanoEra _ a)) = _fundValidity a
66-
6742
getFundLovelace :: Fund -> Lovelace
6843
getFundLovelace (Fund (InAnyCardanoEra _ a)) = case _fundVal a of
6944
TxOutAdaOnly _era l -> l
7045
TxOutValue _era v -> selectLovelace v
7146

72-
7347
-- This effectively rules out era-transitions for transactions !
7448
-- This is not what we want !!
7549
getFundWitness :: forall era. IsShelleyBasedEra era => Fund -> Witness WitCtxTxIn era
@@ -85,14 +59,6 @@ getFundWitness fund = case (cardanoEra @ era, fund) of
8559
-- It should be possible to cast KeyWitnesses from one era to an other !
8660
(_ , _) -> error "getFundWitness: era mismatch"
8761

88-
data IsConfirmed = IsConfirmed | IsNotConfirmed
89-
deriving (Show, Eq, Ord)
90-
91-
isConfirmed :: Fund -> IsConfirmed
92-
isConfirmed f = case getFundValidity f of
93-
Confirmed -> IsConfirmed
94-
InFlight _ _ -> IsNotConfirmed
95-
9662
instance Show Fund where
9763
show (Fund (InAnyCardanoEra _ f)) = show f
9864

@@ -105,32 +71,11 @@ instance Eq Fund where
10571
instance Ord Fund where
10672
compare a b = compare (getFundTxIn a) (getFundTxIn b)
10773

108-
type FundIndices = '[ TxIn, IsConfirmed, Target, SeqNumber, Lovelace, Variant ]
109-
type FundSet = IxSet FundIndices Fund
110-
111-
instance Indexable FundIndices Fund where
112-
indices = ixList
113-
(ixFun $ \f -> [ getFundTxIn f ])
114-
(ixFun $ \f -> [ isConfirmed f ])
115-
(ixFun $ \f -> case getFundValidity f of
116-
Confirmed -> []
117-
InFlight t _ -> [t]
118-
)
119-
(ixFun $ \f -> case getFundValidity f of
120-
Confirmed -> [SeqNumber (-1) ] -- Confirmed Txs get SeqNumber -1
121-
InFlight _ n -> [ n ]
122-
)
123-
(ixFun $ \f -> [ getFundLovelace f ])
124-
(ixFun $ \f -> [ getFundVariant f ])
125-
126-
emptyFunds :: FundSet
127-
emptyFunds = IxSet.empty
74+
emptyFundSet :: FundSet
75+
emptyFundSet = Fifo.emptyFifo
12876

12977
insertFund :: FundSet -> Fund -> FundSet
130-
insertFund s f = updateIx (getFundTxIn f) f s
131-
132-
deleteFund :: FundSet -> Fund -> FundSet
133-
deleteFund s f = deleteIx (getFundTxIn f) s
78+
insertFund = Fifo.insert
13479

13580
liftAnyEra :: ( forall era. IsCardanoEra era => f1 era -> f2 era ) -> InAnyCardanoEra f1 -> InAnyCardanoEra f2
13681
liftAnyEra f x = case x of
@@ -141,126 +86,6 @@ liftAnyEra f x = case x of
14186
InAnyCardanoEra AlonzoEra a -> InAnyCardanoEra AlonzoEra $ f a
14287
InAnyCardanoEra BabbageEra a -> InAnyCardanoEra BabbageEra $ f a
14388

144-
type FundSelector = FundSet -> Either String [Fund]
145-
type FundSource = IO (Either String [Fund])
146-
type FundToStore = [Fund] -> IO ()
147-
148-
-- Select Funds to cover a minimum value.
149-
-- TODO:
150-
-- This fails unless there is a single fund with the required value
151-
-- Extend this to really return a list of funds.
152-
selectMinValue :: Lovelace -> FundSet -> Either String [Fund]
153-
selectMinValue minValue fs = case coins of
154-
[] -> Left $ "findSufficientCoin: no single coin with min value >= " ++ show minValue
155-
(c:_) -> Right [c]
156-
where coins = toAscList ( Proxy :: Proxy Lovelace) (fs @=PlainOldFund @= IsConfirmed @>= minValue)
157-
158-
selectCollateral :: FundSet -> Either String [Fund]
159-
selectCollateral fs = case coins of
160-
[] -> Left "no matching none-Plutus fund found"
161-
(c:_) -> Right [c]
162-
where
163-
coins = toAscList ( Proxy :: Proxy Lovelace) (fs @=CollateralFund @= IsConfirmed )
164-
165-
data AllowRecycle
166-
= UseConfirmedOnly
167-
| ReuseSameTarget
168-
-- ReuseAny can cause none-deterministic runtime errors !
169-
-- The problematic case is the reuse of an UTxO/Tx that is not yet confirmed
170-
-- and still waits in the mempool of an other target-node.
171-
| ReuseAny
172-
| ConfirmedBeforeReuse -- useful for testing
173-
deriving (Eq, Ord, Enum, Show)
174-
175-
-- There are many possible heuristics to implement the selectInputs function.
176-
-- TODO: Check that the complexity of selectInputs is good enough.
177-
selectInputs ::
178-
AllowRecycle
179-
-> Int
180-
-> Lovelace
181-
-> Variant
182-
-> Target
183-
-> FundSet
184-
-> Either String [Fund]
185-
selectInputs allowRecycle count minTotalValue variant targetNode fs
186-
= case allowRecycle of
187-
UseConfirmedOnly -> selectConfirmed
188-
ReuseSameTarget -> reuseSameTarget <|> selectConfirmed
189-
ReuseAny -> reuseSameTarget <|> selectConfirmed <|> reuseAnyCoin
190-
ConfirmedBeforeReuse -> selectConfirmed <|> reuseSameTarget
191-
where
192-
selectConfirmed = selectConfirmedSmallValue <|> selectConfirmedBigValue
193-
194-
isSufficientCoins coins = length coins == count && sum (map getFundLovelace coins) >= minTotalValue
195-
196-
checkCoins :: String -> [Fund] -> Either String [Fund]
197-
checkCoins err coins
198-
= if isSufficientCoins coins then Right coins else Left err
199-
200-
-- Share intermediate results for variantIxSet confirmedIxSet and targetIxSet
201-
-- TODO: it unclear if this helps on the complexity or it it is even harmful.
202-
variantIxSet = fs @= variant
203-
confirmedIxSet = variantIxSet @= IsConfirmed
204-
targetIxSet = variantIxSet @= targetNode
205-
206-
confirmedBigValueList = toDescList (Proxy :: Proxy Lovelace) confirmedIxSet
207-
sameTargetList = toAscList (Proxy :: Proxy SeqNumber) targetIxSet
208-
209-
selectConfirmedSmallValue
210-
= checkCoins
211-
"selectConfirmedSmall: not enough coins available"
212-
(take count $ toAscList (Proxy :: Proxy Lovelace) confirmedIxSet)
213-
214-
selectConfirmedBigValue
215-
= checkCoins
216-
"selectConfirmedSmall: not enough coins available"
217-
(take count confirmedBigValueList)
218-
219-
-- reuseSameTargetStrict is problematic: It fails if the coins in the queues are too small. But it will never consume the small coins.
220-
-- therefore: (reuseSameTargetStrict <|> reuseSameTargetWithBackup)
221-
reuseSameTargetStrict
222-
= checkCoins
223-
"reuseSameTargetStrict: not enough coins available"
224-
(take count sameTargetList)
225-
226-
-- reuseSameTargetWithBackup can collect some dust.
227-
-- reuseSameTargetWithBackup works fine if there is at least one sufficient confirmed UTxO available.
228-
reuseSameTargetWithBackup = checkCoins "reuseSameTargetWithBackup: not enough coins available" (backupCoin ++ targetCoins)
229-
where
230-
-- targetCoins and backupCoins must be disjoint.
231-
-- This is case because IsConfirmed \= InFlight target.
232-
backupCoin = take 1 $ toAscList (Proxy :: Proxy Lovelace) (confirmedIxSet @> minTotalValue)
233-
targetCoins = take (count - 1) sameTargetList
234-
235-
reuseSameTarget = reuseSameTargetStrict <|> reuseSameTargetWithBackup
236-
237-
-- reuseAnyCoin is the last resort !
238-
reuseAnyCoin
239-
= checkCoins
240-
"reuseAnyTarget: not enough coins available"
241-
(take count $ confirmedBigValueList ++ inFlightCoins)
242-
where
243-
-- inFlightCoins and confirmedCoins are disjoint
244-
inFlightCoins = toAscList (Proxy :: Proxy SeqNumber) (variantIxSet @=IsNotConfirmed)
245-
246-
selectToBuffer ::
247-
Int
248-
-> Lovelace
249-
-> Maybe Variant
250-
-> FundSet
251-
-> Either String [Fund]
252-
selectToBuffer count minValue variant fs
253-
= if length coins < count
254-
then Left $ concat
255-
[ "selectToBuffer: not enough coins found: count: ", show count
256-
, " minValue: ", show minValue
257-
, " variant: ", show variant
258-
]
259-
else Right coins
260-
where
261-
coins = case variant of
262-
Just v -> take count $ toAscList ( Proxy :: Proxy Lovelace) (fs @=v @= IsConfirmed @>= minValue)
263-
Nothing -> take count $ toAscList ( Proxy :: Proxy Lovelace) (fs @= IsConfirmed @>= minValue)
26489
-- Todo: check sufficient funds and minimumValuePerUtxo
26590
inputsToOutputsWithFee :: Lovelace -> Int -> [Lovelace] -> [Lovelace]
26691
inputsToOutputsWithFee fee count inputs = map (quantityToLovelace . Quantity) outputs

bench/tx-generator/src/Cardano/Benchmarking/GeneratorTx.hs

+2-3
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ import Ouroboros.Consensus.Shelley.Eras (StandardShelley)
3939

4040
import Cardano.Api hiding (txFee)
4141

42-
import qualified Cardano.Benchmarking.FundSet as FundSet
4342
import Cardano.Benchmarking.GeneratorTx.Error
4443
import Cardano.Benchmarking.GeneratorTx.Genesis
4544
import Cardano.Benchmarking.GeneratorTx.NodeToNode
@@ -152,7 +151,7 @@ walletBenchmark :: forall era. IsShelleyBasedEra era
152151
-> SubmissionErrorPolicy
153152
-> AsType era
154153
-> NumberOfTxs
155-
-> (FundSet.Target -> WalletScript era)
154+
-> WalletScript era
156155
-> ExceptT TxGenError IO AsyncBenchmarkControl
157156
walletBenchmark
158157
traceSubmit
@@ -184,7 +183,7 @@ walletBenchmark
184183
client = txSubmissionClient
185184
traceN2N
186185
traceSubmit
187-
(walletTxSource (walletScript (FundSet.Target $ show remoteAddr)) tpsThrottle)
186+
(walletTxSource walletScript tpsThrottle)
188187
(submitSubmissionThreadStats reportRef)
189188
async $ handle errorHandler (connectClient remoteAddr client)
190189

bench/tx-generator/src/Cardano/Benchmarking/ListBufferedSelector.hs

-36
This file was deleted.

0 commit comments

Comments
 (0)