@@ -14,7 +14,7 @@ module BotPlutusInterface.CardanoCLI (
1414 queryTip ,
1515) where
1616
17- import BotPlutusInterface.Effects (PABEffect , ShellArgs (.. ), callCommand )
17+ import BotPlutusInterface.Effects (PABEffect , ShellArgs (.. ), callCommand , queryChainIndex )
1818import BotPlutusInterface.Files (
1919 DummyPrivKey (FromSKey , FromVKey ),
2020 datumJsonFilePath ,
@@ -24,7 +24,7 @@ import BotPlutusInterface.Files (
2424 txFilePath ,
2525 validatorScriptFilePath ,
2626 )
27- import BotPlutusInterface.Types (PABConfig , Tip )
27+ import BotPlutusInterface.Types (PABConfig ( pcSlotConfig ) , Tip )
2828import BotPlutusInterface.UtxoParser qualified as UtxoParser
2929import Cardano.Api.Shelley (NetworkId (Mainnet , Testnet ), NetworkMagic (.. ), serialiseAddress )
3030import Codec.Serialise qualified as Codec
@@ -33,6 +33,7 @@ import Control.Monad.Freer (Eff, Member)
3333import Data.Aeson qualified as JSON
3434import Data.Aeson.Extras (encodeByteString )
3535import Data.Attoparsec.Text (parseOnly )
36+ import Data.Bifunctor (second )
3637import Data.Bool (bool )
3738import Data.ByteString.Lazy qualified as LazyByteString
3839import Data.ByteString.Lazy.Char8 qualified as Char8
@@ -41,7 +42,7 @@ import Data.Either (fromRight)
4142import Data.Either.Combinators (mapLeft , maybeToRight )
4243import Data.Hex (hex )
4344import Data.Kind (Type )
44- import Data.List (sort )
45+ import Data.List (nub , sort )
4546import Data.Map (Map )
4647import Data.Map qualified as Map
4748import Data.Maybe (fromMaybe , maybeToList )
@@ -63,6 +64,7 @@ import Ledger.Interval (
6364 )
6465import Ledger.Scripts (Datum , DatumHash (.. ))
6566import Ledger.Scripts qualified as Scripts
67+ import Ledger.TimeSlot (slotRangeToPOSIXTimeRange )
6668import Ledger.Tx (
6769 ChainIndexTxOut ,
6870 RedeemerPtr (.. ),
@@ -77,7 +79,9 @@ import Ledger.Tx (
7779import Ledger.TxId (TxId (.. ))
7880import Ledger.Value (Value )
7981import Ledger.Value qualified as Value
82+ import Plutus.ChainIndex.Tx (txOutRefMap )
8083import Plutus.Contract.CardanoAPI (toCardanoAddress )
84+ import Plutus.Contract.Effects (ChainIndexQuery (TxsFromTxIds ), ChainIndexResponse (TxIdsResponse ))
8185import Plutus.V1.Ledger.Ada (fromValue , getLovelace )
8286import Plutus.V1.Ledger.Api (
8387 BuiltinData ,
@@ -86,7 +90,10 @@ import Plutus.V1.Ledger.Api (
8690 ExCPU (.. ),
8791 ExMemory (.. ),
8892 Script ,
93+ ScriptContext (ScriptContext ),
8994 TokenName (.. ),
95+ TxInInfo (TxInInfo ),
96+ TxInfo (TxInfo ),
9097 )
9198import Plutus.V1.Ledger.Api qualified as Plutus
9299import PlutusTx.Builtins (fromBuiltin )
@@ -176,6 +183,59 @@ calculateMinFee pabConf tx =
176183 , cmdOutParser = mapLeft Text. pack . parseOnly UtxoParser. feeParser . Text. pack
177184 }
178185
186+ queryTxOuts ::
187+ forall (w :: Type ) (effs :: [Type -> Type ]).
188+ Member (PABEffect w ) effs =>
189+ [TxId ] ->
190+ Eff effs (Either Text (Map TxOutRef TxOut ))
191+ queryTxOuts txIds = do
192+ res <- queryChainIndex @ w $ TxsFromTxIds txIds
193+ return $ case res of
194+ TxIdsResponse chainTxs -> Right $ foldMap (fmap fst . txOutRefMap) chainTxs
195+ _ -> Left " Wrong PAB response"
196+
197+ -- There is no match txOutRefs request, and we don't want a separate PAB query per input.
198+ -- So, for efficiency, we're going to query the transactions for all inputs combined,
199+ -- then pick out the outputs we care about
200+ getTxInInfos ::
201+ forall (w :: Type ) (effs :: [Type -> Type ]).
202+ Member (PABEffect w ) effs =>
203+ [TxOutRef ] ->
204+ Eff effs (Either Text [TxInInfo ])
205+ getTxInInfos txOutRefs = do
206+ let ids = nub $ txOutRefId <$> txOutRefs
207+ eAllOutRefs <- queryTxOuts @ w ids
208+ return $
209+ eAllOutRefs >>= \ allOutRefs ->
210+ sequence $ (\ ref -> toEither $ TxInInfo ref <$> Map. lookup ref allOutRefs) <$> txOutRefs
211+ where
212+ toEither :: Maybe TxInInfo -> Either Text TxInInfo
213+ toEither = maybeToRight " Couldn't find TxOut"
214+
215+ buildTxInfo ::
216+ forall (w :: Type ) (effs :: [Type -> Type ]).
217+ Member (PABEffect w ) effs =>
218+ PABConfig ->
219+ Tx ->
220+ Eff effs (Either Text TxInfo )
221+ buildTxInfo pabConf tx = do
222+ let txOutRefs = txInRef <$> Set. toList (txInputs tx)
223+ eTxInInfos <- getTxInInfos @ w txOutRefs
224+ return $
225+ (`second` eTxInInfos) $ \ txInInfos ->
226+ TxInfo
227+ { txInfoInputs = txInInfos
228+ , txInfoOutputs = txOutputs tx
229+ , txInfoFee = txFee tx
230+ , txInfoMint = txMint tx
231+ , txInfoDCert = [] -- We don't support staking or stake redeeming at this time
232+ , txInfoWdrl = []
233+ , txInfoValidRange = slotRangeToPOSIXTimeRange (pcSlotConfig pabConf) $ txValidRange tx
234+ , txInfoSignatories = Ledger. pubKeyHash <$> Map. keys (txSignatures tx)
235+ , txInfoData = Map. toList $ txData tx
236+ , txInfoId = Ledger. txId tx
237+ }
238+
179239-- | Build a tx body and write it to disk
180240buildTx ::
181241 forall (w :: Type ) (effs :: [Type -> Type ]).
@@ -184,8 +244,11 @@ buildTx ::
184244 Map PubKeyHash DummyPrivKey ->
185245 Tx ->
186246 Eff effs (Either Text () )
187- buildTx pabConf privKeys tx =
188- callCommand @ w $ ShellArgs " cardano-cli" opts (const () )
247+ buildTx pabConf privKeys tx = do
248+ eTxInfo <- buildTxInfo @ w pabConf tx
249+ case eTxInfo of
250+ Right txInfo -> callCommand @ w $ ShellArgs " cardano-cli" (opts txInfo) (const () )
251+ Left e -> return $ Left e
189252 where
190253 requiredSigners =
191254 concatMap
@@ -200,13 +263,13 @@ buildTx pabConf privKeys tx =
200263 []
201264 )
202265 (Map. keys (Ledger. txSignatures tx))
203- opts =
266+ opts txInfo =
204267 mconcat
205268 [ [" transaction" , " build-raw" , " --alonzo-era" ]
206- , txInOpts pabConf (txInputs tx)
269+ , txInOpts pabConf txInfo (txInputs tx)
207270 , txInCollateralOpts (txCollateral tx)
208271 , txOutOpts pabConf (txData tx) (txOutputs tx)
209- , mintOpts pabConf (txMintScripts tx) (txRedeemers tx) (txMint tx)
272+ , mintOpts pabConf txInfo (txMintScripts tx) (txRedeemers tx) (txMint tx)
210273 , validRangeOpts (txValidRange tx)
211274 , requiredSigners
212275 , [" --fee" , showText . getLovelace . fromValue $ txFee tx]
@@ -259,19 +322,20 @@ submitTx pabConf tx =
259322 )
260323 (const () )
261324
262- txInOpts :: PABConfig -> Set TxIn -> [Text ]
263- txInOpts pabConf =
325+ txInOpts :: PABConfig -> TxInfo -> Set TxIn -> [Text ]
326+ txInOpts pabConf txInfo =
264327 concatMap
265328 ( \ (TxIn txOutRef txInType) ->
266329 mconcat
267330 [ [" --tx-in" , txOutRefToCliArg txOutRef]
268331 , case txInType of
269332 Just (ConsumeScriptAddress validator redeemer datum) ->
270- let exBudget =
333+ let scriptContext = ScriptContext txInfo $ Plutus. Spending txOutRef
334+ exBudget =
271335 fromRight (ExBudget (ExCPU 0 ) (ExMemory 0 )) $
272336 calculateExBudget
273337 (Scripts. unValidatorScript validator)
274- [Plutus. getRedeemer redeemer, Plutus. getDatum datum]
338+ [Plutus. getRedeemer redeemer, Plutus. getDatum datum, Plutus. toBuiltinData scriptContext ]
275339 in mconcat
276340 [
277341 [ " --tx-in-script-file"
@@ -302,20 +366,21 @@ txInCollateralOpts =
302366 concatMap (\ (TxIn txOutRef _) -> [" --tx-in-collateral" , txOutRefToCliArg txOutRef]) . Set. toList
303367
304368-- Minting options
305- mintOpts :: PABConfig -> Set Scripts. MintingPolicy -> Redeemers -> Value -> [Text ]
306- mintOpts pabConf mintingPolicies redeemers mintValue =
369+ mintOpts :: PABConfig -> TxInfo -> Set Scripts. MintingPolicy -> Redeemers -> Value -> [Text ]
370+ mintOpts pabConf txInfo mintingPolicies redeemers mintValue =
307371 mconcat
308372 [ mconcat $
309373 concatMap
310374 ( \ (idx, policy) ->
311375 let redeemerPtr = RedeemerPtr Mint idx
312376 redeemer = Map. lookup redeemerPtr redeemers
313377 curSymbol = Value. mpsSymbol $ Scripts. mintingPolicyHash policy
378+ scriptContext = ScriptContext txInfo $ Plutus. Minting curSymbol
314379 exBudget r =
315380 fromRight (ExBudget (ExCPU 0 ) (ExMemory 0 )) $
316381 calculateExBudget
317382 (Scripts. unMintingPolicyScript policy)
318- [Plutus. getRedeemer r]
383+ [Plutus. getRedeemer r, Plutus. toBuiltinData scriptContext ]
319384 toOpts r =
320385 [ [" --mint-script-file" , policyScriptFilePath pabConf curSymbol]
321386 , [" --mint-redeemer-file" , redeemerJsonFilePath pabConf (Ledger. redeemerHash r)]
@@ -401,7 +466,7 @@ calculateExBudget :: Script -> [BuiltinData] -> Either Text ExBudget
401466calculateExBudget script builtinData = do
402467 modelParams <- maybeToRight " Cost model params invalid." Plutus. defaultCostModelParams
403468 let serialisedScript = ShortByteString. toShort $ LazyByteString. toStrict $ Codec. serialise script
404- let pData = map Plutus. builtinDataToData builtinData
469+ pData = map Plutus. builtinDataToData builtinData
405470 mapLeft showText $
406471 snd $
407472 Plutus. evaluateScriptCounting Plutus. Verbose modelParams serialisedScript pData
0 commit comments