@@ -7,18 +7,26 @@ import Hydra.Prelude
7
7
import Test.Hydra.Prelude
8
8
9
9
import Cardano.Api.UTxO qualified as UTxO
10
+ import Cardano.Ledger.Alonzo.Tx (hashScriptIntegrity )
11
+ import Cardano.Ledger.Api.PParams (AlonzoEraPParams , PParams , getLanguageView )
12
+ import Cardano.Ledger.Api.Tx (EraTx , bodyTxL , datsTxWitsL , rdmrsTxWitsL , witsTxL )
13
+ import Cardano.Ledger.Api.Tx qualified as Ledger
14
+ import Cardano.Ledger.Api.Tx.Body (AlonzoEraTxBody , scriptIntegrityHashTxBodyL )
15
+ import Cardano.Ledger.Api.Tx.Wits (AlonzoEraTxWits )
16
+ import Cardano.Ledger.Plutus.Language (Language (PlutusV3 ))
10
17
import CardanoClient (
11
18
QueryPoint (QueryTip ),
12
19
RunningNode (.. ),
13
20
buildTransaction ,
21
+ buildTransactionWithPParams ,
14
22
queryTip ,
15
23
queryUTxOFor ,
16
24
submitTx ,
17
25
waitForUTxO ,
18
26
)
19
27
import CardanoNode (NodeLog )
20
28
import Control.Concurrent.Async (mapConcurrently_ )
21
- import Control.Lens ((^..) , (^?) )
29
+ import Control.Lens ((.~) , (^.) , ( ^..) , (^?) )
22
30
import Data.Aeson (Value , object , (.=) )
23
31
import Data.Aeson qualified as Aeson
24
32
import Data.Aeson.Lens (key , values , _JSON , _String )
@@ -37,12 +45,17 @@ import Hydra.Cardano.Api (
37
45
Coin (.. ),
38
46
File (File ),
39
47
Key (SigningKey ),
48
+ KeyWitnessInCtx (KeyWitnessForSpending ),
40
49
PaymentKey ,
41
50
Tx ,
42
51
TxId ,
43
52
UTxO ,
44
53
addTxIns ,
54
+ addTxInsCollateral ,
55
+ addTxOuts ,
56
+ createAndValidateTransactionBody ,
45
57
defaultTxBodyContent ,
58
+ fromLedgerTx ,
46
59
getTxBody ,
47
60
getTxId ,
48
61
getVerificationKey ,
@@ -51,16 +64,24 @@ import Hydra.Cardano.Api (
51
64
mkScriptAddress ,
52
65
mkScriptDatum ,
53
66
mkScriptWitness ,
67
+ mkTxIn ,
68
+ mkTxOutAutoBalance ,
54
69
mkTxOutDatumHash ,
55
70
mkVkAddress ,
56
71
scriptWitnessInCtx ,
57
72
selectLovelace ,
73
+ setTxProtocolParams ,
58
74
signTx ,
75
+ toLedgerTx ,
59
76
toScriptData ,
60
77
txOutValue ,
78
+ txOuts' ,
61
79
utxoFromTx ,
62
80
writeFileTextEnvelope ,
63
81
pattern BuildTxWith ,
82
+ pattern KeyWitness ,
83
+ pattern LedgerProtocolParameters ,
84
+ pattern PlutusScriptWitness ,
64
85
pattern ReferenceScriptNone ,
65
86
pattern ScriptWitness ,
66
87
pattern TxOut ,
@@ -73,6 +94,7 @@ import Hydra.Cluster.Mithril (MithrilLog)
73
94
import Hydra.Cluster.Options (Options )
74
95
import Hydra.Cluster.Util (chainConfigFor , keysFor , modifyConfig , setNetworkId )
75
96
import Hydra.Ledger.Cardano (mkSimpleTx , mkTransferTx , unsafeBuildTransaction )
97
+ import Hydra.Ledger.Cardano.Evaluate (maxTxExecutionUnits )
76
98
import Hydra.Logging (Tracer , traceWith )
77
99
import Hydra.Options (DirectChainConfig (.. ), networkId , startChainFrom )
78
100
import Hydra.Tx (HeadId , IsTx (balance ), Party , txId )
@@ -381,6 +403,145 @@ singlePartyCommitsFromExternal tracer workDir node hydraScriptsTxId =
381
403
where
382
404
RunningNode {nodeSocket, blockTime} = node
383
405
406
+ singlePartyUsesScriptOnL2 ::
407
+ Tracer IO EndToEndLog ->
408
+ FilePath ->
409
+ RunningNode ->
410
+ [TxId ] ->
411
+ IO ()
412
+ singlePartyUsesScriptOnL2 tracer workDir node hydraScriptsTxId =
413
+ ( `finally`
414
+ do
415
+ returnFundsToFaucet tracer node Alice
416
+ returnFundsToFaucet tracer node AliceFunds
417
+ )
418
+ $ do
419
+ refuelIfNeeded tracer node Alice 250_000_000
420
+ aliceChainConfig <- chainConfigFor Alice workDir nodeSocket hydraScriptsTxId [] $ UnsafeContestationPeriod 1
421
+ let hydraNodeId = 1
422
+ let hydraTracer = contramap FromHydraNode tracer
423
+ withHydraNode hydraTracer aliceChainConfig workDir hydraNodeId aliceSk [] [1 ] $ \ n1 -> do
424
+ send n1 $ input " Init" []
425
+ headId <- waitMatch (10 * blockTime) n1 $ headIsInitializingWith (Set. fromList [alice])
426
+
427
+ (walletVk, walletSk) <- keysFor AliceFunds
428
+
429
+ -- Create money on L1
430
+ let commitAmount = 100_000_000
431
+ utxoToCommit <- seedFromFaucet node walletVk commitAmount (contramap FromFaucet tracer)
432
+
433
+ -- Push it into L2
434
+ requestCommitTx n1 utxoToCommit
435
+ <&> signTx walletSk >>= \ tx -> do
436
+ submitTx node tx
437
+
438
+ -- Check UTxO is present in L2
439
+ waitFor hydraTracer (10 * blockTime) [n1] $
440
+ output " HeadIsOpen" [" utxo" .= toJSON utxoToCommit, " headId" .= headId]
441
+
442
+ pparamsReq <-
443
+ parseUrlThrow (" GET " <> hydraNodeBaseUrl n1 <> " /protocol-parameters" )
444
+ >>= httpJSON
445
+ let pparams = getResponseBody pparamsReq
446
+
447
+ -- Send the UTxO to a script; in preparation for running the script
448
+ let serializedScript = dummyValidatorScript
449
+ let scriptAddress = mkScriptAddress networkId serializedScript
450
+ let scriptOutput =
451
+ mkTxOutAutoBalance
452
+ pparams
453
+ scriptAddress
454
+ (lovelaceToValue 0 )
455
+ (mkTxOutDatumHash () )
456
+ ReferenceScriptNone
457
+
458
+ Right tx <- buildTransactionWithPParams pparams networkId nodeSocket (mkVkAddress networkId walletVk) utxoToCommit [] [scriptOutput]
459
+
460
+ let signedL2tx = signTx walletSk tx
461
+ send n1 $ input " NewTx" [" transaction" .= signedL2tx]
462
+
463
+ waitMatch 10 n1 $ \ v -> do
464
+ guard $ v ^? key " tag" == Just " SnapshotConfirmed"
465
+ guard $
466
+ toJSON signedL2tx
467
+ `elem` (v ^.. key " snapshot" . key " confirmed" . values)
468
+
469
+ -- Now, spend the money from the script
470
+ let scriptWitness =
471
+ BuildTxWith $
472
+ ScriptWitness scriptWitnessInCtx $
473
+ PlutusScriptWitness
474
+ serializedScript
475
+ (mkScriptDatum () )
476
+ (toScriptData () )
477
+ maxTxExecutionUnits
478
+
479
+ let txIn = mkTxIn signedL2tx 0
480
+ let remainder = mkTxIn signedL2tx 1
481
+
482
+ let outAmt = foldMap txOutValue (txOuts' tx)
483
+ let body =
484
+ defaultTxBodyContent
485
+ & addTxIns [(txIn, scriptWitness), (remainder, BuildTxWith $ KeyWitness KeyWitnessForSpending )]
486
+ & addTxInsCollateral [remainder]
487
+ & addTxOuts [TxOut (mkVkAddress networkId walletVk) outAmt TxOutDatumNone ReferenceScriptNone ]
488
+ & setTxProtocolParams (BuildTxWith $ Just $ LedgerProtocolParameters pparams)
489
+
490
+ -- TODO: Instead of using `createAndValidateTransactionBody`, we
491
+ -- should be able to just construct the Tx with autobalancing via
492
+ -- `buildTransactionWithBody`. Unfortunately this is broken in the
493
+ -- version of cardano-api that we presently use; in a future upgrade
494
+ -- of that library we can try again.
495
+ -- tx' <- either (failure . show) pure =<< buildTransactionWithBody networkId nodeSocket (mkVkAddress networkId walletVk) body utxoToCommit
496
+ txBody <- either (failure . show ) pure (createAndValidateTransactionBody body)
497
+
498
+ let spendTx' = makeSignedTransaction [] txBody
499
+ spendTx = fromLedgerTx $ recomputeIntegrityHash pparams [PlutusV3 ] (toLedgerTx spendTx')
500
+ let signedTx = signTx walletSk spendTx
501
+
502
+ send n1 $ input " NewTx" [" transaction" .= signedTx]
503
+
504
+ waitMatch 10 n1 $ \ v -> do
505
+ guard $ v ^? key " tag" == Just " SnapshotConfirmed"
506
+ guard $
507
+ toJSON signedTx
508
+ `elem` (v ^.. key " snapshot" . key " confirmed" . values)
509
+
510
+ -- And check that we can close and fanout the head successfully
511
+ send n1 $ input " Close" []
512
+ deadline <- waitMatch (10 * blockTime) n1 $ \ v -> do
513
+ guard $ v ^? key " tag" == Just " HeadIsClosed"
514
+ v ^? key " contestationDeadline" . _JSON
515
+ remainingTime <- diffUTCTime deadline <$> getCurrentTime
516
+ waitFor hydraTracer (remainingTime + 3 * blockTime) [n1] $
517
+ output " ReadyToFanout" [" headId" .= headId]
518
+ send n1 $ input " Fanout" []
519
+ waitMatch (10 * blockTime) n1 $ \ v ->
520
+ guard $ v ^? key " tag" == Just " HeadIsFinalized"
521
+
522
+ -- Assert final wallet balance
523
+ (balance <$> queryUTxOFor networkId nodeSocket QueryTip walletVk)
524
+ `shouldReturn` lovelaceToValue commitAmount
525
+ where
526
+ RunningNode {networkId, nodeSocket, blockTime} = node
527
+ hydraNodeBaseUrl HydraClient {hydraNodeId} = " http://127.0.0.1:" <> show (4000 + hydraNodeId)
528
+
529
+ -- | Compute the integrity hash of a transaction using a list of plutus languages.
530
+ recomputeIntegrityHash ::
531
+ (AlonzoEraPParams ppera , AlonzoEraTxWits txera , AlonzoEraTxBody txera , EraTx txera ) =>
532
+ PParams ppera ->
533
+ [Language ] ->
534
+ Ledger. Tx txera ->
535
+ Ledger. Tx txera
536
+ recomputeIntegrityHash pp languages tx = do
537
+ tx & bodyTxL . scriptIntegrityHashTxBodyL .~ integrityHash
538
+ where
539
+ integrityHash =
540
+ hashScriptIntegrity
541
+ (Set. fromList $ getLanguageView pp <$> languages)
542
+ (tx ^. witsTxL . rdmrsTxWitsL)
543
+ (tx ^. witsTxL . datsTxWitsL)
544
+
384
545
singlePartyCommitsScriptBlueprint ::
385
546
Tracer IO EndToEndLog ->
386
547
FilePath ->
0 commit comments