Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI deployment of Wst using Blockfrost #44

Merged
merged 24 commits into from
Jan 15, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a166444
Add ScriptTarget flag
j-mueller Jan 2, 2025
7450aff
Add build target in one place
j-mueller Jan 2, 2025
b2418d6
WIP - script dependencies
j-mueller Jan 2, 2025
4f4997a
Fix build error
j-mueller Jan 2, 2025
1035988
Use scripts from env everywhere
j-mueller Jan 2, 2025
e83ec79
Parameterise tests by script target
j-mueller Jan 2, 2025
f280bf8
Delete node params (not required anymore)
j-mueller Jan 2, 2025
0a6da4f
Add filter for NFT to globalParamsNode
j-mueller Jan 6, 2025
3949143
10x ex units and memory for testing
j-mueller Jan 6, 2025
110b9ff
Rename workflow
j-mueller Jan 6, 2025
0f32d70
github action: Fix concurrency group
j-mueller Jan 6, 2025
21a8743
Deploy all scripts and reg in single tx test case
amirmrad Jan 6, 2025
234168c
Nonce permissioned scripts
amirmrad Jan 6, 2025
32d12f9
CLI deployment and include registrations in deploy tx
amirmrad Jan 9, 2025
88b78c6
Merge branch 'main' of github.com:input-output-hk/wsc-poc into amir/d…
amirmrad Jan 9, 2025
242ce17
Add vkey-witness and submit endpoints
amirmrad Jan 10, 2025
12db0da
Merge branch 'main' of github.com:input-output-hk/wsc-poc into amir/d…
amirmrad Jan 10, 2025
69559e3
Include CA root certificates (#62)
j-mueller Jan 13, 2025
6f53e3c
Fix build
j-mueller Jan 13, 2025
8453053
Review comments
amirmrad Jan 14, 2025
796332c
Fixes and better errors
amirmrad Jan 14, 2025
89818f5
Export scripts json
amirmrad Jan 14, 2025
4c40704
Export applied scripts json
amirmrad Jan 14, 2025
0f2550e
Fix nix ci script
amirmrad Jan 15, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/lib/Wst/AppError.hs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ data AppError era =
| GlobalParamsNodeNotFound -- ^ The node with the global parameters was not found
| BalancingError (CoinSelection.BalanceTxError era)
| BlockfrostErr BlockfrostError
| NoTokensToSeize -- ^ No tokens to seize
| DuplicateBlacklistNode -- ^ Attempting to add a duplicate blacklist node
| TransferBlacklistedCredential Credential -- ^ Attempting to transfer funds from a blacklisted address
| SubmitError (ValidationError era)
deriving stock (Show)
22 changes: 19 additions & 3 deletions src/lib/Wst/Offchain/BuildTx/TransferLogic.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ module Wst.Offchain.BuildTx.TransferLogic
seizeSmartTokens,
initBlacklist,
insertBlacklistNode,
spendBlacklistOutput,
paySmartTokensToDestination,
registerTransferScripts,
)
Expand All @@ -17,6 +18,7 @@ where
import Cardano.Api qualified as C
import Cardano.Api.Shelley qualified as C
import Control.Lens (over, (^.))
import Control.Monad (when)
import Control.Monad.Except (MonadError (throwError))
import Control.Monad.Reader (MonadReader, asks)
import Convex.BuildTx (MonadBuildTx (addTxBuilder), TxBuilder (TxBuilder),
Expand Down Expand Up @@ -44,7 +46,7 @@ import SmartTokens.Contracts.ExampleTransferLogic (BlacklistProof (..))
import SmartTokens.Types.ProtocolParams
import SmartTokens.Types.PTokenDirectory (BlacklistNode (..),
DirectorySetNode (..))
import Wst.AppError (AppError (TransferBlacklistedCredential))
import Wst.AppError (AppError (DuplicateBlacklistNode, TransferBlacklistedCredential))
import Wst.Offchain.BuildTx.ProgrammableLogic (issueProgrammableToken,
seizeProgrammableToken,
transferProgrammableToken)
Expand Down Expand Up @@ -91,7 +93,7 @@ initBlacklist = Utils.inBabbage @era $ do
opPkh <- asks (fst . Env.bteOperator . Env.operatorEnv)
addRequiredSignature opPkh

insertBlacklistNode :: forall era env m. (MonadReader env m, Env.HasOperatorEnv era env, Env.HasTransferLogicEnv env, C.IsBabbageBasedEra era, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => C.PaymentCredential -> [UTxODat era BlacklistNode]-> m ()
insertBlacklistNode :: forall era env m. (MonadReader env m, Env.HasOperatorEnv era env, Env.HasTransferLogicEnv env, C.IsBabbageBasedEra era, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m, MonadError (AppError era) m) => C.PaymentCredential -> [UTxODat era BlacklistNode]-> m ()
insertBlacklistNode cred blacklistNodes = Utils.inBabbage @era $ do
-- mint new blacklist token
mintingScript <- asks (Env.tleBlacklistMintingScript . Env.transferLogicEnv)
Expand All @@ -102,7 +104,6 @@ insertBlacklistNode cred blacklistNodes = Utils.inBabbage @era $ do
mintPlutus mintingScript () newAssetName quantity

let

-- find the node to insert on
UTxODat {uIn = prevNodeRef,uOut = (C.TxOut prevAddr prevVal _ _), uDatum = prevNode} =
maximumBy (compare `on` (blnKey . uDatum)) $
Expand All @@ -119,6 +120,9 @@ insertBlacklistNode cred blacklistNodes = Utils.inBabbage @era $ do
newPrevNodeDatum = C.TxOutDatumInline C.babbageBasedEra $ C.toHashableScriptData newPrevNode
newPrevNodeOutput = C.TxOut prevAddr prevVal newPrevNodeDatum C.ReferenceScriptNone

when (blnKey prevNode == blnKey newNode)
$ throwError DuplicateBlacklistNode

-- spend previous node
spendingScript <- asks (Env.tleBlacklistSpendingScript . Env.transferLogicEnv)
spendPlutusInlineDatum prevNodeRef spendingScript ()
Expand All @@ -131,6 +135,13 @@ insertBlacklistNode cred blacklistNodes = Utils.inBabbage @era $ do
opPkh <- asks (fst . Env.bteOperator . Env.operatorEnv)
addRequiredSignature opPkh

spendBlacklistOutput :: forall era env m. (MonadReader env m, Env.HasOperatorEnv era env, Env.HasTransferLogicEnv env, C.IsBabbageBasedEra era, C.HasScriptLanguageInEra C.PlutusScriptV3 era, MonadBuildTx era m) => C.TxIn -> m ()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently unused in the endpoints. I only needed it to remove a duplicate blacklist node I accidentally pushed on preview. We could later cleanup the interface and add an endpoint if need be.

spendBlacklistOutput txin = Utils.inBabbage @era $ do
spendingScript <- asks (Env.tleBlacklistSpendingScript . Env.transferLogicEnv)
spendPlutusInlineDatum txin spendingScript ()
opPkh <- asks (fst . Env.bteOperator . Env.operatorEnv)
addRequiredSignature opPkh

{-| Add a smart token output that locks the given value,
addressed to the payment credential
-}
Expand Down Expand Up @@ -330,6 +341,7 @@ registerTransferScripts = case C.babbageBasedEra @era of
C.BabbageEraOnwardsConway -> Utils.inConway @era $ do
transferMintingScript <- asks (Env.tleMintingScript . Env.transferLogicEnv)
transferSpendingScript <- asks (Env.tleTransferScript . Env.transferLogicEnv)
transferSeizeSpendingScript <- asks (Env.tleIssuerScript . Env.transferLogicEnv)

let
hshMinting = C.hashScript $ C.PlutusScript C.plutusScriptVersion transferMintingScript
Expand All @@ -338,5 +350,9 @@ registerTransferScripts = case C.babbageBasedEra @era of
hshSpending = C.hashScript $ C.PlutusScript C.plutusScriptVersion transferSpendingScript
credSpending = C.StakeCredentialByScript hshSpending

hshSeizeSpending = C.hashScript $ C.PlutusScript C.plutusScriptVersion transferSeizeSpendingScript
credSeizeSpending = C.StakeCredentialByScript hshSeizeSpending

addConwayStakeCredentialCertificate credMinting
addConwayStakeCredentialCertificate credSpending
addConwayStakeCredentialCertificate credSeizeSpending
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration step for transfer script was missing issuer spend (seize) stake script

18 changes: 14 additions & 4 deletions src/lib/Wst/Offchain/Endpoints/Deployment.hs
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ module Wst.Offchain.Endpoints.Deployment(
import Cardano.Api (Quantity)
import Cardano.Api.Shelley qualified as C
import Control.Monad (when)
import Control.Monad.Except (MonadError)
import Control.Monad.Except (MonadError (..))
import Control.Monad.Reader (MonadReader, asks)
import Convex.BuildTx qualified as BuildTx
import Convex.Class (MonadBlockchain, MonadUtxoQuery)
import Convex.CoinSelection qualified
import Data.Foldable (maximumBy)
import Data.Function (on)
import GHC.IsList (IsList (..))
import SmartTokens.Core.Scripts (ScriptTarget (..))
import SmartTokens.Types.PTokenDirectory (DirectorySetNode (..))
import Wst.AppError (AppError)
import Wst.AppError (AppError (NoTokensToSeize))
import Wst.Offchain.BuildTx.DirectorySet (InsertNodeArgs (inaNewKey))
import Wst.Offchain.BuildTx.DirectorySet qualified as BuildTx
import Wst.Offchain.BuildTx.ProgrammableLogic qualified as BuildTx
Expand All @@ -38,7 +39,6 @@ import Wst.Offchain.Query (UTxODat (..))
import Wst.Offchain.Query qualified as Query



{-| Build a transaction that deploys the directory and global params. Returns the
transaction and the 'TxIn' that was selected for the one-shot NFTs.
-}
Expand Down Expand Up @@ -216,7 +216,17 @@ seizeCredentialAssetsTx :: forall era env m.
seizeCredentialAssetsTx sanctionedCred = do
opPkh <- asks (fst . Env.bteOperator . Env.operatorEnv)
directory <- Query.registryNodes @era
seizeTxo <- head <$> Query.userProgrammableOutputs sanctionedCred
let getTxOutValue (C.TxOut _a v _d _r) = v
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just selects the UTxO with the largest non ada asset quantities ignoring the asset names.
To seize all utxos the endpoints must be repeatedly called until NoTokensToSeize is returned.

-- simple fold to choose the UTxO with the most total assets
nonAda v = foldl (\acc -> \case
(C.AdaAssetId, _) -> acc
(_aid, q) -> acc + q
) 0 $ toList v

getNonAdaTokens = nonAda . C.txOutValueToValue . getTxOutValue . uOut
seizeTxo <- maximumBy (compare `on` getNonAdaTokens) <$> Query.userProgrammableOutputs sanctionedCred
when (getNonAdaTokens seizeTxo == 0) $
throwError NoTokensToSeize
paramsTxIn <- Query.globalParamsNode @era
(tx, _) <- Env.balanceTxEnv_ $ do
BuildTx.seizeSmartTokens paramsTxIn seizeTxo (C.PaymentCredentialByKey opPkh) directory
Expand Down
8 changes: 5 additions & 3 deletions src/lib/Wst/Server.hs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

{-| servant server for stablecoin POC
-}
Expand Down Expand Up @@ -130,7 +131,8 @@ queryBlacklistedNodes _ (SerialiseAddress addr) = do
. P.fromBuiltin
. blnKey
. uDatum
Env.withEnv $ Env.withTransfer transferLogic (fmap (fmap getHash) (Query.blacklistNodes @era))
nonHeadNodes (P.fromBuiltin . blnKey . uDatum -> hsh) = hsh /= ""
Env.withEnv $ Env.withTransfer transferLogic (fmap getHash . filter nonHeadNodes <$> (Query.blacklistNodes @era))

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Desezialization would fail in the case of the head node with an empty payment credential

txOutValue :: C.IsMaryBasedEra era => C.TxOut C.CtxUTxO era -> C.Value
txOutValue = L.view (L._TxOut . L._2 . L._TxOutValue)
Expand Down
37 changes: 19 additions & 18 deletions src/test/unit/Wst/Test/UnitTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,10 @@ deployDirectorySet = do
pure scriptRoot

insertDirectoryNode :: (MonadUtxoQuery m, MonadBlockchain C.ConwayEra m, MonadFail m) => DirectoryScriptRoot -> m ()
insertDirectoryNode scriptRoot = failOnError $ Env.withEnv $ do
asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ do
Endpoints.insertNodeTx dummyNodeArgs >>= void . sendTx . signTxOperator admin
Query.registryNodes @C.ConwayEra
>>= void . expectN 2 "registry outputs"
insertDirectoryNode scriptRoot = failOnError $ Env.withEnv $ asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ do
Endpoints.insertNodeTx dummyNodeArgs >>= void . sendTx . signTxOperator admin
Query.registryNodes @C.ConwayEra
>>= void . expectN 2 "registry outputs"

{-| Issue some tokens with the "always succeeds" validator
-}
Expand Down Expand Up @@ -190,9 +189,8 @@ blacklistTransfer = failOnError $ Env.withEnv $ do

aid <- issueTransferLogicProgrammableToken scriptRoot

asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ Env.withTransferFromOperator $ do
Endpoints.deployBlacklistTx
>>= void . sendTx . signTxOperator admin
asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ Env.withTransferFromOperator $ Endpoints.deployBlacklistTx
>>= void . sendTx . signTxOperator admin

opPkh <- asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ Env.withTransferFromOperator $ do
opPkh <- asks (fst . Env.bteOperator . Env.operatorEnv)
Expand All @@ -202,13 +200,11 @@ blacklistTransfer = failOnError $ Env.withEnv $ do

transferLogic <- Env.withDirectoryFor scriptRoot $ Env.transferLogicForDirectory (C.verificationKeyHash . Operator.verificationKey . Operator.oPaymentKey $ admin)

asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ Env.withTransferFromOperator $ do
Endpoints.blacklistCredentialTx userPaymentCred
>>= void . sendTx . signTxOperator admin
asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ Env.withTransferFromOperator $ Endpoints.blacklistCredentialTx userPaymentCred
>>= void . sendTx . signTxOperator admin

asWallet Wallet.w2 $ Env.withDirectoryFor scriptRoot $ Env.withTransfer transferLogic $ do
Endpoints.transferSmartTokensTx aid 30 (C.PaymentCredentialByKey opPkh)
>>= void . sendTx . signTxOperator (user Wallet.w2)
asWallet Wallet.w2 $ Env.withDirectoryFor scriptRoot $ Env.withTransfer transferLogic $ Endpoints.transferSmartTokensTx aid 30 (C.PaymentCredentialByKey opPkh)
>>= void . sendTx . signTxOperator (user Wallet.w2)

seizeUserOutput :: (MonadUtxoQuery m, MonadFail m, MonadMockchain C.ConwayEra m) => DirectoryScriptRoot -> m ()
seizeUserOutput scriptRoot = failOnError $ Env.withEnv $ do
Expand All @@ -217,9 +213,8 @@ seizeUserOutput scriptRoot = failOnError $ Env.withEnv $ do

aid <- issueTransferLogicProgrammableToken scriptRoot

asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ Env.withTransferFromOperator $ do
Endpoints.deployBlacklistTx
>>= void . sendTx . signTxOperator admin
asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ Env.withTransferFromOperator $ Endpoints.deployBlacklistTx
>>= void . sendTx . signTxOperator admin

asAdmin @C.ConwayEra $ Env.withDirectoryFor scriptRoot $ Env.withTransferFromOperator $ do
Endpoints.transferSmartTokensTx aid 50 (C.PaymentCredentialByKey userPkh)
Expand All @@ -240,7 +235,6 @@ seizeUserOutput scriptRoot = failOnError $ Env.withEnv $ do
Query.userProgrammableOutputs (C.PaymentCredentialByKey opPkh)
>>= void . expectN 2 "user programmable outputs"


dummyNodeArgs :: InsertNodeArgs
dummyNodeArgs =
InsertNodeArgs
Expand All @@ -267,17 +261,24 @@ registerTransferScripts :: (MonadFail m, MonadReader env m, Env.HasTransferLogic
registerTransferScripts pkh = failOnError $ do
transferMintingScript <- asks (Env.tleMintingScript . Env.transferLogicEnv)
transferSpendingScript <- asks (Env.tleTransferScript . Env.transferLogicEnv)
transferSeizeSpendingScript <- asks (Env.tleIssuerScript . Env.transferLogicEnv)

let
hshMinting = C.hashScript $ C.PlutusScript C.plutusScriptVersion transferMintingScript
credMinting = C.StakeCredentialByScript hshMinting

hshSpending = C.hashScript $ C.PlutusScript C.plutusScriptVersion transferSpendingScript
credSpending = C.StakeCredentialByScript hshSpending

hshSeizeSpending = C.hashScript $ C.PlutusScript C.plutusScriptVersion transferSeizeSpendingScript
credSeizeSpending = C.StakeCredentialByScript hshSeizeSpending


txBody <- BuildTx.execBuildTxT $ do

addConwayStakeCredentialCertificate credSpending
addConwayStakeCredentialCertificate credMinting
addConwayStakeCredentialCertificate credSeizeSpending

BuildTx.addRequiredSignature pkh

Expand Down
Loading