diff --git a/hydra-plutus/src/Hydra/Contract/Head.hs b/hydra-plutus/src/Hydra/Contract/Head.hs index d5def8d6a12..8947b1b40e6 100644 --- a/hydra-plutus/src/Hydra/Contract/Head.hs +++ b/hydra-plutus/src/Hydra/Contract/Head.hs @@ -415,19 +415,32 @@ checkClose ctx openBefore redeemer = version == 0 && snapshotNumber' == 0 && utxoHash' == initialUtxoHash - CloseUnused{signature} -> + CloseUnusedDec{signature} -> traceIfFalse $(errorCode FailedCloseCurrent) $ verifySnapshotSignature parties (headId, version, snapshotNumber', utxoHash', emptyHash, deltaUTxOHash') signature - CloseUsed{signature, alreadyDecommittedUTxOHash} -> + CloseUsedDec{signature, alreadyDecommittedUTxOHash} -> traceIfFalse $(errorCode FailedCloseOutdated) $ deltaUTxOHash' == emptyHash && verifySnapshotSignature parties (headId, version - 1, snapshotNumber', utxoHash', emptyHash, alreadyDecommittedUTxOHash) signature + CloseUnusedInc{signature, alreadyCommittedUTxOHash} -> + traceIfFalse $(errorCode FailedCloseCurrent) $ + verifySnapshotSignature + parties + (headId, version, snapshotNumber', utxoHash', alreadyCommittedUTxOHash, emptyHash) + signature + CloseUsedInc{signature} -> + traceIfFalse $(errorCode FailedCloseOutdated) $ + deltaUTxOHash' == emptyHash + && verifySnapshotSignature + parties + (headId, version - 1, snapshotNumber', utxoHash', deltaUTxOHash', emptyHash) + signature checkDeadline = traceIfFalse $(errorCode IncorrectClosedContestationDeadline) $ diff --git a/hydra-plutus/src/Hydra/Contract/HeadState.hs b/hydra-plutus/src/Hydra/Contract/HeadState.hs index 5a66890f423..898cc2b80cd 100644 --- a/hydra-plutus/src/Hydra/Contract/HeadState.hs +++ b/hydra-plutus/src/Hydra/Contract/HeadState.hs @@ -82,17 +82,29 @@ data CloseRedeemer = -- | Intial snapshot is used to close. CloseInitial | -- | Closing snapshot refers to the current state version - CloseUnused + CloseUnusedDec { signature :: [Signature] -- ^ Multi-signature of a snapshot ξ } | -- | Closing snapshot refers to the previous state version - CloseUsed + CloseUsedDec { signature :: [Signature] -- ^ Multi-signature of a snapshot ξ , alreadyDecommittedUTxOHash :: Hash -- ^ UTxO which was already decommitted ηω } + | -- | Closing snapshot refers to the current state version + CloseUnusedInc + { signature :: [Signature] + -- ^ Multi-signature of a snapshot ξ + , alreadyCommittedUTxOHash :: Hash + -- ^ UTxO which was already committed ηα + } + | -- | Closing snapshot refers to the previous state version + CloseUsedInc + { signature :: [Signature] + -- ^ Multi-signature of a snapshot ξ + } deriving stock (Show, Generic) PlutusTx.unstableMakeIsData ''CloseRedeemer diff --git a/hydra-tx/src/Hydra/Tx/Close.hs b/hydra-tx/src/Hydra/Tx/Close.hs index 5c0a3674dff..1c76b1f0034 100644 --- a/hydra-tx/src/Hydra/Tx/Close.hs +++ b/hydra-tx/src/Hydra/Tx/Close.hs @@ -100,15 +100,29 @@ closeTx scriptRegistry vk headId openVersion confirmedSnapshot startSlotNo (endS closeRedeemer = case confirmedSnapshot of InitialSnapshot{} -> Head.CloseInitial - ConfirmedSnapshot{signatures, snapshot = Snapshot{version, utxoToDecommit}} - | version == openVersion -> - Head.CloseUnused{signature = toPlutusSignatures signatures} + ConfirmedSnapshot{signatures, snapshot = Snapshot{version, utxoToCommit, utxoToDecommit}} + | version == openVersion + , isJust utxoToCommit -> + Head.CloseUnusedInc{signature = toPlutusSignatures signatures, alreadyCommittedUTxOHash = toBuiltin . hashUTxO $ fromMaybe mempty utxoToCommit} + | version == openVersion + , isJust utxoToDecommit -> + Head.CloseUnusedDec{signature = toPlutusSignatures signatures} | otherwise -> -- NOTE: This will only work for version == openVersion - 1 - Head.CloseUsed - { signature = toPlutusSignatures signatures - , alreadyDecommittedUTxOHash = toBuiltin . hashUTxO $ fromMaybe mempty utxoToDecommit - } + if isJust utxoToCommit + then + Head.CloseUsedInc + { signature = toPlutusSignatures signatures + } + else + if isJust utxoToDecommit + then + Head.CloseUsedDec + { signature = toPlutusSignatures signatures + , alreadyDecommittedUTxOHash = toBuiltin . hashUTxO $ fromMaybe mempty utxoToDecommit + } + else + error "closeTx: unexpected snapshot" headOutputAfter = modifyTxOutDatum (const headDatumAfter) headOutputBefore @@ -123,8 +137,10 @@ closeTx scriptRegistry vk headId openVersion confirmedSnapshot startSlotNo (endS toBuiltin . hashUTxO . utxo $ getSnapshot confirmedSnapshot , deltaUTxOHash = case closeRedeemer of - Head.CloseUnused{} -> + Head.CloseUnusedDec{} -> toBuiltin . hashUTxO @Tx . fromMaybe mempty . utxoToDecommit $ getSnapshot confirmedSnapshot + Head.CloseUsedInc{} -> + toBuiltin . hashUTxO @Tx . fromMaybe mempty . utxoToCommit $ getSnapshot confirmedSnapshot _ -> toBuiltin $ hashUTxO @Tx mempty , parties = openParties , contestationDeadline diff --git a/hydra-tx/test/Hydra/Tx/Contract/Close/CloseUnused.hs b/hydra-tx/test/Hydra/Tx/Contract/Close/CloseUnused.hs index 8632ef04205..1d0028a41e7 100644 --- a/hydra-tx/test/Hydra/Tx/Contract/Close/CloseUnused.hs +++ b/hydra-tx/test/Hydra/Tx/Contract/Close/CloseUnused.hs @@ -212,7 +212,7 @@ genCloseCurrentMutation (tx, _utxo) = pure $ ChangeOutput 0 (modifyTxOutAddress (const mutatedAddress) headTxOut) , SomeMutation (pure $ toErrorCode SignatureVerificationFailed) MutateSignatureButNotSnapshotNumber . ChangeHeadRedeemer <$> do signature <- toPlutusSignatures <$> (arbitrary :: Gen (MultiSignature (Snapshot Tx))) - pure $ Head.Close Head.CloseUnused{signature} + pure $ Head.Close Head.CloseUnusedDec{signature} , SomeMutation (pure $ toErrorCode SignatureVerificationFailed) MutateSnapshotNumberButNotSignature <$> do mutatedSnapshotNumber <- arbitrarySizedNatural `suchThat` (> healthyCurrentSnapshotNumber) pure $ ChangeOutput 0 $ modifyInlineDatum (replaceSnapshotNumber $ toInteger mutatedSnapshotNumber) headTxOut @@ -277,7 +277,7 @@ genCloseCurrentMutation (tx, _utxo) = ( Just $ toScriptData ( Head.Close - Head.CloseUnused + Head.CloseUnusedDec { signature = toPlutusSignatures $ healthySignature healthyCurrentSnapshot } ) diff --git a/hydra-tx/test/Hydra/Tx/Contract/Close/CloseUsed.hs b/hydra-tx/test/Hydra/Tx/Contract/Close/CloseUsed.hs index 57ab66bbbe7..ec23c9996e8 100644 --- a/hydra-tx/test/Hydra/Tx/Contract/Close/CloseUsed.hs +++ b/hydra-tx/test/Hydra/Tx/Contract/Close/CloseUsed.hs @@ -238,7 +238,7 @@ genCloseOutdatedMutation (tx, _utxo) = pure $ ChangeOutput 0 (modifyTxOutAddress (const mutatedAddress) headTxOut) , SomeMutation (pure $ toErrorCode SignatureVerificationFailed) MutateSignatureButNotSnapshotNumber . ChangeHeadRedeemer <$> do signature <- toPlutusSignatures <$> (arbitrary :: Gen (MultiSignature (Snapshot Tx))) - pure $ Head.Close Head.CloseUnused{signature} + pure $ Head.Close Head.CloseUnusedDec{signature} , SomeMutation (pure $ toErrorCode SignatureVerificationFailed) MutateSnapshotNumberButNotSignature <$> do mutatedSnapshotNumber <- arbitrarySizedNatural `suchThat` (> healthyOutdatedSnapshotNumber) pure $ ChangeOutput 0 $ modifyInlineDatum (replaceSnapshotNumber $ toInteger mutatedSnapshotNumber) headTxOut @@ -303,7 +303,7 @@ genCloseOutdatedMutation (tx, _utxo) = ( Just $ toScriptData ( Head.Close - Head.CloseUnused + Head.CloseUnusedDec { signature = toPlutusSignatures $ healthySignature healthyOutdatedSnapshot @@ -334,7 +334,7 @@ genCloseOutdatedMutation (tx, _utxo) = mutatedUTxOHash <- genHash `suchThat` (/= healthyUTxOToDecommitHash) pure $ Head.Close - Head.CloseUsed + Head.CloseUsedDec { signature = toPlutusSignatures $ signatures healthyOutdatedConfirmedClosingSnapshot , alreadyDecommittedUTxOHash = toBuiltin mutatedUTxOHash } @@ -349,7 +349,7 @@ genCloseOutdatedMutation (tx, _utxo) = signature <- toPlutusSignatures <$> (arbitrary `suchThat` (/= signatures healthyOutdatedConfirmedClosingSnapshot)) pure $ Head.Close - Head.CloseUsed + Head.CloseUsedDec { signature , alreadyDecommittedUTxOHash = toBuiltin healthyUTxOToDecommitHash } @@ -357,7 +357,7 @@ genCloseOutdatedMutation (tx, _utxo) = -- Close redeemer claims whether the snapshot is valid against current -- or previous version. If we change it then it should cause invalid -- signature error. - pure $ Head.Close Head.CloseUnused{signature = toPlutusSignatures $ signatures healthyOutdatedConfirmedClosingSnapshot} + pure $ Head.Close Head.CloseUnusedDec{signature = toPlutusSignatures $ signatures healthyOutdatedConfirmedClosingSnapshot} ] where genOversizedTransactionValidity = do diff --git a/hydra-tx/test/Hydra/Tx/Contract/ContractSpec.hs b/hydra-tx/test/Hydra/Tx/Contract/ContractSpec.hs index b1036676627..2c09b4871c1 100644 --- a/hydra-tx/test/Hydra/Tx/Contract/ContractSpec.hs +++ b/hydra-tx/test/Hydra/Tx/Contract/ContractSpec.hs @@ -132,12 +132,12 @@ spec = parallel $ do propTransactionEvaluates healthyCloseInitialTx prop "does not survive random adversarial mutations" $ propMutation healthyCloseInitialTx genCloseInitialMutation - describe "CloseUnused" $ do + describe "CloseUnusedDec" $ do prop "is healthy" $ propTransactionEvaluates healthyCloseCurrentTx prop "does not survive random adversarial mutations" $ propMutation healthyCloseCurrentTx genCloseCurrentMutation - describe "CloseUsed" $ do + describe "CloseUsedDec" $ do prop "is healthy" $ propTransactionEvaluates healthyCloseOutdatedTx prop "does not survive random adversarial mutations" $