Skip to content

Commit fa2c665

Browse files
committed
tx-generator: make good use of multiple wallets.
This commit allow one to sort the outputs of a transaction into multiple destinct wallets. In particular it makes it possible to put change outputs and collaterals in one wallet and the split-outputs in a other wallet.
1 parent 56f5a34 commit fa2c665

File tree

8 files changed

+117
-61
lines changed

8 files changed

+117
-61
lines changed

Diff for: bench/tx-generator/src/Cardano/Benchmarking/Compiler.hs

+8-6
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
8382
collateralWallet <- newWallet "collateral_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
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

Diff for: bench/tx-generator/src/Cardano/Benchmarking/Fifo.hs

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ data Fifo a = Fifo ![a] ![a]
99
emptyFifo :: Fifo a
1010
emptyFifo = Fifo [] []
1111

12+
-- Warning : bad complexity when used as a persistent data structure.
1213
toList :: Fifo a -> [a]
1314
toList (Fifo x y) = x ++ reverse y
1415

Diff for: bench/tx-generator/src/Cardano/Benchmarking/FundSet.hs

+2-3
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 #-}
@@ -31,7 +30,8 @@ newtype Fund = Fund {unFund :: InAnyCardanoEra FundInEra}
3130
type FundSet = Fifo Fund
3231

3332
type FundSource m = m (Either String [Fund])
34-
type FundToStore m = [Fund] -> m ()
33+
type FundToStore m = Fund -> m ()
34+
type FundToStoreList m = [Fund] -> m ()
3535

3636
getFundTxIn :: Fund -> TxIn
3737
getFundTxIn (Fund (InAnyCardanoEra _ a)) = _fundTxIn a
@@ -71,7 +71,6 @@ instance Eq Fund where
7171
instance Ord Fund where
7272
compare a b = compare (getFundTxIn a) (getFundTxIn b)
7373

74-
7574
emptyFundSet :: FundSet
7675
emptyFundSet = Fifo.emptyFifo
7776

Diff for: bench/tx-generator/src/Cardano/Benchmarking/Script/Action.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ action a = case a of
2121
AddFund era wallet txIn lovelace keyName -> addFund era wallet txIn lovelace keyName
2222
Delay t -> delay t
2323
ImportGenesisFund era wallet submitMode genesisKey fundKey -> importGenesisFund era wallet submitMode genesisKey fundKey
24-
CreateChange era sourceWallet dstWallet payMode submitMode value count -> createChange era sourceWallet dstWallet payMode submitMode value count
24+
CreateChange era sourceWallet payMode changeMode submitMode value count -> createChange era sourceWallet payMode changeMode submitMode value count
2525
RunBenchmark era sourceWallet submitMode thread auxArgs collateralWallet tps
2626
-> runBenchmark era sourceWallet submitMode thread auxArgs collateralWallet tps
2727
WaitBenchmark thread -> waitBenchmark thread

Diff for: bench/tx-generator/src/Cardano/Benchmarking/Script/Core.hs

+25-26
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ localSubmitTx tx = do
242242
-- It should be possible to exit the tx-generator with an exception and also get the log messages.
243243
-- Problem 1: When doing throwE $ ApiError msg logmessages get lost !
244244
-- Problem 2: Workbench restarts the tx-generator -> this may be the reason for loss of messages
245+
245246
makeMetadata :: forall era. IsShelleyBasedEra era => ActionM (TxMetadataInEra era)
246247
makeMetadata = do
247248
payloadSize <- getUser TTxAdditionalSize
@@ -273,7 +274,7 @@ runBenchmarkInEra :: forall era. IsShelleyBasedEra era
273274
runBenchmarkInEra sourceWallet submitMode (ThreadName threadName) shape collateralWallet tps era = do
274275
tracers <- get BenchTracers
275276
networkId <- getUser TNetworkId
276-
fundKey <- getName $ KeyName "pass-partout" -- should be walletkey
277+
fundKey <- getName $ KeyName "pass-partout" -- should be walletkey -- TODO: Remove magic
277278
protocolParameters <- getProtocolParameters
278279
walletRefSrc <- getName sourceWallet
279280
let walletRefDst = walletRefSrc
@@ -282,22 +283,22 @@ runBenchmarkInEra sourceWallet submitMode (ThreadName threadName) shape collater
282283
let fundSource = walletSource walletRefSrc (auxInputsPerTx shape)
283284

284285
collaterals <- selectCollateralFunds collateralWallet
285-
286286
let
287287
inToOut :: [Lovelace] -> [Lovelace]
288288
inToOut = FundSet.inputsToOutputsWithFee (auxFee shape) (auxOutputsPerTx shape)
289289

290290
txGenerator = genTx protocolParameters collaterals (mkFee (auxFee shape)) metadata
291291

292292
toUTxO :: [ ToUTxO era ]
293-
toUTxO = repeat $ Wallet.mkUTxOVariant networkId fundKey
293+
toUTxO = repeat $ Wallet.mkUTxOVariant networkId fundKey -- TODO: make configurable
294294

295-
fundToStore = mkWalletFundStore walletRefDst
295+
fundToStore = mkWalletFundStoreList walletRefDst
296296

297-
walletScript :: WalletScript era
298-
walletScript = benchmarkWalletScript walletRefSrc txGenerator (NumberOfTxs $ auxTxCount shape)
299-
fundSource inToOut toUTxO fundToStore
297+
sourceToStore = sourceToStoreTransaction txGenerator fundSource inToOut (makeToUTxOList toUTxO) fundToStore
300298

299+
walletScript :: WalletScript era
300+
walletScript = benchmarkWalletScript sourceToStore (NumberOfTxs $ auxTxCount shape)
301+
301302
case submitMode of
302303
NodeToNode targetNodes -> do
303304
connectClient <- getConnectClient
@@ -364,51 +365,49 @@ importGenesisFund era wallet submitMode genesisKeyName destKey = do
364365
initWallet :: WalletName -> ActionM ()
365366
initWallet name = liftIO Wallet.initWallet >>= setName name
366367

367-
createChange :: AnyCardanoEra -> WalletName -> WalletName -> SubmitMode -> PayMode -> Lovelace -> Int -> ActionM ()
368-
createChange era sourceWallet dstWallet submitMode payMode value count
369-
= withEra era $ createChangeInEra sourceWallet dstWallet submitMode payMode value count
368+
createChange :: AnyCardanoEra -> WalletName -> SubmitMode -> PayMode -> PayMode -> Lovelace -> Int -> ActionM ()
369+
createChange era sourceWallet submitMode payMode changeMode value count
370+
= withEra era $ createChangeInEra sourceWallet submitMode payMode changeMode value count
370371

371372
createChangeInEra :: forall era. IsShelleyBasedEra era
372373
=> WalletName
373-
-> WalletName
374374
-> SubmitMode
375375
-> PayMode
376+
-> PayMode
376377
-> Lovelace
377378
-> Int
378379
-> AsType era
379380
-> ActionM ()
380-
createChangeInEra sourceWallet dstWallet submitMode payMode value count _era = do
381-
walletRef <- getName dstWallet
381+
createChangeInEra sourceWallet submitMode payMode changeMode value count _era = do
382382
fee <- getUser TFee
383383
protocolParameters <- getProtocolParameters
384384
(toUTxO, addressMsg) <- interpretPayMode payMode
385+
(toUTxOChange, _) <- interpretPayMode changeMode
385386
let
386387
createCoins :: FundSet.FundSource IO -> [Lovelace] -> ActionM (Either String (TxInMode CardanoMode))
387388
createCoins fundSource coins = do
388-
(tx :: Either String (Tx era)) <- liftIO $ sourceToStoreTransaction
389+
(tx :: Either String (Tx era)) <- liftIO $ sourceToStoreTransactionNew
389390
(genTx protocolParameters (TxInsCollateralNone, [])
390391
(mkFee fee) TxMetadataNone )
391-
fundSource (Wallet.includeChange fee coins)
392-
(makeToUTxOList $ repeat toUTxO)
393-
(mkWalletFundStore walletRef)
392+
fundSource
393+
(Wallet.includeChangeNew fee coins)
394+
(mangleWithChange toUTxOChange toUTxO)
394395
return $ fmap txInModeCardano tx
395396
createChangeGeneric sourceWallet submitMode createCoins addressMsg value count
396397

397-
interpretPayMode :: forall era. IsShelleyBasedEra era => PayMode -> ActionM (ToUTxO era, String)
398+
interpretPayMode :: forall era. IsShelleyBasedEra era => PayMode -> ActionM (CreateAndStore IO era, String)
398399
interpretPayMode payMode = do
399400
networkId <- getUser TNetworkId
400401
case payMode of
401-
PayToAddr keyName -> do
402-
fundKey <- getName keyName
403-
return ( Wallet.mkUTxOVariant networkId fundKey
404-
, Text.unpack $ serialiseAddress $ keyAddress @ era networkId fundKey)
405-
PayToCollateral keyName -> do
402+
PayToAddr keyName destWallet -> do
406403
fundKey <- getName keyName
407-
return ( Wallet.mkUTxOVariant networkId fundKey
404+
walletRef <- getName destWallet
405+
return ( createAndStore (Wallet.mkUTxOVariant networkId fundKey) (mkWalletFundStore walletRef)
408406
, Text.unpack $ serialiseAddress $ keyAddress @ era networkId fundKey)
409-
PayToScript scriptSpec -> do
407+
PayToScript scriptSpec destWallet -> do
408+
walletRef <- getName destWallet
410409
(witness, script, scriptData, _scriptFee) <- makePlutusContext scriptSpec
411-
return ( mkUTxOScript networkId (script, scriptData) witness
410+
return ( createAndStore (mkUTxOScript networkId (script, scriptData) witness) (mkWalletFundStore walletRef)
412411
, Text.unpack $ serialiseAddress $ makeShelleyAddress networkId (PaymentCredentialByScript $ hashScript script) NoStakeAddress )
413412

414413
createChangeGeneric ::

Diff for: bench/tx-generator/src/Cardano/Benchmarking/Script/Selftest.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,9 @@ testScript protocolFile submitMode =
6666
era = AnyCardanoEra AllegraEra
6767
wallet = WalletName "test-wallet"
6868
key = KeyName "pass-partout"
69-
addr = PayToAddr key
69+
payMode = PayToAddr key wallet
7070
createChange val count
71-
= CreateChange era wallet wallet submitMode addr (Lovelace val) count
71+
= CreateChange era wallet submitMode payMode payMode (Lovelace val) count
7272
extraArgs = RunBenchmarkAux {
7373
auxTxCount = 4000
7474
, auxFee = 1000000

Diff for: bench/tx-generator/src/Cardano/Benchmarking/Script/Types.hs

+3-4
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ data Action where
3434
DefineSigningKey :: !KeyName -> !TextEnvelope -> Action
3535
AddFund :: !AnyCardanoEra -> !WalletName -> !TxIn -> !Lovelace -> !KeyName -> Action
3636
ImportGenesisFund :: !AnyCardanoEra -> !WalletName -> !SubmitMode -> !KeyName -> !KeyName -> Action
37-
CreateChange :: !AnyCardanoEra -> !WalletName -> !WalletName -> !SubmitMode -> !PayMode -> !Lovelace -> !Int -> Action
37+
CreateChange :: !AnyCardanoEra -> !WalletName -> !SubmitMode -> !PayMode -> !PayMode -> !Lovelace -> !Int -> Action
3838
RunBenchmark :: !AnyCardanoEra -> !WalletName -> !SubmitMode -> !ThreadName -> !RunBenchmarkAux -> Maybe WalletName -> !TPSRate -> Action
3939
WaitBenchmark :: !ThreadName -> Action
4040
CancelBenchmark :: !ThreadName -> Action
@@ -59,9 +59,8 @@ data SubmitMode where
5959
deriving instance Generic SubmitMode
6060

6161
data PayMode where
62-
PayToAddr :: !KeyName -> PayMode
63-
PayToCollateral :: !KeyName -> PayMode
64-
PayToScript :: !ScriptSpec -> PayMode
62+
PayToAddr :: !KeyName -> !WalletName -> PayMode
63+
PayToScript :: !ScriptSpec -> !WalletName -> PayMode
6564
deriving (Show, Eq)
6665
deriving instance Generic PayMode
6766

Diff for: bench/tx-generator/src/Cardano/Benchmarking/Wallet.hs

+75-19
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,21 @@ type WalletRef = MVar FundSet
2727
type TxGenerator era = [Fund] -> [TxOut CtxTx era] -> Either String (Tx era, TxId)
2828

2929
type ToUTxO era = Lovelace -> (TxOut CtxTx era, TxIx -> TxId -> Fund)
30+
type ToUTxOList era split = split -> ([TxOut CtxTx era], TxId -> [Fund])
31+
32+
type CreateAndStore m era = Lovelace -> (TxOut CtxTx era, TxIx -> TxId -> m ())
33+
34+
type CreateAndStoreList m era split = split -> ([TxOut CtxTx era], TxId -> m ())
3035

31-
--todo: inline inToOut :: [Lovelace] -> [Lovelace] and FundToStore
32-
type ToUTxOList era = [Lovelace] -> ([TxOut CtxTx era], TxId -> [Fund])
3336
-- 'ToUTxOList era' is more powerful than '[ ToUTxO era ]' but
3437
-- '[ ToUTxO era ]` is easier to construct.
3538

39+
createAndStore :: ToUTxO era -> (Fund -> m ()) -> CreateAndStore m era
40+
createAndStore create store lovelace = (utxo, toStore)
41+
where
42+
(utxo, mkFund) = create lovelace
43+
toStore txIx txId = store $ mkFund txIx txId
44+
3645
initWallet :: IO WalletRef
3746
initWallet = newMVar emptyFundSet
3847

@@ -44,16 +53,20 @@ askWalletRef r f = do
4453
walletRefInsertFund :: WalletRef -> Fund -> IO ()
4554
walletRefInsertFund ref fund = modifyMVar_ ref $ \w -> return $ FundSet.insertFund w fund
4655

47-
mkWalletFundStore :: WalletRef -> FundToStore IO
48-
mkWalletFundStore walletRef funds = modifyMVar_ walletRef
56+
mkWalletFundStoreList :: WalletRef -> FundToStoreList IO
57+
mkWalletFundStoreList walletRef funds = modifyMVar_ walletRef
4958
$ \wallet -> return (foldl FundSet.insertFund wallet funds)
5059

60+
mkWalletFundStore :: WalletRef -> FundToStore IO
61+
mkWalletFundStore walletRef fund = modifyMVar_ walletRef
62+
$ \wallet -> return $ FundSet.insertFund wallet fund
63+
5164
walletSource :: WalletRef -> Int -> FundSource IO
5265
walletSource ref munch = modifyMVar ref $ \fifo -> return $ case Fifo.removeN munch fifo of
5366
Nothing -> (fifo, Left "WalletSource: out of funds")
5467
Just (newFifo, funds) -> (newFifo, Right funds)
5568

56-
makeToUTxOList :: [ ToUTxO era ] -> ToUTxOList era
69+
makeToUTxOList :: [ ToUTxO era ] -> ToUTxOList era [ Lovelace ]
5770
makeToUTxOList fkts values
5871
= (outs, \txId -> map (\f -> f txId) fs)
5972
where
@@ -62,14 +75,32 @@ makeToUTxOList fkts values
6275
= let (o, f ) = toUTxO value
6376
in (o, f idx)
6477

78+
data PayWithChange
79+
= PayExact [Lovelace]
80+
| PayWithChange Lovelace [Lovelace]
81+
82+
mangleWithChange :: Monad m => CreateAndStore m era -> CreateAndStore m era -> CreateAndStoreList m era PayWithChange
83+
mangleWithChange mkChange mkPayment outs = case outs of
84+
PayExact l -> mangle (repeat mkPayment) l
85+
PayWithChange change payments -> mangle (mkChange : repeat mkPayment) (change : payments)
86+
87+
mangle :: Monad m => [ CreateAndStore m era ] -> CreateAndStoreList m era [ Lovelace ]
88+
mangle fkts values
89+
= (outs, \txId -> mapM_ (\f -> f txId) fs)
90+
where
91+
(outs, fs) =unzip $ map worker $ zip3 fkts values [TxIx 0 ..]
92+
worker (toUTxO, value, idx)
93+
= let (o, f ) = toUTxO value
94+
in (o, f idx)
95+
6596
--TODO use Error monad
6697
--TODO need to break this up
6798
sourceToStoreTransaction ::
6899
TxGenerator era
69100
-> FundSource IO
70-
-> ([Lovelace] -> [Lovelace]) --inline to ToUTxOList
71-
-> ToUTxOList era
72-
-> FundToStore IO --inline to ToUTxOList
101+
-> ([Lovelace] -> split)
102+
-> ToUTxOList era split
103+
-> FundToStoreList IO --inline to ToUTxOList
73104
-> IO (Either String (Tx era))
74105
sourceToStoreTransaction txGenerator fundSource inToOut mkTxOut fundToStore = do
75106
fundSource >>= \case
@@ -86,13 +117,41 @@ sourceToStoreTransaction txGenerator fundSource inToOut mkTxOut fundToStore = do
86117
fundToStore $ toFunds txId
87118
return $ Right tx
88119

120+
sourceToStoreTransactionNew ::
121+
TxGenerator era
122+
-> FundSource IO
123+
-> ([Lovelace] -> split)
124+
-> CreateAndStoreList IO era split
125+
-> IO (Either String (Tx era))
126+
sourceToStoreTransactionNew txGenerator fundSource valueSplitter toStore = do
127+
fundSource >>= \case
128+
Left err -> return $ Left err
129+
Right inputFunds -> work inputFunds
130+
where
131+
work inputFunds = do
132+
let
133+
split = valueSplitter $ map getFundLovelace inputFunds
134+
(outputs, storeAction) = toStore split
135+
case txGenerator inputFunds outputs of
136+
Left err -> return $ Left err
137+
Right (tx, txId) -> do
138+
storeAction txId
139+
return $ Right tx
140+
89141
includeChange :: Lovelace -> [Lovelace] -> [Lovelace] -> [Lovelace]
90142
includeChange fee spend have = case compare changeValue 0 of
91143
GT -> changeValue : spend
92144
EQ -> spend
93145
LT -> error "genTX: Bad transaction: insufficient funds"
94146
where changeValue = sum have - sum spend - fee
95147

148+
includeChangeNew :: Lovelace -> [Lovelace] -> [Lovelace] -> PayWithChange
149+
includeChangeNew fee spend have = case compare changeValue 0 of
150+
GT -> PayWithChange changeValue spend
151+
EQ -> PayExact spend
152+
LT -> error "genTX: Bad transaction: insufficient funds"
153+
where changeValue = sum have - sum spend - fee
154+
96155
mkUTxOVariant :: forall era. IsShelleyBasedEra era
97156
=> NetworkId
98157
-> SigningKey PaymentKey
@@ -195,26 +254,23 @@ data WalletStep era
195254
| Error String
196255

197256
-- TODO:
198-
-- use explicit tx- counter for each walletscript
199-
-- Do not rely on global walletSeqNum
257+
-- Define generator for a single transaction and define combinator for
258+
-- repeat and sequence.
259+
260+
200261
benchmarkWalletScript :: forall era .
201262
IsShelleyBasedEra era
202-
=> WalletRef
203-
-> TxGenerator era
263+
=> IO (Either String (Tx era)) -- make polymorphic
204264
-> NumberOfTxs
205-
-> FundSource IO
206-
-> ([Lovelace] -> [Lovelace])
207-
-> [ToUTxO era]
208-
-> FundToStore IO
209265
-> WalletScript era
210-
benchmarkWalletScript wRef txGenerator totalCount fundSource inOut toUTxO fundToStore
266+
benchmarkWalletScript sourceToStore totalCount
211267
= WalletScript $ walletStep totalCount
212268
where
213269
walletStep :: NumberOfTxs -> IO (WalletStep era)
214270
walletStep (NumberOfTxs 0) = return Done
215-
walletStep count = sourceToStoreTransaction txGenerator fundSource inOut (makeToUTxOList toUTxO) fundToStore >>= \case
271+
walletStep count = sourceToStore >>= \case
216272
Left err -> return $ Error err
217-
Right tx -> return $ NextTx (benchmarkWalletScript wRef txGenerator (pred count) fundSource inOut toUTxO fundToStore) tx
273+
Right tx -> return $ NextTx (benchmarkWalletScript sourceToStore (pred count)) tx
218274

219275
limitSteps ::
220276
NumberOfTxs

0 commit comments

Comments
 (0)