From bcf82c4a015c6ea7b3da59ad76779b6bc2cd53d0 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Thu, 20 Jul 2023 16:36:01 +0200 Subject: [PATCH 01/14] parseReferenceScript part 1 --- .../Cardano/Wallet/Api/Http/Shelley/Server.hs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs index 4adb35ba9dd..2c4d82c1cf2 100644 --- a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs +++ b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs @@ -144,6 +144,7 @@ import Cardano.Address.Script ( Cosigner (..) , KeyHash (KeyHash) , KeyRole (..) + , Script , ScriptTemplate (..) , ValidationLevel (..) , foldScript @@ -2462,6 +2463,9 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d mintBurnData <- liftHandler $ except $ parseMintBurnData body validityInterval + mintBurnReferenceScript <- + liftHandler $ except $ parseMintBurnReferenceScript body validityInterval + delegationRequest <- liftHandler $ traverse parseDelegationRequest $ body ^. #delegations @@ -2622,6 +2626,30 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d walletId = getApiT apiWalletId + parseMintBurnReferenceScript + :: ApiConstructTransactionData n + -> (SlotNo, SlotNo) + -> Either ErrConstructTx (Maybe (ApiT (Script Cosigner))) + parseMintBurnReferenceScript tx validity = do + let mbRefScript = tx ^. #referencePolicyScriptTemplate + for mbRefScript $ \refScript -> do + guardWrongScriptTemplate refScript + Right refScript + where + guardWrongScriptTemplate + :: ApiT (Script Cosigner) -> Either ErrConstructTx () + guardWrongScriptTemplate apiScript = + when (wrongMintingTemplate apiScript) + $ Left ErrConstructTxWrongMintingBurningTemplate + where + wrongMintingTemplate (ApiT script) = + isLeft (validateScriptOfTemplate RecommendedValidation script) + || countCosigners script /= (1 :: Int) + || existsNonZeroCosigner script + countCosigners = foldScript (const (+ 1)) 0 + existsNonZeroCosigner = + foldScript (\cosigner a -> a || cosigner /= Cosigner 0) False + parseMintBurnData :: ApiConstructTransactionData n -> (SlotNo, SlotNo) From a7db812cabd49225e53dad8e54a99c9ffedc5e70 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Thu, 20 Jul 2023 16:51:33 +0200 Subject: [PATCH 02/14] parseReferenceScript part 2 --- .../Cardano/Wallet/Api/Http/Shelley/Server.hs | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs index 2c4d82c1cf2..458a00eb829 100644 --- a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs +++ b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs @@ -2464,7 +2464,7 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d liftHandler $ except $ parseMintBurnData body validityInterval mintBurnReferenceScript <- - liftHandler $ except $ parseMintBurnReferenceScript body validityInterval + liftHandler $ except $ parseReferenceScript body validityInterval delegationRequest <- liftHandler $ traverse parseDelegationRequest $ body ^. #delegations @@ -2626,23 +2626,24 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d walletId = getApiT apiWalletId - parseMintBurnReferenceScript + parseReferenceScript :: ApiConstructTransactionData n -> (SlotNo, SlotNo) - -> Either ErrConstructTx (Maybe (ApiT (Script Cosigner))) - parseMintBurnReferenceScript tx validity = do + -> Either ErrConstructTx (Maybe (Script Cosigner)) + parseReferenceScript tx validity = do let mbRefScript = tx ^. #referencePolicyScriptTemplate - for mbRefScript $ \refScript -> do + for mbRefScript $ \(ApiT refScript) -> do guardWrongScriptTemplate refScript + guardOutsideValidityInterval validity refScript Right refScript where guardWrongScriptTemplate - :: ApiT (Script Cosigner) -> Either ErrConstructTx () + :: Script Cosigner -> Either ErrConstructTx () guardWrongScriptTemplate apiScript = when (wrongMintingTemplate apiScript) $ Left ErrConstructTxWrongMintingBurningTemplate where - wrongMintingTemplate (ApiT script) = + wrongMintingTemplate script = isLeft (validateScriptOfTemplate RecommendedValidation script) || countCosigners script /= (1 :: Int) || existsNonZeroCosigner script @@ -2650,6 +2651,18 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d existsNonZeroCosigner = foldScript (\cosigner a -> a || cosigner /= Cosigner 0) False + guardOutsideValidityInterval + :: (SlotNo, SlotNo) + -> Script Cosigner + -> Either ErrConstructTx () + guardOutsideValidityInterval (before, hereafter) script = + when notWithinValidityInterval $ + Left ErrConstructTxValidityIntervalNotWithinScriptTimelock + where + notWithinValidityInterval = + not $ withinSlotInterval before hereafter $ + scriptSlotIntervals script + parseMintBurnData :: ApiConstructTransactionData n -> (SlotNo, SlotNo) From c8bedc776926176727381df753f06ec3bce6acad Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Thu, 20 Jul 2023 17:24:14 +0200 Subject: [PATCH 03/14] compute referenceScript from template --- .../http/Cardano/Wallet/Api/Http/Shelley/Server.hs | 12 ++++++++++-- .../src/Cardano/Wallet/Address/Keys/MintBurn.hs | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs index 458a00eb829..c27f7ab90ff 100644 --- a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs +++ b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs @@ -244,7 +244,7 @@ import Cardano.Wallet.Address.Discovery.Shared import Cardano.Wallet.Address.HasDelegation ( HasDelegation (..) ) import Cardano.Wallet.Address.Keys.MintBurn - ( toTokenMapAndScript, toTokenPolicyId ) + ( replaceCosigner, toTokenMapAndScript, toTokenPolicyId ) import Cardano.Wallet.Address.Keys.SequentialAny ( mkSeqStateFromRootXPrv ) import Cardano.Wallet.Address.Keys.Shared @@ -2463,7 +2463,7 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d mintBurnData <- liftHandler $ except $ parseMintBurnData body validityInterval - mintBurnReferenceScript <- + mintBurnReferenceScriptTemplate <- liftHandler $ except $ parseReferenceScript body validityInterval delegationRequest <- @@ -2563,6 +2563,14 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d else pure (transactionCtx1, Nothing) + let referenceScript = case policyXPubM of + Just policyXPub -> + replaceCosigner + ShelleyKeyS + (Map.singleton (Cosigner 0) policyXPub) + <$> mintBurnReferenceScriptTemplate + Nothing -> Nothing + outs <- case body ^. #payments of Nothing -> pure [] Just (ApiPaymentAddresses content) -> diff --git a/lib/wallet/src/Cardano/Wallet/Address/Keys/MintBurn.hs b/lib/wallet/src/Cardano/Wallet/Address/Keys/MintBurn.hs index b5ed65f017e..9bae8e75a65 100644 --- a/lib/wallet/src/Cardano/Wallet/Address/Keys/MintBurn.hs +++ b/lib/wallet/src/Cardano/Wallet/Address/Keys/MintBurn.hs @@ -7,6 +7,7 @@ module Cardano.Wallet.Address.Keys.MintBurn ( derivePolicyKeyAndHash , toTokenMapAndScript , toTokenPolicyId + , replaceCosigner ) where import Prelude From c1e788a628654ee9e7b2af7f528ad8ece7e0fba8 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Wed, 9 Aug 2023 16:51:21 +0200 Subject: [PATCH 04/14] extend TransactionCtx --- .../Cardano/Wallet/Api/Http/Shelley/Server.hs | 17 +++++++++-------- lib/wallet/src/Cardano/Wallet/Transaction.hs | 6 ++++++ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs index c27f7ab90ff..e37037db31f 100644 --- a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs +++ b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs @@ -2469,9 +2469,6 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d delegationRequest <- liftHandler $ traverse parseDelegationRequest $ body ^. #delegations - when (isJust $ body ^. #referencePolicyScriptTemplate) $ - liftHandler $ throwE ErrConstructTxNotImplemented - let metadata = body ^? #metadata . traverse . #txMetadataWithSchema_metadata @@ -2563,7 +2560,7 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d else pure (transactionCtx1, Nothing) - let referenceScript = case policyXPubM of + let referenceScriptM = case policyXPubM of Just policyXPub -> replaceCosigner ShelleyKeyS @@ -2571,6 +2568,10 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d <$> mintBurnReferenceScriptTemplate Nothing -> Nothing + let transactionCtx3 = transactionCtx2 + { txReferenceScript = referenceScriptM + } + outs <- case body ^. #payments of Nothing -> pure [] Just (ApiPaymentAddresses content) -> @@ -2590,7 +2591,7 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d unbalancedTx <- liftHandler $ W.constructTransaction @n @'CredFromKeyK @era - txLayer db transactionCtx2 + txLayer db transactionCtx3 PreSelection { outputs = outs <> mintingOuts } balancedTx <- @@ -2610,11 +2611,11 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d (_, _, rewardPath) <- handler $ W.readRewardAccount @s db - let deposits = case txDelegationAction transactionCtx2 of + let deposits = case txDelegationAction transactionCtx3 of Just (JoinRegisteringKey _poolId) -> [W.getStakeKeyDeposit pp] _ -> [] - let refunds = case txDelegationAction transactionCtx2 of + let refunds = case txDelegationAction transactionCtx3 of Just Quit -> [W.getStakeKeyDeposit pp] _ -> [] @@ -2623,7 +2624,7 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d , coinSelection = mkApiCoinSelection deposits refunds - ((,rewardPath) <$> transactionCtx2 ^. #txDelegationAction) + ((,rewardPath) <$> transactionCtx3 ^. #txDelegationAction) metadata (unsignedTx rewardPath (outs ++ mintingOuts) apiDecoded) , fee = apiDecoded ^. #fee diff --git a/lib/wallet/src/Cardano/Wallet/Transaction.hs b/lib/wallet/src/Cardano/Wallet/Transaction.hs index 72ffb95bae5..c6f2123c96b 100644 --- a/lib/wallet/src/Cardano/Wallet/Transaction.hs +++ b/lib/wallet/src/Cardano/Wallet/Transaction.hs @@ -231,6 +231,10 @@ data TransactionCtx = TransactionCtx -- ^ Script template regulating delegation credentials , txNativeScriptInputs :: Map TxIn (Script KeyHash) -- ^ A map of script hashes related to inputs. Only for multisig wallets + , txCollateralRequirement :: SelectionCollateralRequirement + -- ^ The collateral requirement. + , txReferenceScript :: Maybe (Script KeyHash) + -- ^ The reference script. } deriving Generic -- | Represents a preliminary selection of tx outputs typically made by user. @@ -308,6 +312,8 @@ defaultTransactionCtx = TransactionCtx , txPaymentCredentialScriptTemplate = Nothing , txStakingCredentialScriptTemplate = Nothing , txNativeScriptInputs = Map.empty + , txCollateralRequirement = SelectionCollateralNotRequired + , txReferenceScript = Nothing } -- | User-requested action related to a delegation From 13831cc3784e4081c0ff3240806fab8d8fe6a742 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Thu, 10 Aug 2023 16:46:32 +0200 Subject: [PATCH 05/14] sneak reference script to shelley layer --- .../src/Cardano/Wallet/Shelley/Transaction.hs | 21 ++++++++++++------- .../Cardano/Wallet/Shelley/TransactionSpec.hs | 4 ++-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs b/lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs index 9faa2694dff..2a692e9cb66 100644 --- a/lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs +++ b/lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs @@ -250,15 +250,17 @@ constructUnsignedTx -- ^ scripts for inputs -> Maybe (Script KeyHash) -- ^ Delegation script + -> Maybe (Script KeyHash) + -- ^ Reference script -> ShelleyBasedEra era -> Either ErrMkTransaction (Cardano.TxBody era) constructUnsignedTx networkId (md, certs) ttl wdrl - cs fee toMint toBurn inpScripts stakingScriptM era = + cs fee toMint toBurn inpScripts stakingScriptM refScriptM era = mkUnsignedTx era ttl cs md wdrls certs (toCardanoLovelace fee) (fst toMint) (fst toBurn) mintingScripts inpScripts - stakingScriptM + stakingScriptM refScriptM where wdrls = mkWithdrawals networkId wdrl mintingScripts = Map.union (snd toMint) (snd toBurn) @@ -291,7 +293,7 @@ mkTx keyF networkId payload ttl (rewardAcnt, pwdAcnt) addrResolver wdrl cs fees unsigned <- mkUnsignedTx era ttl (Right cs) md wdrls certs (toCardanoLovelace fees) - TokenMap.empty TokenMap.empty Map.empty Map.empty Nothing + TokenMap.empty TokenMap.empty Map.empty Map.empty Nothing Nothing let signed = signTransaction keyF networkId AnyWitnessCountCtx acctResolver (const Nothing) (const Nothing) addrResolver inputResolver (unsigned, mkExtraWits unsigned) @@ -546,6 +548,7 @@ newTransactionLayer keyF networkId = TransactionLayer let assetsToBeMinted = view #txAssetsToMint ctx let assetsToBeBurned = view #txAssetsToBurn ctx let inpsScripts = view #txNativeScriptInputs ctx + let refScriptM = view #txReferenceScript ctx let stakingScriptM = flip (replaceCosignersWithVerKeys CA.Stake) minBound <$> view #txStakingCredentialScriptTemplate ctx @@ -558,17 +561,17 @@ newTransactionLayer keyF networkId = TransactionLayer if ourRewardAcctM == Just rewardAcct then constructUnsignedTx networkId (md, []) ttl wdrl selection delta assetsToBeMinted assetsToBeBurned inpsScripts - stakingScriptM + stakingScriptM refScriptM (Write.shelleyBasedEraFromRecentEra Write.recentEra) else constructUnsignedTx networkId (md, []) ttl wdrl selection delta assetsToBeMinted assetsToBeBurned inpsScripts - Nothing + Nothing refScriptM (Write.shelleyBasedEraFromRecentEra Write.recentEra) _ -> constructUnsignedTx networkId (md, []) ttl wdrl selection delta assetsToBeMinted assetsToBeBurned inpsScripts - Nothing + Nothing refScriptM (Write.shelleyBasedEraFromRecentEra Write.recentEra) Just action -> do let certs = case stakeCred of @@ -582,7 +585,7 @@ newTransactionLayer keyF networkId = TransactionLayer let payload = (view #txMetadata ctx, certs) constructUnsignedTx networkId payload ttl wdrl selection delta assetsToBeMinted assetsToBeBurned inpsScripts - stakingScriptM + stakingScriptM refScriptM (Write.shelleyBasedEraFromRecentEra Write.recentEra) , decodeTx = _decodeSealedTx @@ -659,6 +662,7 @@ mkUnsignedTx -> Map AssetId (Script KeyHash) -> Map TxIn (Script KeyHash) -> Maybe (Script KeyHash) + -> Maybe (Script KeyHash) -> Either ErrMkTransaction (Cardano.TxBody era) mkUnsignedTx era @@ -672,7 +676,8 @@ mkUnsignedTx burnData mintingScripts inpsScripts - stakingScriptM = extractValidatedOutputs cs >>= \outs -> + stakingScriptM + refScriptM = extractValidatedOutputs cs >>= \outs -> left toErrMkTx $ fmap removeDummyInput $ Cardano.createAndValidateTransactionBody Cardano.TxBodyContent { Cardano.txIns = inputWits diff --git a/lib/wallet/test/unit/Cardano/Wallet/Shelley/TransactionSpec.hs b/lib/wallet/test/unit/Cardano/Wallet/Shelley/TransactionSpec.hs index 9c85ec5e54d..b7eaea22d04 100644 --- a/lib/wallet/test/unit/Cardano/Wallet/Shelley/TransactionSpec.hs +++ b/lib/wallet/test/unit/Cardano/Wallet/Shelley/TransactionSpec.hs @@ -1395,7 +1395,7 @@ binaryCalculationsSpec' era = describe ("calculateBinary - "+||era||+"") $ do unsigned = either (error . show) id $ mkUnsignedTx (shelleyBasedEraFromRecentEra era) (Nothing, slotNo) (Right cs) md mempty [] fee - TokenMap.empty TokenMap.empty Map.empty Map.empty Nothing + TokenMap.empty TokenMap.empty Map.empty Map.empty Nothing Nothing cs = Selection { inputs = NE.fromList inps , collateral = [] @@ -1450,7 +1450,7 @@ makeShelleyTx era testCase = Cardano.makeSignedTransaction addrWits unsigned fee = toCardanoLovelace $ selectionDelta cs unsigned = either (error . show) id $ mkUnsignedTx era (Nothing, slotNo) (Right cs) md mempty [] fee - TokenMap.empty TokenMap.empty Map.empty Map.empty Nothing + TokenMap.empty TokenMap.empty Map.empty Map.empty Nothing Nothing addrWits = map (mkShelleyWitness unsigned) pairs cs = Selection { inputs = NE.fromList inps From 8b0eefa20b2d55112358ca8a94d4054c4f8fe089 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Thu, 10 Aug 2023 17:43:00 +0200 Subject: [PATCH 06/14] sneak to toCardanoTxOut --- .../src/Cardano/Wallet/Shelley/Compatibility.hs | 12 ++++++++---- lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs | 11 ++++++++++- .../unit/Cardano/Wallet/Shelley/TransactionSpec.hs | 1 + 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/wallet/src/Cardano/Wallet/Shelley/Compatibility.hs b/lib/wallet/src/Cardano/Wallet/Shelley/Compatibility.hs index 0616be770ca..5c09ff64bb0 100644 --- a/lib/wallet/src/Cardano/Wallet/Shelley/Compatibility.hs +++ b/lib/wallet/src/Cardano/Wallet/Shelley/Compatibility.hs @@ -1297,13 +1297,17 @@ toCardanoLovelace (W.Coin c) = Cardano.Lovelace $ intCast c toCardanoUTxO :: ShelleyBasedEra era -> W.UTxO -> Cardano.UTxO era toCardanoUTxO era = Cardano.UTxO . Map.fromList - . map (bimap toCardanoTxIn (toCardanoTxOut era)) + . map (bimap toCardanoTxIn (toCardanoTxOut era Nothing)) . Map.toList . W.unUTxO -toCardanoTxOut :: HasCallStack - => ShelleyBasedEra era -> W.TxOut -> Cardano.TxOut ctx era -toCardanoTxOut era = case era of +toCardanoTxOut + :: HasCallStack + => ShelleyBasedEra era + -> Maybe (Script KeyHash) + -> W.TxOut + -> Cardano.TxOut ctx era +toCardanoTxOut era refScriptM = case era of ShelleyBasedEraShelley -> toShelleyTxOut ShelleyBasedEraAllegra -> toAllegraTxOut ShelleyBasedEraMary -> toMaryTxOut diff --git a/lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs b/lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs index 2a692e9cb66..f387869c6c6 100644 --- a/lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs +++ b/lib/wallet/src/Cardano/Wallet/Shelley/Transaction.hs @@ -684,7 +684,16 @@ mkUnsignedTx , txInsReference = Cardano.TxInsReferenceNone - , Cardano.txOuts = map (toCardanoTxOut era) outs + , Cardano.txOuts = case refScriptM of + Nothing -> + map (toCardanoTxOut era Nothing) outs + Just _ -> case outs of + firstOut:rest -> + let cardanoFirstTxOut = toCardanoTxOut era refScriptM firstOut + in cardanoFirstTxOut:(map (toCardanoTxOut era Nothing) rest) + _ -> + [] + , Cardano.txWithdrawals = case stakingScriptM of Nothing -> let ctx = Cardano.BuildTxWith diff --git a/lib/wallet/test/unit/Cardano/Wallet/Shelley/TransactionSpec.hs b/lib/wallet/test/unit/Cardano/Wallet/Shelley/TransactionSpec.hs index b7eaea22d04..27d085fbfa3 100644 --- a/lib/wallet/test/unit/Cardano/Wallet/Shelley/TransactionSpec.hs +++ b/lib/wallet/test/unit/Cardano/Wallet/Shelley/TransactionSpec.hs @@ -3808,6 +3808,7 @@ estimateSignedTxSizeSpec = describe "estimateSignedTxSize" $ do [ (i , Compatibility.toCardanoTxOut (shelleyBasedEra @era) + Nothing (TxOut addr mempty) ) | i <- allTxIns body From 598bd192c241df324dea15ba42768d26fd175f8e Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Fri, 11 Aug 2023 16:02:40 +0200 Subject: [PATCH 07/14] conclude toCardanoTxOut --- .../Cardano/Wallet/Shelley/Compatibility.hs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/wallet/src/Cardano/Wallet/Shelley/Compatibility.hs b/lib/wallet/src/Cardano/Wallet/Shelley/Compatibility.hs index 5c09ff64bb0..b568f6cdd8b 100644 --- a/lib/wallet/src/Cardano/Wallet/Shelley/Compatibility.hs +++ b/lib/wallet/src/Cardano/Wallet/Shelley/Compatibility.hs @@ -1406,7 +1406,14 @@ toCardanoTxOut era refScriptM = case era of datumHash refScript where - refScript = Cardano.ReferenceScriptNone + refScript = case refScriptM of + Nothing -> + Cardano.ReferenceScriptNone + Just script -> + let aux = Cardano.ReferenceTxInsScriptsInlineDatumsInBabbageEra + scriptApi = Cardano.toScriptInAnyLang $ Cardano.SimpleScript $ + toCardanoSimpleScript script + in Cardano.ReferenceScript aux scriptApi datumHash = Cardano.TxOutDatumNone addrInEra = tina "toCardanoTxOut: malformed address" [ Cardano.AddressInEra @@ -1427,7 +1434,14 @@ toCardanoTxOut era refScriptM = case era of datumHash refScript where - refScript = Cardano.ReferenceScriptNone + refScript = case refScriptM of + Nothing -> + Cardano.ReferenceScriptNone + Just script -> + let aux = Cardano.ReferenceTxInsScriptsInlineDatumsInConwayEra + scriptApi = Cardano.toScriptInAnyLang $ Cardano.SimpleScript $ + toCardanoSimpleScript script + in Cardano.ReferenceScript aux scriptApi datumHash = Cardano.TxOutDatumNone addrInEra = tina "toCardanoTxOut: malformed address" [ Cardano.AddressInEra From ae0ca59d3b4c68c80c3bbbc6bb39dc4e783b7e10 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Tue, 22 Aug 2023 14:04:24 +0200 Subject: [PATCH 08/14] integration test - part 1 --- .../Scenario/API/Shelley/TransactionsNew.hs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs index 94e82cb1632..837ae9a2bcb 100644 --- a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs +++ b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs @@ -1148,6 +1148,49 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do , expectErrorMessage errMsg403NotEnoughMoney ] + it "TRANS_NEW_ASSETS_CREATE_02 - using reference script" $ \ctx -> runResourceT $ do + + let initialAmt = 3 * minUTxOValue (_mainEra ctx) + wa <- fixtureWalletWith @n ctx [initialAmt] + wb <- emptyWallet ctx + let amt = (minUTxOValue (_mainEra ctx) :: Natural) + + let policyWithHash = Link.getPolicyKey @'Shelley wa (Just True) + (_, policyKeyHashPayload) <- + unsafeRequest @ApiPolicyKey ctx policyWithHash Empty + let (Just policyKeyHash) = + keyHashFromBytes (Policy, getApiPolicyKey policyKeyHashPayload) + let _scriptUsed = RequireAllOf + [ RequireSignatureOf policyKeyHash + ] + + addrs <- listAddresses @n ctx wb + let destination = (addrs !! 1) ^. #id + let payload = Json [json|{ + "reference_policy_script_template": + { "all": [ "cosigner#0" ] }, + "payments": [{ + "address": #{destination}, + "amount": { + "quantity": #{amt}, + "unit": "lovelace" + } + }] + }|] + + let expectedCreateTx = + [ expectSuccess + , expectResponseCode HTTP.status202 + , expectField (#coinSelection . #inputs) (`shouldSatisfy` (not . null)) + , expectField (#coinSelection . #outputs) (`shouldSatisfy` (not . null)) + , expectField (#coinSelection . #change) (`shouldSatisfy` (not . null)) + , expectField (#fee . #getQuantity) (`shouldSatisfy` (> 0)) + ] + + rTx <- request @(ApiConstructTransaction n) ctx + (Link.createUnsignedTransaction @'Shelley wa) Default payload + verify rTx expectedCreateTx + it "TRANS_NEW_VALIDITY_INTERVAL_01a - \ \Validity interval with second" $ \ctx -> runResourceT $ do From 7b498aa402559dc036514932c55769720bd0475c Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Wed, 23 Aug 2023 12:08:36 +0200 Subject: [PATCH 09/14] integration test - part 2 --- .../Cardano/Wallet/Api/Http/Shelley/Server.hs | 29 +++++++-------- .../Scenario/API/Shared/Transactions.hs | 31 ++++------------ .../Scenario/API/Shelley/TransactionsNew.hs | 35 +++++++++++++++++-- lib/wallet/src/Cardano/Wallet/Transaction.hs | 25 ++++++++++++- 4 files changed, 76 insertions(+), 44 deletions(-) diff --git a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs index e37037db31f..0e7f894896b 100644 --- a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs +++ b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs @@ -720,7 +720,6 @@ import qualified Network.Wai.Handler.Warp as Warp import qualified Network.Wai.Handler.WarpTLS as Warp - -- | Allow configuring which port the wallet server listen to in an integration -- setup. Crashes if the variable is not a number. walletListenFromEnv :: Show e @@ -2508,10 +2507,11 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d Just action -> transactionCtx0 { txDelegationAction = Just action } - (transactionCtx2, policyXPubM) <- + (policyXPub, _) <- + liftHandler $ W.readPolicyPublicKey wrk + + transactionCtx2 <- if isJust mintBurnData then do - (policyXPub, _) <- - liftHandler $ W.readPolicyPublicKey wrk let isMinting (ApiMintBurnDataFromScript _ _ (ApiMint _)) = True isMinting _ = False let getMinting = \case @@ -2552,21 +2552,18 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d map getBurning $ filter (not . isMinting) $ NE.toList $ fromJust mintBurnData - pure ( transactionCtx1 + pure transactionCtx1 { txAssetsToMint = mintingData , txAssetsToBurn = burningData } - , Just policyXPub) else - pure (transactionCtx1, Nothing) - - let referenceScriptM = case policyXPubM of - Just policyXPub -> - replaceCosigner - ShelleyKeyS - (Map.singleton (Cosigner 0) policyXPub) - <$> mintBurnReferenceScriptTemplate - Nothing -> Nothing + pure transactionCtx1 + + let referenceScriptM = + replaceCosigner + ShelleyKeyS + (Map.singleton (Cosigner 0) policyXPub) + <$> mintBurnReferenceScriptTemplate let transactionCtx3 = transactionCtx2 { txReferenceScript = referenceScriptM @@ -2584,7 +2581,7 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d let mintingOuts = case mintBurnData of Just mintBurns -> coalesceTokensPerAddr $ - map (toMintTxOut (fromJust policyXPubM)) $ + map (toMintTxOut policyXPub) $ filter mintWithAddress $ NE.toList mintBurns Nothing -> [] diff --git a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shared/Transactions.hs b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shared/Transactions.hs index c737b3c31c9..139750da768 100644 --- a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shared/Transactions.hs +++ b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shared/Transactions.hs @@ -21,8 +21,6 @@ module Test.Integration.Scenario.API.Shared.Transactions import Prelude -import Cardano.Address.Script - ( KeyHash (..), Script (..) ) import Cardano.Mnemonic ( MkSomeMnemonic (..) ) import Cardano.Wallet.Address.Derivation @@ -78,7 +76,11 @@ import Cardano.Wallet.Primitive.Types.Tx import Cardano.Wallet.Primitive.Types.Tx.TxMeta ( Direction (..), TxStatus (..) ) import Cardano.Wallet.Transaction - ( AnyExplicitScript (..), ScriptReference (..), WitnessCount (..) ) + ( AnyExplicitScript (..) + , ScriptReference (..) + , WitnessCount (..) + , changeRoleInAnyExplicitScript + ) import Control.Monad ( forM_ ) import Control.Monad.IO.Unlift @@ -926,7 +928,7 @@ spec = describe "SHARED_TRANSACTIONS" $ do -- it only is aware of its policy verification key let noVerKeyWitnessHex = mkApiWitnessCount WitnessCount { verificationKey = 0 - , scripts = [changeRole CA.Unknown paymentScript] + , scripts = [changeRoleInAnyExplicitScript CA.Unknown paymentScript] , bootstrap = 0 } let witsExp1hex = @@ -964,7 +966,7 @@ spec = describe "SHARED_TRANSACTIONS" $ do -- it only is aware of its policy verification key let oneVerKeyWitnessHex = mkApiWitnessCount WitnessCount { verificationKey = 1 - , scripts = [changeRole CA.Unknown paymentScript] + , scripts = [changeRoleInAnyExplicitScript CA.Unknown paymentScript] , bootstrap = 0 } let witsExp2hex = @@ -3301,22 +3303,3 @@ spec = describe "SHARED_TRANSACTIONS" $ do (#balance . #available . #getQuantity) (`shouldBe` amt) ] - - changeRole :: CA.KeyRole -> AnyExplicitScript -> AnyExplicitScript - changeRole role = \case - NativeExplicitScript script scriptRole -> - let changeRole' = \case - RequireSignatureOf (KeyHash _ p) -> - RequireSignatureOf $ KeyHash role p - RequireAllOf xs -> - RequireAllOf (map changeRole' xs) - RequireAnyOf xs -> - RequireAnyOf (map changeRole' xs) - RequireSomeOf m xs -> - RequireSomeOf m (map changeRole' xs) - ActiveFromSlot s -> - ActiveFromSlot s - ActiveUntilSlot s -> - ActiveUntilSlot s - in NativeExplicitScript (changeRole' script) scriptRole - PlutusExplicitScript _ _ -> error "wrong usage" diff --git a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs index 837ae9a2bcb..00a25080d99 100644 --- a/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs +++ b/lib/wallet/integration/src/Test/Integration/Scenario/API/Shelley/TransactionsNew.hs @@ -125,6 +125,7 @@ import Cardano.Wallet.Transaction , ScriptReference (..) , ValidityIntervalExplicit (..) , WitnessCount (..) + , changeRoleInAnyExplicitScript ) import Cardano.Wallet.Unsafe ( unsafeFromHex, unsafeMkMnemonic ) @@ -228,6 +229,7 @@ import Test.Integration.Framework.TestData import UnliftIO.Exception ( fromEither ) +import qualified Cardano.Address.Script as CA import qualified Cardano.Api as Cardano import qualified Cardano.Ledger.Keys as Ledger import qualified Cardano.Wallet.Address.Derivation.Shelley as Shelley @@ -1150,17 +1152,17 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do it "TRANS_NEW_ASSETS_CREATE_02 - using reference script" $ \ctx -> runResourceT $ do - let initialAmt = 3 * minUTxOValue (_mainEra ctx) + let initialAmt = 1_000_000_000 wa <- fixtureWalletWith @n ctx [initialAmt] wb <- emptyWallet ctx - let amt = (minUTxOValue (_mainEra ctx) :: Natural) + let amt = 10_000_000 :: Natural let policyWithHash = Link.getPolicyKey @'Shelley wa (Just True) (_, policyKeyHashPayload) <- unsafeRequest @ApiPolicyKey ctx policyWithHash Empty let (Just policyKeyHash) = keyHashFromBytes (Policy, getApiPolicyKey policyKeyHashPayload) - let _scriptUsed = RequireAllOf + let scriptUsed = RequireAllOf [ RequireSignatureOf policyKeyHash ] @@ -1191,6 +1193,33 @@ spec = describe "NEW_SHELLEY_TRANSACTIONS" $ do (Link.createUnsignedTransaction @'Shelley wa) Default payload verify rTx expectedCreateTx + let (ApiSerialisedTransaction apiTx _) = getFromResponse #transaction rTx + + signedTx <- signTx ctx wa apiTx [ expectResponseCode HTTP.status202 ] + + submittedTx <- submitTxWithWid ctx wa signedTx + verify submittedTx + [ expectSuccess + , expectResponseCode HTTP.status202 + ] + + let (ApiT txId) = getFromResponse #id submittedTx + let refInp = ReferenceInput $ TxIn txId 0 + let referenceScript = NativeExplicitScript scriptUsed (ViaReferenceInput refInp) + let witnessCountWithNativeScript = mkApiWitnessCount WitnessCount + { verificationKey = 1 + , scripts = [changeRoleInAnyExplicitScript CA.Unknown referenceScript] + , bootstrap = 0 + } + + let decodePayload = Json (toJSON signedTx) + rTx1 <- request @(ApiDecodedTransaction n) ctx + (Link.decodeTransaction @'Shelley wa) Default decodePayload + verify rTx1 + [ expectResponseCode HTTP.status202 + , expectField (#witnessCount) (`shouldBe` witnessCountWithNativeScript) + ] + it "TRANS_NEW_VALIDITY_INTERVAL_01a - \ \Validity interval with second" $ \ctx -> runResourceT $ do diff --git a/lib/wallet/src/Cardano/Wallet/Transaction.hs b/lib/wallet/src/Cardano/Wallet/Transaction.hs index c6f2123c96b..f610e52ee93 100644 --- a/lib/wallet/src/Cardano/Wallet/Transaction.hs +++ b/lib/wallet/src/Cardano/Wallet/Transaction.hs @@ -36,6 +36,7 @@ module Cardano.Wallet.Transaction , TokenMapWithScripts (..) , emptyTokenMapWithScripts , AnyExplicitScript (..) + , changeRoleInAnyExplicitScript , AnyScript (..) , PlutusScriptInfo (..) , PlutusVersion (..) @@ -60,7 +61,7 @@ import Prelude import Cardano.Address.Derivation ( XPrv, XPub ) import Cardano.Address.Script - ( KeyHash (..), KeyRole (..), Script, ScriptHash, ScriptTemplate ) + ( KeyHash (..), KeyRole (..), Script (..), ScriptHash, ScriptTemplate ) import Cardano.Api ( AnyCardanoEra ) import Cardano.Api.Extra @@ -394,6 +395,28 @@ data AnyExplicitScript = deriving (Eq, Generic, Show) deriving anyclass NFData +changeRoleInAnyExplicitScript + :: KeyRole + -> AnyExplicitScript + -> AnyExplicitScript +changeRoleInAnyExplicitScript newrole = \case + NativeExplicitScript script scriptRole -> + let changeRole' = \case + RequireSignatureOf (KeyHash _ p) -> + RequireSignatureOf $ KeyHash newrole p + RequireAllOf xs -> + RequireAllOf (map changeRole' xs) + RequireAnyOf xs -> + RequireAnyOf (map changeRole' xs) + RequireSomeOf m xs -> + RequireSomeOf m (map changeRole' xs) + ActiveFromSlot s -> + ActiveFromSlot s + ActiveUntilSlot s -> + ActiveUntilSlot s + in NativeExplicitScript (changeRole' script) scriptRole + PlutusExplicitScript _ _ -> error "wrong usage" + data WitnessCount = WitnessCount { verificationKey :: Word8 , scripts :: [AnyExplicitScript] From 7d4caecb7c788a6b325e664e6298e082a8d7afc3 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Thu, 7 Sep 2023 11:38:24 +0200 Subject: [PATCH 10/14] post rebase fix --- lib/wallet/src/Cardano/Wallet/Transaction.hs | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/wallet/src/Cardano/Wallet/Transaction.hs b/lib/wallet/src/Cardano/Wallet/Transaction.hs index f610e52ee93..c3401b2bf84 100644 --- a/lib/wallet/src/Cardano/Wallet/Transaction.hs +++ b/lib/wallet/src/Cardano/Wallet/Transaction.hs @@ -232,8 +232,6 @@ data TransactionCtx = TransactionCtx -- ^ Script template regulating delegation credentials , txNativeScriptInputs :: Map TxIn (Script KeyHash) -- ^ A map of script hashes related to inputs. Only for multisig wallets - , txCollateralRequirement :: SelectionCollateralRequirement - -- ^ The collateral requirement. , txReferenceScript :: Maybe (Script KeyHash) -- ^ The reference script. } deriving Generic @@ -313,7 +311,6 @@ defaultTransactionCtx = TransactionCtx , txPaymentCredentialScriptTemplate = Nothing , txStakingCredentialScriptTemplate = Nothing , txNativeScriptInputs = Map.empty - , txCollateralRequirement = SelectionCollateralNotRequired , txReferenceScript = Nothing } From bf1b84a69a42cb39b075da5ad25902757ebcd848 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Fri, 8 Sep 2023 14:57:05 +0200 Subject: [PATCH 11/14] add script template remark in spec --- specifications/api/mint-burn.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specifications/api/mint-burn.md b/specifications/api/mint-burn.md index d2b4b8fd742..df9d077b131 100644 --- a/specifications/api/mint-burn.md +++ b/specifications/api/mint-burn.md @@ -16,7 +16,7 @@ Specifically: 1. Creation of a transaction output that contains a minting script and is suitable for use as reference input. - In the `reference_policy_script_template` field, you can optionally add a script template. The HTTP endpoint will map this script template into a script using the wallet's policy public key, and this script will be included in the first transaction output (i.e. at index `0`) of the transaction. + In the `reference_policy_script_template` field, you can optionally add a script template. The HTTP endpoint will map this script template into a script using the wallet's policy public key, and this script will be included in the first transaction output (i.e. at index `0`) of the transaction. It is worth mentioning that for shelley wallets script template needs to include one cosigner only. Example `POST` data for the endpoint: From 88073da1264ea121ec4b8225f2697315a26cd5b1 Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Tue, 12 Sep 2023 10:01:23 +0200 Subject: [PATCH 12/14] get rid of validity check during publication of the script --- .../Cardano/Wallet/Api/Http/Shelley/Server.hs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs index 0e7f894896b..311633adcfd 100644 --- a/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs +++ b/lib/wallet/api/http/Cardano/Wallet/Api/Http/Shelley/Server.hs @@ -2463,7 +2463,7 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d liftHandler $ except $ parseMintBurnData body validityInterval mintBurnReferenceScriptTemplate <- - liftHandler $ except $ parseReferenceScript body validityInterval + liftHandler $ except $ parseReferenceScript body delegationRequest <- liftHandler $ traverse parseDelegationRequest $ body ^. #delegations @@ -2634,13 +2634,11 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d parseReferenceScript :: ApiConstructTransactionData n - -> (SlotNo, SlotNo) -> Either ErrConstructTx (Maybe (Script Cosigner)) - parseReferenceScript tx validity = do + parseReferenceScript tx = do let mbRefScript = tx ^. #referencePolicyScriptTemplate for mbRefScript $ \(ApiT refScript) -> do guardWrongScriptTemplate refScript - guardOutsideValidityInterval validity refScript Right refScript where guardWrongScriptTemplate @@ -2657,18 +2655,6 @@ constructTransaction api argGenChange knownPools poolStatus apiWalletId body = d existsNonZeroCosigner = foldScript (\cosigner a -> a || cosigner /= Cosigner 0) False - guardOutsideValidityInterval - :: (SlotNo, SlotNo) - -> Script Cosigner - -> Either ErrConstructTx () - guardOutsideValidityInterval (before, hereafter) script = - when notWithinValidityInterval $ - Left ErrConstructTxValidityIntervalNotWithinScriptTimelock - where - notWithinValidityInterval = - not $ withinSlotInterval before hereafter $ - scriptSlotIntervals script - parseMintBurnData :: ApiConstructTransactionData n -> (SlotNo, SlotNo) From f3cc31f0347b484172ebd06194b74a44e240f2fa Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Tue, 12 Sep 2023 16:19:17 +0200 Subject: [PATCH 13/14] more docs polish --- specifications/api/mint-burn.md | 2 +- specifications/api/swagger.yaml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/specifications/api/mint-burn.md b/specifications/api/mint-burn.md index df9d077b131..1b2437aa1a2 100644 --- a/specifications/api/mint-burn.md +++ b/specifications/api/mint-burn.md @@ -16,7 +16,7 @@ Specifically: 1. Creation of a transaction output that contains a minting script and is suitable for use as reference input. - In the `reference_policy_script_template` field, you can optionally add a script template. The HTTP endpoint will map this script template into a script using the wallet's policy public key, and this script will be included in the first transaction output (i.e. at index `0`) of the transaction. It is worth mentioning that for shelley wallets script template needs to include one cosigner only. + In the `reference_policy_script_template` field, you can optionally add a script template. The HTTP endpoint will map this script template into a script using the wallet's policy public key, and this script will be included in the first transaction output (i.e. at index `0`) of the transaction. For Shelley-style wallets, the script template must contain a single cosigner only, but it may include time locks. Example `POST` data for the endpoint: diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index 127aaa3460f..61f06febbcb 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -2657,7 +2657,6 @@ components: coin_selection: *ApiCoinSelection fee: *amount - ApiInputsGeneral: &ApiInputsGeneral <<: *transactionInputsGeneral description: | @@ -3658,8 +3657,10 @@ components: required: - operation - reference_input + - policy_id properties: reference_input: *referenceInput + policy_id: *assetPolicyId asset_name: *assetName operation: *ApiMintBurnOperation @@ -3691,6 +3692,7 @@ components: In future transactions, the reference script can be used by any wallet multiple times by referencing the current transaction `id` and `index = 0`. + The script template must contain a single cosigner only, but it may include time locks. encoding: type: string enum: ["base16", "base64"] From baa6679c1220dd3e5ec0316e9b83ba760df65b2e Mon Sep 17 00:00:00 2001 From: Pawel Jakubas Date: Tue, 12 Sep 2023 17:31:49 +0200 Subject: [PATCH 14/14] some fix --- specifications/api/swagger.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/specifications/api/swagger.yaml b/specifications/api/swagger.yaml index 61f06febbcb..b7e125046f0 100644 --- a/specifications/api/swagger.yaml +++ b/specifications/api/swagger.yaml @@ -3657,10 +3657,8 @@ components: required: - operation - reference_input - - policy_id properties: reference_input: *referenceInput - policy_id: *assetPolicyId asset_name: *assetName operation: *ApiMintBurnOperation