Skip to content

Commit ff11a1a

Browse files
authored
Allow partial ada commit (#2160)
fix #2140 Allow users to specify _amount_ of lovelace to deposit which is very convenient since that way users don't need to assemble the UTxO with the exact amount they want to deposit to a Head. - NOTE: This workflow is only enabled on `SimpleCommitRequest` since the full one contains a blueprint tx that specifies how to spend utxo into a head which could conflict with these changes. --- <!-- Consider each and tick it off one way or the other --> * [x] CHANGELOG updated or not needed * [x] Documentation updated or not needed * [x] Haddocks updated or not needed * [x] No new TODOs introduced or explained herafter
2 parents 87a709e + b6466c8 commit ff11a1a

File tree

26 files changed

+1000
-502
lines changed

26 files changed

+1000
-502
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ changes.
1010

1111
## [0.23.0] - UNRELEASED
1212

13+
- Accept additional field `amount` when depositing to specify the amount of Lovelace that should be depositted to a Head returning any leftover to the user.
14+
1315
- Don't keep around invalid transactions as they could lead to stuck Head.
1416

1517
- Hydra API server responds with the correct `Content-Type` header `application-json`.

docs/docs/how-to/incremental-commit.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ cardano-cli query utxo \
4949
--out-file commit-utxo.json
5050
```
5151

52+
:::info
53+
You can also specify `amount` of lovelace you want to commit together with the `UTxO` and hydra-node would
54+
commit only the specified amount and return any leftover to the user address.
55+
:::
56+
5257
Then a request to the `/commit` endpoint provides us with a transaction:
5358

5459
```shell

hydra-cardano-api/src/Hydra/Cardano/Api.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ import Cardano.Api as X hiding (
8585
import Cardano.Api.Ledger as X (
8686
PParams,
8787
)
88+
import Cardano.Api.Ledger.Lens as X (
89+
mkAdaValue,
90+
)
8891
import Cardano.Api.Shelley as X (
8992
AcquiringFailure (..),
9093
Hash (HeaderHash),

hydra-cluster/src/Hydra/Cluster/Scenarios.hs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ import Hydra.Node.DepositPeriod (DepositPeriod (..))
109109
import Hydra.Options (CardanoChainConfig (..), ChainBackendOptions (..), DirectOptions (..), RunOptions (..), startChainFrom)
110110
import Hydra.Tx (HeadId, IsTx (balance), Party, txId)
111111
import Hydra.Tx.ContestationPeriod qualified as CP
112+
import Hydra.Tx.Deposit (capUTxO)
112113
import Hydra.Tx.Utils (dummyValidatorScript, verificationKeyToOnChainId)
113114
import HydraNode (
114115
HydraClient (..),
@@ -122,6 +123,7 @@ import HydraNode (
122123
postDecommit,
123124
prepareHydraNode,
124125
requestCommitTx,
126+
requestCommitTx',
125127
send,
126128
waitFor,
127129
waitForAllMatch,
@@ -563,6 +565,7 @@ singlePartyCommitsFromExternal tracer workDir backend hydraScriptsTxId =
563565

564566
(walletVk, walletSk) <- keysFor AliceFunds
565567
utxoToCommit <- seedFromFaucet backend walletVk 5_000_000 (contramap FromFaucet tracer)
568+
566569
res <-
567570
runReq defaultHttpConfig $
568571
req
@@ -1297,6 +1300,78 @@ canCommit tracer workDir blockTime backend hydraScriptsTxId =
12971300
where
12981301
hydraTracer = contramap FromHydraNode tracer
12991302

1303+
-- | Open a a two participant head and incrementally commit part of the UTxO.
1304+
canDepositPartially :: ChainBackend backend => Tracer IO EndToEndLog -> FilePath -> NominalDiffTime -> backend -> [TxId] -> IO ()
1305+
canDepositPartially tracer workDir blockTime backend hydraScriptsTxId =
1306+
(`finally` returnFundsToFaucet tracer backend Alice) $ do
1307+
(`finally` returnFundsToFaucet tracer backend Bob) $ do
1308+
refuelIfNeeded tracer backend Alice 30_000_000
1309+
refuelIfNeeded tracer backend Bob 30_000_000
1310+
-- NOTE: Adapt periods to block times
1311+
let contestationPeriod = truncate $ 10 * blockTime
1312+
depositPeriod = truncate $ 100 * blockTime
1313+
networkId <- Backend.queryNetworkId backend
1314+
aliceChainConfig <-
1315+
chainConfigFor Alice workDir backend hydraScriptsTxId [Bob] contestationPeriod
1316+
<&> setNetworkId networkId . modifyConfig (\c -> c{depositPeriod})
1317+
bobChainConfig <-
1318+
chainConfigFor Bob workDir backend hydraScriptsTxId [Alice] contestationPeriod
1319+
<&> setNetworkId networkId . modifyConfig (\c -> c{depositPeriod})
1320+
withHydraNode hydraTracer aliceChainConfig workDir 1 aliceSk [bobVk] [2] $ \n1 -> do
1321+
withHydraNode hydraTracer bobChainConfig workDir 2 bobSk [aliceVk] [1] $ \n2 -> do
1322+
send n1 $ input "Init" []
1323+
headId <- waitMatch (10 * blockTime) n2 $ headIsInitializingWith (Set.fromList [alice, bob])
1324+
1325+
-- Commit nothing
1326+
requestCommitTx n1 mempty >>= Backend.submitTransaction backend
1327+
requestCommitTx n2 mempty >>= Backend.submitTransaction backend
1328+
waitFor hydraTracer (20 * blockTime) [n1, n2] $
1329+
output "HeadIsOpen" ["utxo" .= object mempty, "headId" .= headId]
1330+
1331+
-- Get some L1 funds
1332+
(walletVk, walletSk) <- generate genKeyPair
1333+
commitUTxO <- seedFromFaucet backend walletVk 5_000_000 (contramap FromFaucet tracer)
1334+
-- This one is expected to fail since there is 5 ADA at the wallet address but we specified 6 ADA to commit
1335+
(requestCommitTx' n1 commitUTxO (Just 6_000_000) <&> toJSON)
1336+
`shouldThrow` expectErrorStatus 400 (Just "AmountTooLow")
1337+
1338+
let expectedCommit = fst $ capUTxO commitUTxO 2_000_000
1339+
1340+
depositTransaction <- requestCommitTx' n2 commitUTxO (Just 2_000_000)
1341+
1342+
let tx = signTx walletSk depositTransaction
1343+
1344+
Backend.submitTransaction backend tx
1345+
1346+
waitFor hydraTracer (2 * realToFrac depositPeriod) [n1, n2] $
1347+
output "CommitApproved" ["headId" .= headId, "utxoToCommit" .= expectedCommit]
1348+
waitFor hydraTracer (20 * blockTime) [n1, n2] $
1349+
output "CommitFinalized" ["headId" .= headId, "depositTxId" .= getTxId (getTxBody tx)]
1350+
1351+
getSnapshotUTxO n1 `shouldReturn` expectedCommit
1352+
-- check that user balance balance contains the change from the commit tx
1353+
(balance <$> Backend.queryUTxOFor backend QueryTip walletVk)
1354+
`shouldReturn` lovelaceToValue 3_000_000
1355+
1356+
send n2 $ input "Close" []
1357+
1358+
deadline <- waitMatch (10 * blockTime) n2 $ \v -> do
1359+
guard $ v ^? key "tag" == Just "HeadIsClosed"
1360+
v ^? key "contestationDeadline" . _JSON
1361+
1362+
remainingTime <- diffUTCTime deadline <$> getCurrentTime
1363+
waitFor hydraTracer (remainingTime + 3 * blockTime) [n1, n2] $
1364+
output "ReadyToFanout" ["headId" .= headId]
1365+
send n2 $ input "Fanout" []
1366+
waitMatch (10 * blockTime) n2 $ \v ->
1367+
guard $ v ^? key "tag" == Just "HeadIsFinalized"
1368+
1369+
-- Assert final wallet balance
1370+
(balance <$> Backend.queryUTxOFor backend QueryTip walletVk)
1371+
`shouldReturn` balance commitUTxO
1372+
where
1373+
hydraTracer = contramap FromHydraNode tracer
1374+
13001375
rejectCommit :: ChainBackend backend => Tracer IO EndToEndLog -> FilePath -> NominalDiffTime -> backend -> [TxId] -> IO ()
13011376
rejectCommit tracer workDir blockTime backend hydraScriptsTxId =
13021377
(`finally` returnFundsToFaucet tracer backend Alice) $ do

hydra-cluster/src/HydraNode.hs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,20 @@ waitForAll tracer delay nodes expected = do
189189
-- | Helper to make it easy to obtain a commit tx using some wallet utxo.
190190
-- Create a commit tx using the hydra-node for later submission.
191191
requestCommitTx :: HydraClient -> UTxO -> IO Tx
192-
requestCommitTx HydraClient{apiHost = Host{hostname, port}} utxos =
192+
requestCommitTx client utxos =
193+
requestCommitTx' client utxos Nothing
194+
195+
-- | Helper to make it easy to obtain a commit tx using some wallet utxo and optional amount.
196+
-- Create a commit tx using the hydra-node for later submission.
197+
requestCommitTx' :: HydraClient -> UTxO -> Maybe Coin -> IO Tx
198+
requestCommitTx' HydraClient{apiHost = Host{hostname, port}} utxos amount =
193199
runReq defaultHttpConfig request <&> commitTx . responseBody
194200
where
195201
request =
196202
Req.req
197203
POST
198204
(Req.http hostname /: "commit")
199-
(ReqBodyJson $ SimpleCommitRequest @Tx utxos)
205+
(ReqBodyJson $ SimpleCommitRequest @Tx utxos amount)
200206
(Proxy :: Proxy (JsonResponse (DraftCommitTxResponse Tx)))
201207
(Req.port (fromInteger . toInteger $ port))
202208

hydra-cluster/test/Test/BlockfrostChainSpec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ withBlockfrostChainTest tracer config party action = do
175175
{ postTx
176176
, waitCallback = atomically $ takeTMVar eventMVar
177177
, draftCommitTx = \headId utxo blueprintTx -> do
178-
eTx <- draftCommitTx headId $ CommitBlueprintTx{lookupUTxO = utxo, blueprintTx}
178+
eTx <- draftCommitTx headId CommitBlueprintTx{lookupUTxO = utxo, blueprintTx}
179179
case eTx of
180180
Left e -> throwIO e
181181
Right tx -> pure tx

hydra-cluster/test/Test/DirectChainSpec.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ withDirectChainTest tracer config party action = do
559559
{ postTx
560560
, waitCallback = atomically $ takeTMVar eventMVar
561561
, draftCommitTx = \headId utxo blueprintTx -> do
562-
eTx <- draftCommitTx headId $ CommitBlueprintTx{lookupUTxO = utxo, blueprintTx}
562+
eTx <- draftCommitTx headId CommitBlueprintTx{lookupUTxO = utxo, blueprintTx}
563563
case eTx of
564564
Left e -> throwIO e
565565
Right tx -> pure tx

hydra-cluster/test/Test/EndToEndSpec.hs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import Hydra.Cluster.Scenarios (
5252
canCloseWithLongContestationPeriod,
5353
canCommit,
5454
canDecommit,
55+
canDepositPartially,
5556
canRecoverDeposit,
5657
canResumeOnMemberAlreadyBootstrapped,
5758
canSeePendingDeposits,
@@ -320,6 +321,11 @@ spec = around (showLogsOnFailure "EndToEndSpec") $ do
320321
withBackend (contramap FromCardanoNode tracer) tmpDir $ \_ backend ->
321322
publishHydraScriptsAs backend Faucet
322323
>>= singlePartyCommitsScriptBlueprint tracer tmpDir backend
324+
it "can deposit partial UTxO" $ \tracer -> do
325+
withClusterTempDir $ \tmpDir -> do
326+
withBackend (contramap FromCardanoNode tracer) tmpDir $ \blockTime backend ->
327+
publishHydraScriptsAs backend Faucet
328+
>>= canDepositPartially tracer tmpDir blockTime backend
323329
it "persistence can load with empty commit" $ \tracer -> do
324330
withClusterTempDir $ \tmpDir -> do
325331
withBackend (contramap FromCardanoNode tracer) tmpDir $ \_ backend ->

0 commit comments

Comments
 (0)