From c0b8f157364c2f57e7794efac2802918da6834e9 Mon Sep 17 00:00:00 2001 From: Franco Testagrossa Date: Mon, 12 Dec 2022 14:57:12 +0100 Subject: [PATCH 1/5] Use initial script reference when commit tx --- hydra-node/exe/tx-cost/TxCost.hs | 2 +- hydra-node/src/Hydra/Chain/Direct/State.hs | 13 +++++++------ hydra-node/src/Hydra/Chain/Direct/Tx.hs | 19 +++++++++++++------ .../Hydra/Chain/Direct/Contract/Commit.hs | 9 ++++++++- 4 files changed, 29 insertions(+), 14 deletions(-) diff --git a/hydra-node/exe/tx-cost/TxCost.hs b/hydra-node/exe/tx-cost/TxCost.hs index 02873669a2e..4757346ec23 100644 --- a/hydra-node/exe/tx-cost/TxCost.hs +++ b/hydra-node/exe/tx-cost/TxCost.hs @@ -102,7 +102,7 @@ computeCommitCost = do -- NOTE: number of parties is irrelevant for commit tx ctx <- genHydraContextFor 1 (cctx, stInitial) <- genStInitial ctx - pure (commit cctx stInitial utxo, getKnownUTxO stInitial) + pure (commit cctx stInitial utxo, getKnownUTxO stInitial <> getKnownUTxO cctx) computeCollectComCost :: IO [(NumParties, TxSize, MemUnit, CpuUnit, Lovelace)] computeCollectComCost = diff --git a/hydra-node/src/Hydra/Chain/Direct/State.hs b/hydra-node/src/Hydra/Chain/Direct/State.hs index 42639be1df6..8ac02f0ab30 100644 --- a/hydra-node/src/Hydra/Chain/Direct/State.hs +++ b/hydra-node/src/Hydra/Chain/Direct/State.hs @@ -25,6 +25,7 @@ import Hydra.Cardano.Api ( NetworkMagic (NetworkMagic), PaymentKey, PlutusScript, + ScriptData, SerialiseAsRawBytes (serialiseToRawBytes), SlotNo (SlotNo), Tx, @@ -292,28 +293,28 @@ commit ctx st utxo = do case UTxO.pairs utxo of [aUTxO] -> do rejectByronAddress aUTxO - Right $ commitTx networkId ownParty (Just aUTxO) initial + Right $ commitTx scriptRegistry networkId ownParty (Just aUTxO) initial [] -> do - Right $ commitTx networkId ownParty Nothing initial + Right $ commitTx scriptRegistry networkId ownParty Nothing initial _ -> Left (MoreThanOneUTxOCommitted @Tx) where - ChainContext{networkId, ownParty, ownVerificationKey} = ctx + ChainContext{networkId, ownParty, ownVerificationKey, scriptRegistry} = ctx InitialState { initialInitials , initialHeadTokenScript } = st - ownInitial :: Maybe (TxIn, TxOut CtxUTxO, Hash PaymentKey) + ownInitial :: Maybe (TxIn, TxOut CtxUTxO, Hash PaymentKey, ScriptData) ownInitial = foldl' go Nothing initialInitials where go (Just x) _ = Just x - go Nothing (i, out, _) = do + go Nothing (i, out, sd) = do let vkh = verificationKeyHash ownVerificationKey guard $ hasMatchingPT vkh (txOutValue out) - pure (i, out, vkh) + pure (i, out, vkh, sd) hasMatchingPT :: Hash PaymentKey -> Value -> Bool hasMatchingPT vkh val = diff --git a/hydra-node/src/Hydra/Chain/Direct/Tx.hs b/hydra-node/src/Hydra/Chain/Direct/Tx.hs index 6b79093156b..2c7478533ba 100644 --- a/hydra-node/src/Hydra/Chain/Direct/Tx.hs +++ b/hydra-node/src/Hydra/Chain/Direct/Tx.hs @@ -165,6 +165,8 @@ mkInitialOutput networkId tokenPolicyId (verificationKeyHash -> pkh) = -- | Craft a commit transaction which includes the "committed" utxo as a datum. commitTx :: + -- | Published Hydra scripts to reference. + ScriptRegistry -> NetworkId -> Party -> -- | A single UTxO to commit to the Head @@ -172,22 +174,27 @@ commitTx :: Maybe (TxIn, TxOut CtxUTxO) -> -- | The initial output (sent to each party) which should contain the PT and is -- locked by initial script - (TxIn, TxOut CtxUTxO, Hash PaymentKey) -> + (TxIn, TxOut CtxUTxO, Hash PaymentKey, ScriptData) -> Tx -commitTx networkId party utxo (initialInput, out, vkh) = +commitTx scriptRegistry networkId party utxo (initialInput, out, vkh, sd) = unsafeBuildTransaction $ emptyTxBody - & addInputs [(initialInput, initialWitness_)] + & addInputs [(initialInput, initialWitness)] + & addReferenceInputs [initialScriptRef] & addVkInputs (maybeToList mCommittedInput) & addExtraRequiredSigners [vkh] & addOutputs [commitOutput] where - initialWitness_ = - BuildTxWith $ ScriptWitness scriptWitnessCtx $ mkScriptWitness initialScript initialDatum initialRedeemer + initialWitness = + BuildTxWith $ + ScriptWitness scriptWitnessCtx $ + mkScriptReference initialScriptRef initialScript initialDatum initialRedeemer initialScript = fromPlutusScript @PlutusScriptV2 Initial.validatorScript + initialScriptRef = + fst (initialReference scriptRegistry) initialDatum = - mkScriptDatum $ Initial.datum () + ScriptDatumForTxIn sd initialRedeemer = toScriptData . Initial.redeemer $ Initial.ViaCommit (toPlutusTxOutRef <$> mCommittedInput) diff --git a/hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs b/hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs index e7d7868a866..61e3669e608 100644 --- a/hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs +++ b/hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs @@ -15,6 +15,7 @@ import Hydra.Chain.Direct.Contract.Mutation ( SomeMutation (..), ) import qualified Hydra.Chain.Direct.Fixture as Fixture +import Hydra.Chain.Direct.ScriptRegistry (genScriptRegistry, registryUTxO) import Hydra.Chain.Direct.Tx (commitTx, headPolicyId, mkInitialOutput) import Hydra.Ledger.Cardano ( genAddressInEra, @@ -36,12 +37,18 @@ healthyCommitTx = lookupUTxO = UTxO.singleton (initialInput, toUTxOContext initialOutput) <> UTxO.singleton healthyCommittedUTxO + <> registryUTxO scriptRegistry tx = commitTx + scriptRegistry Fixture.testNetworkId commitParty (Just healthyCommittedUTxO) - (initialInput, toUTxOContext initialOutput, initialPubKeyHash) + (initialInput, toUTxOContext initialOutput, initialPubKeyHash, scriptData) + + scriptData = fromJust . getScriptData $ initialOutput + + scriptRegistry = genScriptRegistry `generateWith` 42 initialInput = generateWith arbitrary 42 From 8354358204018917995903bc2ea87eb2326a60d6 Mon Sep 17 00:00:00 2001 From: Franco Testagrossa Date: Mon, 12 Dec 2022 23:04:25 +0100 Subject: [PATCH 2/5] Revert the need for `ScriptData` to build the `initialDatum` as it is not needed --- hydra-node/src/Hydra/Chain/Direct/State.hs | 7 +++---- hydra-node/src/Hydra/Chain/Direct/Tx.hs | 6 +++--- hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs | 4 +--- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/hydra-node/src/Hydra/Chain/Direct/State.hs b/hydra-node/src/Hydra/Chain/Direct/State.hs index 8ac02f0ab30..629da291c5c 100644 --- a/hydra-node/src/Hydra/Chain/Direct/State.hs +++ b/hydra-node/src/Hydra/Chain/Direct/State.hs @@ -25,7 +25,6 @@ import Hydra.Cardano.Api ( NetworkMagic (NetworkMagic), PaymentKey, PlutusScript, - ScriptData, SerialiseAsRawBytes (serialiseToRawBytes), SlotNo (SlotNo), Tx, @@ -306,15 +305,15 @@ commit ctx st utxo = do , initialHeadTokenScript } = st - ownInitial :: Maybe (TxIn, TxOut CtxUTxO, Hash PaymentKey, ScriptData) + ownInitial :: Maybe (TxIn, TxOut CtxUTxO, Hash PaymentKey) ownInitial = foldl' go Nothing initialInitials where go (Just x) _ = Just x - go Nothing (i, out, sd) = do + go Nothing (i, out, _) = do let vkh = verificationKeyHash ownVerificationKey guard $ hasMatchingPT vkh (txOutValue out) - pure (i, out, vkh, sd) + pure (i, out, vkh) hasMatchingPT :: Hash PaymentKey -> Value -> Bool hasMatchingPT vkh val = diff --git a/hydra-node/src/Hydra/Chain/Direct/Tx.hs b/hydra-node/src/Hydra/Chain/Direct/Tx.hs index 2c7478533ba..a21d8589405 100644 --- a/hydra-node/src/Hydra/Chain/Direct/Tx.hs +++ b/hydra-node/src/Hydra/Chain/Direct/Tx.hs @@ -174,9 +174,9 @@ commitTx :: Maybe (TxIn, TxOut CtxUTxO) -> -- | The initial output (sent to each party) which should contain the PT and is -- locked by initial script - (TxIn, TxOut CtxUTxO, Hash PaymentKey, ScriptData) -> + (TxIn, TxOut CtxUTxO, Hash PaymentKey) -> Tx -commitTx scriptRegistry networkId party utxo (initialInput, out, vkh, sd) = +commitTx scriptRegistry networkId party utxo (initialInput, out, vkh) = unsafeBuildTransaction $ emptyTxBody & addInputs [(initialInput, initialWitness)] @@ -194,7 +194,7 @@ commitTx scriptRegistry networkId party utxo (initialInput, out, vkh, sd) = initialScriptRef = fst (initialReference scriptRegistry) initialDatum = - ScriptDatumForTxIn sd + mkScriptDatum $ Initial.datum () initialRedeemer = toScriptData . Initial.redeemer $ Initial.ViaCommit (toPlutusTxOutRef <$> mCommittedInput) diff --git a/hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs b/hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs index 61e3669e608..a31d24c1d56 100644 --- a/hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs +++ b/hydra-node/test/Hydra/Chain/Direct/Contract/Commit.hs @@ -44,9 +44,7 @@ healthyCommitTx = Fixture.testNetworkId commitParty (Just healthyCommittedUTxO) - (initialInput, toUTxOContext initialOutput, initialPubKeyHash, scriptData) - - scriptData = fromJust . getScriptData $ initialOutput + (initialInput, toUTxOContext initialOutput, initialPubKeyHash) scriptRegistry = genScriptRegistry `generateWith` 42 From 43645097de49896b49ff017419a816274bf6d574 Mon Sep 17 00:00:00 2001 From: Sebastian Nagel Date: Wed, 14 Dec 2022 10:40:43 +0100 Subject: [PATCH 3/5] Fix script integrity hash to always use PlutusV2 (HACK) --- hydra-node/src/Hydra/Chain/Direct/Wallet.hs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hydra-node/src/Hydra/Chain/Direct/Wallet.hs b/hydra-node/src/Hydra/Chain/Direct/Wallet.hs index 5605cee3fe3..83c36e4011f 100644 --- a/hydra-node/src/Hydra/Chain/Direct/Wallet.hs +++ b/hydra-node/src/Hydra/Chain/Direct/Wallet.hs @@ -10,6 +10,7 @@ import Hydra.Prelude import Cardano.Crypto.Hash.Class import qualified Cardano.Ledger.Address as Ledger import Cardano.Ledger.Alonzo.Data (Data (Data)) +import Cardano.Ledger.Alonzo.Language (Language (PlutusV2)) import Cardano.Ledger.Alonzo.PlutusScriptApi (language) import Cardano.Ledger.Alonzo.Scripts (CostModels (CostModels), ExUnits (ExUnits), Tag (Spend), txscriptfee) import Cardano.Ledger.Alonzo.Tools (TransactionScriptFailure, evaluateTransactionExecutionUnits) @@ -258,12 +259,8 @@ coverFee_ pparams systemStart epochInfo lookupUTxO walletUTxO partialTx@Validate needlesslyHighFee let newOutputs = outputs body <> StrictSeq.singleton (mkSized change) - langs = - [ getLanguageView pparams l - | (_hash, script) <- Map.toList (txscripts wits) - , (not . isNativeScript @LedgerEra) script - , Just l <- [language script] - ] + -- FIXME: NONONO.. use cardano-api instead of doing hard-coding this here + langs = [getLanguageView pparams (PlutusV2)] finalBody = body { inputs = inputs' From f33d4c6ada35c226afb2d103fb0e75ff41092340 Mon Sep 17 00:00:00 2001 From: Franco Testagrossa Date: Wed, 14 Dec 2022 11:27:47 +0100 Subject: [PATCH 4/5] Fix langs used to calculate the scriptIntegrityHash of the tx by including the list of referenceScripts --- hydra-node/src/Hydra/Chain/Direct/Wallet.hs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/hydra-node/src/Hydra/Chain/Direct/Wallet.hs b/hydra-node/src/Hydra/Chain/Direct/Wallet.hs index 83c36e4011f..efe622edb5f 100644 --- a/hydra-node/src/Hydra/Chain/Direct/Wallet.hs +++ b/hydra-node/src/Hydra/Chain/Direct/Wallet.hs @@ -10,14 +10,14 @@ import Hydra.Prelude import Cardano.Crypto.Hash.Class import qualified Cardano.Ledger.Address as Ledger import Cardano.Ledger.Alonzo.Data (Data (Data)) -import Cardano.Ledger.Alonzo.Language (Language (PlutusV2)) import Cardano.Ledger.Alonzo.PlutusScriptApi (language) import Cardano.Ledger.Alonzo.Scripts (CostModels (CostModels), ExUnits (ExUnits), Tag (Spend), txscriptfee) import Cardano.Ledger.Alonzo.Tools (TransactionScriptFailure, evaluateTransactionExecutionUnits) import Cardano.Ledger.Alonzo.TxInfo (TranslationError) import Cardano.Ledger.Alonzo.TxWitness (RdmrPtr (RdmrPtr), Redeemers (..), TxWitness (txrdmrs), txdats, txscripts) import Cardano.Ledger.Babbage.PParams (PParams, PParams' (..)) -import Cardano.Ledger.Babbage.Tx (ValidatedTx (..), getLanguageView, hashData, hashScriptIntegrity) +import Cardano.Ledger.Babbage.Scripts (refScripts) +import Cardano.Ledger.Babbage.Tx (ValidatedTx (..), getLanguageView, hashData, hashScriptIntegrity, referenceInputs) import Cardano.Ledger.Babbage.TxBody (Datum (..), collateral, inputs, outputs, outputs', scriptIntegrityHash, txfee) import qualified Cardano.Ledger.Babbage.TxBody as Ledger.Babbage import qualified Cardano.Ledger.BaseTypes as Ledger @@ -142,7 +142,7 @@ newTinyWallet tracer networkId (vk, sk) queryWalletInfo queryEpochInfo = do pure TinyWallet { getUTxO - , getSeedInput = (fmap (fromLedgerTxIn . fst) . findFuelUTxO) <$> getUTxO + , getSeedInput = fmap (fromLedgerTxIn . fst) . findFuelUTxO <$> getUTxO , sign = Util.signWith sk , coverFee = \lookupUTxO partialTx -> do -- XXX: We should query pparams here. If not, we likely will have @@ -240,8 +240,9 @@ coverFee_ pparams systemStart epochInfo lookupUTxO walletUTxO partialTx@Validate let inputs' = inputs body <> Set.singleton input resolvedInputs <- traverse resolveInput (toList inputs') + let utxo = lookupUTxO <> walletUTxO estimatedScriptCosts <- - estimateScriptsCost pparams systemStart epochInfo (lookupUTxO <> walletUTxO) partialTx + estimateScriptsCost pparams systemStart epochInfo utxo partialTx let adjustedRedeemers = adjustRedeemers (inputs body) @@ -259,8 +260,13 @@ coverFee_ pparams systemStart epochInfo lookupUTxO walletUTxO partialTx@Validate needlesslyHighFee let newOutputs = outputs body <> StrictSeq.singleton (mkSized change) - -- FIXME: NONONO.. use cardano-api instead of doing hard-coding this here - langs = [getLanguageView pparams (PlutusV2)] + referenceScripts = refScripts @LedgerEra (referenceInputs body) (Ledger.UTxO utxo) + langs = + [ getLanguageView pparams l + | (_hash, script) <- Map.toList $ Map.union (txscripts wits) referenceScripts + , (not . isNativeScript @LedgerEra) script + , l <- maybeToList (language script) + ] finalBody = body { inputs = inputs' From 43003c991bacc9af5ddff9ccca2412bad39c4902 Mon Sep 17 00:00:00 2001 From: Franco Testagrossa Date: Wed, 14 Dec 2022 11:30:27 +0100 Subject: [PATCH 5/5] update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5e76364fc7d..e8d94f624b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -26,6 +26,8 @@ changes. + Changed logs of `BeginInitialize` and `EndInitialize`. + Added `SkipUpdate` constructor to the wallet logs. +- Reduce cost of `commitTx` by using the initial script as input reference. + ## [0.8.1] - 2022-11-17 - **BREAKING** Implemented [ADR18](https://hydra.family/head-protocol/adr/18) to keep only a single state: