diff --git a/bench/tx-generator/src/Cardano/Benchmarking/Script/Aeson.hs b/bench/tx-generator/src/Cardano/Benchmarking/Script/Aeson.hs index f8489c5875b..2b85adcbc1c 100644 --- a/bench/tx-generator/src/Cardano/Benchmarking/Script/Aeson.hs +++ b/bench/tx-generator/src/Cardano/Benchmarking/Script/Aeson.hs @@ -1,35 +1,35 @@ {-# OPTIONS_GHC -Wno-orphans #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} -{-# LANGUAGE LambdaCase #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} module Cardano.Benchmarking.Script.Aeson where -import Prelude -import System.Exit -import Data.Functor.Identity -import Data.Text (Text) -import qualified Data.Text as Text -import Data.Dependent.Sum -import qualified Data.HashMap.Strict as HashMap (toList, lookup) -import qualified Data.ByteString.Lazy as BSL -import qualified Data.ByteString as BS -import qualified Data.ByteString.Char8 as BS (lines) import Data.Aeson -import Data.Aeson.Types import Data.Aeson.Encode.Pretty +import Data.Aeson.Types import qualified Data.Attoparsec.ByteString as Atto +import qualified Data.ByteString as BS +import qualified Data.ByteString.Char8 as BS (lines) +import qualified Data.ByteString.Lazy as BSL +import Data.Dependent.Sum +import Data.Functor.Identity +import qualified Data.HashMap.Strict as HashMap (lookup, toList) +import Data.Text (Text) +import qualified Data.Text as Text +import Prelude +import System.Exit -import Cardano.Api (AnyCardanoEra(..), CardanoEra(..), ScriptData, ScriptDataJsonSchema(..), scriptDataFromJson, scriptDataToJson) -import Cardano.CLI.Types (SigningKeyFile(..)) +import Cardano.Api (ScriptData, ScriptDataJsonSchema (..), scriptDataFromJson, + scriptDataToJson) +import Cardano.CLI.Types (SigningKeyFile (..)) import Cardano.Benchmarking.Script.Env import Cardano.Benchmarking.Script.Setters import Cardano.Benchmarking.Script.Store import Cardano.Benchmarking.Script.Types -import Cardano.Benchmarking.Types (NumberOfTxs(..), TPSRate(..)) +import Cardano.Benchmarking.Types (NumberOfTxs (..), TPSRate (..)) testJSONRoundTrip :: [Action] -> Maybe String testJSONRoundTrip l = case fromJSON $ toJSON l of @@ -47,15 +47,6 @@ prettyPrint = encodePretty' conf , "runBenchmark", "asyncBenchmark", "waitBenchmark", "cancelBenchmark" , "reserved" ] -instance FromJSON AnyCardanoEra where - parseJSON = withText "AnyCardanoEra" $ \case - "Byron" -> return $ AnyCardanoEra ByronEra - "Shelley" -> return $ AnyCardanoEra ShelleyEra - "Allegra" -> return $ AnyCardanoEra AllegraEra - "Mary" -> return $ AnyCardanoEra MaryEra - "Alonzo" -> return $ AnyCardanoEra AlonzoEra - era -> parseFail ("Error: Cannot parse JSON value '" <> Text.unpack era <> "' to AnyCardanoEra.") - jsonOptionsUnTaggedSum :: Options jsonOptionsUnTaggedSum = defaultOptions { sumEncoding = ObjectWithSingleField } @@ -111,7 +102,7 @@ actionToJSON a = case a of Delay t -> object ["delay" .= t ] PrepareTxList (TxListName name) (KeyName key) (FundListName fund) -> object ["prepareTxList" .= name, "newKey" .= key, "fundList" .= fund ] - AsyncBenchmark (ThreadName t) (TxListName txs) (TPSRate tps) + AsyncBenchmark (ThreadName t) (TxListName txs) (TPSRate tps) -> object ["asyncBenchmark" .= t, "txList" .= txs, "tps" .= tps] ImportGenesisFund submitMode (KeyName genesisKey) (KeyName fundKey) -> object ["importGenesisFund" .= genesisKey, "submitMode" .= submitMode, "fundKey" .= fundKey ] @@ -203,7 +194,7 @@ objectToAction obj = case obj of parseAsyncBenchmark v = AsyncBenchmark <$> ( ThreadName <$> parseJSON v ) <*> ( TxListName <$> parseField obj "txList" ) - <*> ( TPSRate <$> parseField obj "tps" ) + <*> ( TPSRate <$> parseField obj "tps" ) parseRunBenchmark v = RunBenchmark <$> parseField obj "submitMode" diff --git a/cardano-api/gen/Gen/Cardano/Api/Typed.hs b/cardano-api/gen/Gen/Cardano/Api/Typed.hs index 748fe01a2ac..c3c772af51f 100644 --- a/cardano-api/gen/Gen/Cardano/Api/Typed.hs +++ b/cardano-api/gen/Gen/Cardano/Api/Typed.hs @@ -39,6 +39,7 @@ module Gen.Cardano.Api.Typed , genStakeAddress , genTx , genTxBody + , genLovelace , genValue , genValueDefault , genVerificationKey diff --git a/cardano-api/src/Cardano/Api/Address.hs b/cardano-api/src/Cardano/Api/Address.hs index ef254b0b7d9..82a19f1222e 100644 --- a/cardano-api/src/Cardano/Api/Address.hs +++ b/cardano-api/src/Cardano/Api/Address.hs @@ -507,6 +507,15 @@ instance SerialiseAddress StakeAddress where either (const Nothing) Just $ deserialiseFromBech32 AsStakeAddress t +instance ToJSON StakeAddress where + toJSON s = Aeson.String $ serialiseAddress s + +instance FromJSON StakeAddress where + parseJSON = withText "StakeAddress" $ \str -> + case deserialiseAddress AsStakeAddress str of + Nothing -> + fail $ "Error while deserialising StakeAddress: " <> Text.unpack str + Just sAddr -> pure sAddr makeStakeAddress :: NetworkId -> StakeCredential diff --git a/cardano-api/src/Cardano/Api/Block.hs b/cardano-api/src/Cardano/Api/Block.hs index 79139c7f3f2..c9afe1a394e 100644 --- a/cardano-api/src/Cardano/Api/Block.hs +++ b/cardano-api/src/Cardano/Api/Block.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE DerivingVia #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GADTs #-} @@ -46,7 +47,7 @@ module Cardano.Api.Block ( import Prelude -import Data.Aeson (ToJSON (..), object, (.=)) +import Data.Aeson (FromJSON (..), ToJSON (..), object, (.=)) import qualified Data.Aeson as Aeson import qualified Data.ByteString as BS import qualified Data.ByteString.Short as SBS @@ -69,15 +70,16 @@ import qualified Ouroboros.Network.Block as Consensus import qualified Cardano.Chain.Block as Byron import qualified Cardano.Chain.UTxO as Byron +import qualified Cardano.Ledger.Block as Ledger import qualified Cardano.Ledger.Era as Ledger import qualified Cardano.Protocol.TPraos.BHeader as TPraos -import qualified Cardano.Ledger.Block as Ledger import Cardano.Api.Eras import Cardano.Api.HasTypeProxy import Cardano.Api.Hash import Cardano.Api.Modes import Cardano.Api.SerialiseRaw +import Cardano.Api.SerialiseUsing import Cardano.Api.Tx {- HLINT ignore "Use lambda" -} @@ -237,6 +239,9 @@ data BlockHeader = BlockHeader !SlotNo -- representation. newtype instance Hash BlockHeader = HeaderHash SBS.ShortByteString deriving (Eq, Ord, Show) + deriving (ToJSON, FromJSON) via UsingRawBytesHex (Hash BlockHeader) + + instance SerialiseAsRawBytes (Hash BlockHeader) where serialiseToRawBytes (HeaderHash bs) = SBS.fromShort bs diff --git a/cardano-api/src/Cardano/Api/Eras.hs b/cardano-api/src/Cardano/Api/Eras.hs index ded57c49aa0..a599f8273cd 100644 --- a/cardano-api/src/Cardano/Api/Eras.hs +++ b/cardano-api/src/Cardano/Api/Eras.hs @@ -46,7 +46,8 @@ module Cardano.Api.Eras import Prelude -import Data.Aeson (ToJSON, toJSON) +import Data.Aeson (FromJSON (..), ToJSON, toJSON, withText) +import qualified Data.Text as Text import Data.Type.Equality (TestEquality (..), (:~:) (Refl)) import Ouroboros.Consensus.Shelley.Eras as Ledger (StandardAllegra, StandardAlonzo, @@ -229,6 +230,17 @@ instance Enum AnyCardanoEra where instance ToJSON AnyCardanoEra where toJSON (AnyCardanoEra era) = toJSON era +instance FromJSON AnyCardanoEra where + parseJSON = withText "AnyCardanoEra" + $ \case + "Byron" -> pure $ AnyCardanoEra ByronEra + "Shelley" -> pure $ AnyCardanoEra ShelleyEra + "Allegra" -> pure $ AnyCardanoEra AllegraEra + "Mary" -> pure $ AnyCardanoEra MaryEra + "Alonzo" -> pure $ AnyCardanoEra AlonzoEra + wrong -> fail $ "Failed to parse unknown era: " <> Text.unpack wrong + + -- | Like the 'AnyCardanoEra' constructor but does not demand a 'IsCardanoEra' -- class constraint. -- diff --git a/cardano-api/src/Cardano/Api/KeysShelley.hs b/cardano-api/src/Cardano/Api/KeysShelley.hs index 2da67760739..1cca72bcdd6 100644 --- a/cardano-api/src/Cardano/Api/KeysShelley.hs +++ b/cardano-api/src/Cardano/Api/KeysShelley.hs @@ -37,11 +37,12 @@ module Cardano.Api.KeysShelley ( import Prelude -import Data.Aeson.Types (ToJSONKey (..), toJSONKeyText) +import Data.Aeson.Types (ToJSONKey (..), toJSONKeyText, withText) import Data.ByteString (ByteString) import qualified Data.ByteString as BS import Data.Maybe import Data.String (IsString (..)) +import qualified Data.Text as Text import qualified Cardano.Crypto.DSIGN.Class as Crypto import qualified Cardano.Crypto.Hash.Class as Crypto @@ -52,6 +53,7 @@ import qualified Cardano.Ledger.Keys as Shelley import Cardano.Ledger.Crypto (StandardCrypto) +import Cardano.Api.Error import Cardano.Api.Hash import Cardano.Api.HasTypeProxy import Cardano.Api.Key @@ -62,7 +64,6 @@ import Cardano.Api.SerialiseRaw import Cardano.Api.SerialiseTextEnvelope import Cardano.Api.SerialiseUsing - -- -- Shelley payment keys -- @@ -1205,6 +1206,14 @@ instance ToJSON (Hash StakePoolKey) where instance ToJSONKey (Hash StakePoolKey) where toJSONKey = toJSONKeyText serialiseToBech32 +instance FromJSON (Hash StakePoolKey) where + parseJSON = withText "PoolId" $ \str -> + case deserialiseFromBech32 (AsHash AsStakePoolKey) str of + Left err -> + fail $ "Error deserialising Hash StakePoolKey: " <> Text.unpack str <> + " Error: " <> displayError err + Right h -> pure h + instance HasTextEnvelope (VerificationKey StakePoolKey) where textEnvelopeType _ = "StakePoolVerificationKey_" <> fromString (Crypto.algorithmNameDSIGN proxy) diff --git a/cardano-cli/cardano-cli.cabal b/cardano-cli/cardano-cli.cabal index 13d96c8aa47..62fb7e92396 100644 --- a/cardano-cli/cardano-cli.cabal +++ b/cardano-cli/cardano-cli.cabal @@ -176,6 +176,7 @@ test-suite cardano-cli-test , cardano-node , cardano-prelude , cardano-slotting + , containers , directory , exceptions , filepath @@ -189,6 +190,7 @@ test-suite cardano-cli-test other-modules: Test.Config.Mainnet Test.Cli.FilePermissions Test.Cli.ITN + Test.Cli.JSON Test.Cli.MultiAssetParsing Test.Cli.Pioneers.Exercise1 Test.Cli.Pioneers.Exercise2 diff --git a/cardano-cli/src/Cardano/CLI/Shelley/Commands.hs b/cardano-cli/src/Cardano/CLI/Shelley/Commands.hs index 977124b7498..7c82f3d207a 100644 --- a/cardano-cli/src/Cardano/CLI/Shelley/Commands.hs +++ b/cardano-cli/src/Cardano/CLI/Shelley/Commands.hs @@ -39,7 +39,6 @@ module Cardano.CLI.Shelley.Commands , VerificationKeyBase64 (..) , GenesisKeyFile (..) , MetadataFile (..) - , PoolId (..) , PoolMetadataFile (..) , PrivKeyFile (..) , BlockId (..) @@ -50,7 +49,7 @@ module Cardano.CLI.Shelley.Commands import Data.Text (Text) import Prelude -import Cardano.Api.Shelley hiding (PoolId) +import Cardano.Api.Shelley import Ouroboros.Consensus.BlockchainTime (SystemStart (..)) @@ -467,10 +466,6 @@ newtype OutputFile = OutputFile FilePath deriving Show -newtype PoolId - = PoolId String -- Probably not a String - deriving Show - newtype PoolMetadataFile = PoolMetadataFile { unPoolMetadataFile :: FilePath } deriving Show diff --git a/cardano-cli/src/Cardano/CLI/Shelley/Orphans.hs b/cardano-cli/src/Cardano/CLI/Shelley/Orphans.hs index 79bb96050ac..2914ede157d 100644 --- a/cardano-cli/src/Cardano/CLI/Shelley/Orphans.hs +++ b/cardano-cli/src/Cardano/CLI/Shelley/Orphans.hs @@ -38,9 +38,9 @@ import qualified Cardano.Ledger.Credential as Ledger import qualified Cardano.Ledger.Shelley.API.Protocol as Ledger import qualified Cardano.Ledger.Shelley.EpochBoundary as Ledger import qualified Cardano.Ledger.Shelley.Rewards as Ledger +import Cardano.Ledger.TxIn (TxId (..)) import qualified Cardano.Protocol.TPraos.Rules.Prtcl as Ledger import qualified Cardano.Protocol.TPraos.Rules.Tickn as Ledger -import Cardano.Ledger.TxIn (TxId (..)) import qualified Cardano.Ledger.Mary.Value as Ledger.Mary @@ -64,8 +64,8 @@ instance ToJSON (HeaderHash blk) => ToJSON (Tip blk) where , "blockNo" .= blockNo ] --- This instance is temporarily duplicated in cardano-config deriving newtype instance ToJSON BlockNo +deriving newtype instance FromJSON BlockNo -- -- Simple newtype wrappers JSON conversion diff --git a/cardano-cli/src/Cardano/CLI/Shelley/Output.hs b/cardano-cli/src/Cardano/CLI/Shelley/Output.hs index a688540a0ea..b310335a5da 100644 --- a/cardano-cli/src/Cardano/CLI/Shelley/Output.hs +++ b/cardano-cli/src/Cardano/CLI/Shelley/Output.hs @@ -7,17 +7,15 @@ module Cardano.CLI.Shelley.Output ) where import Cardano.Api +import Prelude + +import Data.Aeson (FromJSON (..), KeyValue, ToJSON (..), object, pairs, withObject, (.:?), + (.=)) +import Data.Text (Text) import Cardano.CLI.Shelley.Orphans () -import Cardano.Prelude (Text) -import Cardano.Slotting.Time (SystemStart (..)) -import Data.Aeson (KeyValue, ToJSON (..), (.=)) -import Data.Function (id, ($), (.)) -import Data.Maybe ( Maybe(..) ) -import Data.Monoid (mconcat) import Cardano.Ledger.Shelley.Scripts () - -import qualified Data.Aeson as J +import Cardano.Slotting.Time (SystemStart (..)) data QueryTipLocalState mode = QueryTipLocalState { era :: AnyCardanoEra @@ -31,7 +29,7 @@ data QueryTipLocalStateOutput = QueryTipLocalStateOutput , mEra :: Maybe AnyCardanoEra , mEpoch :: Maybe EpochNo , mSyncProgress :: Maybe Text - } + } deriving Show -- | A key-value pair difference list for encoding a JSON object. (..=) :: (KeyValue kv, ToJSON v) => Text -> v -> [kv] -> [kv] @@ -46,13 +44,13 @@ data QueryTipLocalStateOutput = QueryTipLocalStateOutput instance ToJSON QueryTipLocalStateOutput where toJSON a = case localStateChainTip a of ChainTipAtGenesis -> - J.object $ + object $ ( ("era" ..=? mEra a) . ("epoch" ..=? mEpoch a) . ("syncProgress" ..=? mSyncProgress a) ) [] ChainTip slotNo blockHeader blockNo -> - J.object $ + object $ ( ("slot" ..= slotNo) . ("hash" ..= serialiseToRawBytesHexText blockHeader) . ("block" ..= blockNo) @@ -62,13 +60,13 @@ instance ToJSON QueryTipLocalStateOutput where ) [] toEncoding a = case localStateChainTip a of ChainTipAtGenesis -> - J.pairs $ mconcat $ + pairs $ mconcat $ ( ("era" ..=? mEra a) . ("epoch" ..=? mEpoch a) . ("syncProgress" ..=? mSyncProgress a) ) [] ChainTip slotNo blockHeader blockNo -> - J.pairs $ mconcat $ + pairs $ mconcat $ ( ("slot" ..= slotNo) . ("hash" ..= serialiseToRawBytesHexText blockHeader) . ("block" ..= blockNo) @@ -76,3 +74,31 @@ instance ToJSON QueryTipLocalStateOutput where . ("epoch" ..=? mEpoch a) . ("syncProgress" ..=? mSyncProgress a) ) [] + +instance FromJSON QueryTipLocalStateOutput where + parseJSON = withObject "QueryTipLocalStateOutput" $ \o -> do + mEra' <- o .:? "era" + mEpoch' <- o .:? "epoch" + mSyncProgress' <- o .:? "syncProgress" + + mSlot <- o .:? "slot" + mHash <- o .:? "hash" + mBlock <- o .:? "block" + case (mSlot, mHash, mBlock) of + (Nothing, Nothing, Nothing) -> + pure $ QueryTipLocalStateOutput + ChainTipAtGenesis + mEra' + mEpoch' + mSyncProgress' + (Just slot, Just hash, Just block) -> + pure $ QueryTipLocalStateOutput + (ChainTip slot hash block) + mEra' + mEpoch' + mSyncProgress' + (_,_,_) -> fail "QueryTipLocalStateOutput was incorrectly JSON encoded.\ + \ Expected slot, header hash and block number (ChainTip)\ + \ or none (ChainTipAtGenesis)" + + diff --git a/cardano-cli/src/Cardano/CLI/Shelley/Run/Query.hs b/cardano-cli/src/Cardano/CLI/Shelley/Run/Query.hs index c8606cd520e..2ec53783259 100644 --- a/cardano-cli/src/Cardano/CLI/Shelley/Run/Query.hs +++ b/cardano-cli/src/Cardano/CLI/Shelley/Run/Query.hs @@ -12,18 +12,23 @@ {-# OPTIONS_GHC -fno-warn-orphans #-} module Cardano.CLI.Shelley.Run.Query - ( ShelleyQueryCmdError + ( DelegationsAndRewards(..) + , ShelleyQueryCmdError , ShelleyQueryCmdLocalStateQueryError (..) , renderShelleyQueryCmdError , renderLocalStateQueryError , runQueryCmd + , mergeDelegsAndRewards , percentage , executeQuery ) where +import Prelude (String, id) + import Cardano.Api import Cardano.Api.Byron import Cardano.Api.Shelley + import Cardano.Binary (decodeFull) import Cardano.CLI.Environment (EnvSocketError, readEnvSocketPath, renderEnvSocketError) import Cardano.CLI.Helpers (HelpersError (..), hushM, pPrintCBOR, renderHelpersError) @@ -46,31 +51,31 @@ import Cardano.Ledger.Shelley.Scripts () import Cardano.Prelude hiding (atomically) import Control.Monad.Trans.Except (except) import Control.Monad.Trans.Except.Extra (firstExceptT, handleIOExceptT, hoistMaybe, left) -import Data.Aeson (ToJSON (..), (.=)) -import qualified Data.Aeson as Aeson import Data.Aeson.Encode.Pretty (encodePretty) +import Data.Aeson.Types as Aeson +import Data.List (nub) +import Data.Time.Clock +import Numeric (showEFloat) +import Ouroboros.Consensus.BlockchainTime.WallClock.Types (RelativeTime (..), + SystemStart (..), toRelativeTime) +import Ouroboros.Consensus.Cardano.Block as Consensus (EraMismatch (..)) +import Ouroboros.Network.Block (Serialised (..)) +import Ouroboros.Network.Protocol.LocalStateQuery.Type (AcquireFailure (..)) +import Text.Printf (printf) + import qualified Data.ByteString.Lazy.Char8 as LBS import qualified Data.Compact.VMap as VMap -import Data.List (nub) import qualified Data.Map.Strict as Map import qualified Data.Set as Set import qualified Data.Text as Text import qualified Data.Text.Encoding as Text import qualified Data.Text.IO as T import qualified Data.Text.IO as Text -import Data.Time.Clock import qualified Data.Vector as Vector -import Numeric (showEFloat) -import Ouroboros.Consensus.BlockchainTime.WallClock.Types (RelativeTime (..), - SystemStart (..), toRelativeTime) -import Ouroboros.Consensus.Cardano.Block as Consensus (EraMismatch (..)) + import qualified Ouroboros.Consensus.HardFork.History.Qry as Qry -import Ouroboros.Network.Block (Serialised (..)) -import Ouroboros.Network.Protocol.LocalStateQuery.Type (AcquireFailure (..)) import qualified Ouroboros.Network.Protocol.LocalStateQuery.Type as LocalStateQuery -import Prelude (String, id) import qualified System.IO as IO -import Text.Printf (printf) {- HLINT ignore "Reduce duplication" -} @@ -803,8 +808,10 @@ printStakeDistribution stakeDistrib = do -- | A mapping of Shelley reward accounts to both the stake pool that they -- delegate to and their reward account balance. +-- TODO: Move to cardano-api newtype DelegationsAndRewards = DelegationsAndRewards (Map StakeAddress Lovelace, Map StakeAddress PoolId) + deriving (Eq, Show) mergeDelegsAndRewards :: DelegationsAndRewards -> [(StakeAddress, Maybe Lovelace, Maybe PoolId)] @@ -822,11 +829,39 @@ instance ToJSON DelegationsAndRewards where delegAndRwdToJson :: (StakeAddress, Maybe Lovelace, Maybe PoolId) -> Aeson.Value delegAndRwdToJson (addr, mRewards, mPoolId) = Aeson.object - [ "address" .= serialiseAddress addr + [ "address" .= addr , "delegation" .= mPoolId , "rewardAccountBalance" .= mRewards ] +instance FromJSON DelegationsAndRewards where + parseJSON = withArray "DelegationsAndRewards" $ \arr -> do + let vals = Vector.toList arr + decoded <- mapM decodeObject vals + pure $ zipper decoded + where + zipper :: [(StakeAddress, Maybe Lovelace, Maybe PoolId)] + -> DelegationsAndRewards + zipper l = do + let maps = [ ( maybe mempty (Map.singleton sa) delegAmt + , maybe mempty (Map.singleton sa) mPool + ) + | (sa, delegAmt, mPool) <- l + ] + DelegationsAndRewards + $ foldl + (\(amtA, delegA) (amtB, delegB) -> (amtA <> amtB, delegA <> delegB)) + (mempty, mempty) + maps + + decodeObject :: Aeson.Value + -> Aeson.Parser (StakeAddress, Maybe Lovelace, Maybe PoolId) + decodeObject = withObject "DelegationsAndRewards" $ \o -> do + address <- o .: "address" + delegation <- o .:? "delegation" + rewardAccountBalance <- o .:? "rewardAccountBalance" + pure (address, rewardAccountBalance, delegation) + -- Helpers calcEraInMode diff --git a/cardano-cli/test/Test/Cli/JSON.hs b/cardano-cli/test/Test/Cli/JSON.hs new file mode 100644 index 00000000000..dfc8ce67764 --- /dev/null +++ b/cardano-cli/test/Test/Cli/JSON.hs @@ -0,0 +1,44 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} + +module Test.Cli.JSON where + +import Cardano.Prelude hiding (filter) + +import Cardano.Api.Shelley +import Gen.Cardano.Api.Typed (genLovelace, genStakeAddress, genVerificationKeyHash) + +import Data.Aeson +import qualified Data.Map.Strict as Map + +import Cardano.CLI.Shelley.Run.Query + +import Hedgehog (Gen, Property, checkSequential, discover, forAll, property, tripping) +import Hedgehog.Gen as Gen +import Hedgehog.Range as Range + +-- TODO: Move to cardano-api +prop_json_roundtrip_delegations_and_rewards :: Property +prop_json_roundtrip_delegations_and_rewards = + property $ do + dAndG <- forAll genDelegationsAndRewards + tripping dAndG encode eitherDecode + +genDelegationsAndRewards :: Gen DelegationsAndRewards +genDelegationsAndRewards = do + let r = Range.constant 0 3 + sAddrs <- Gen.list r genStakeAddress + sLovelace <- Gen.list r genLovelace + let delegMapAmt = Map.fromList $ zip sAddrs sLovelace + poolIDs <- Gen.list r genPoolId + let delegMapPool = Map.fromList $ zip sAddrs poolIDs + return $ DelegationsAndRewards (delegMapAmt,delegMapPool) + +genPoolId :: Gen (Hash StakePoolKey) +genPoolId = genVerificationKeyHash AsStakePoolKey + +-- ----------------------------------------------------------------------------- + +tests :: IO Bool +tests = + checkSequential $$discover diff --git a/cardano-cli/test/cardano-cli-test.hs b/cardano-cli/test/cardano-cli-test.hs index e0f8afe1c99..e7ef43780f4 100644 --- a/cardano-cli/test/cardano-cli-test.hs +++ b/cardano-cli/test/cardano-cli-test.hs @@ -4,6 +4,7 @@ import Hedgehog.Main (defaultMain) import qualified Test.Cli.FilePermissions import qualified Test.Cli.ITN +import qualified Test.Cli.JSON import qualified Test.Cli.MultiAssetParsing import qualified Test.Cli.Pioneers.Exercise1 import qualified Test.Cli.Pioneers.Exercise2 @@ -17,6 +18,7 @@ main = defaultMain [ Test.Cli.FilePermissions.tests , Test.Cli.ITN.tests + , Test.Cli.JSON.tests , Test.Cli.MultiAssetParsing.tests , Test.Cli.Pioneers.Exercise1.tests , Test.Cli.Pioneers.Exercise2.tests diff --git a/cardano-testnet/cardano-testnet.cabal b/cardano-testnet/cardano-testnet.cabal index 77235f0f655..c933cbd1530 100644 --- a/cardano-testnet/cardano-testnet.cabal +++ b/cardano-testnet/cardano-testnet.cabal @@ -36,6 +36,8 @@ library build-depends: aeson , base16-bytestring , bytestring + , cardano-api + , cardano-cli , cardano-node , containers , directory @@ -49,6 +51,7 @@ library , process , random , resourcet + , safe-exceptions , text , time , unordered-containers @@ -63,6 +66,7 @@ library Testnet.List Testnet.Shelley Testnet.SubmitApi + Testnet.Utils executable cardano-testnet import: base, project-config @@ -106,6 +110,7 @@ test-suite cardano-testnet-tests , base16-bytestring , bytestring , cardano-api + , cardano-cli , cardano-testnet , containers , directory @@ -117,7 +122,8 @@ test-suite cardano-testnet-tests , text , unordered-containers - other-modules: Spec.Plutus.Direct.ScriptContextEquality + other-modules: Spec.Plutus.Direct.CertifyingAndWithdrawingPlutus + Spec.Plutus.Direct.ScriptContextEquality Spec.Plutus.Direct.ScriptContextEqualityMint Spec.Plutus.Direct.TxInLockingPlutus Spec.Plutus.Script.TxInLockingPlutus diff --git a/cardano-testnet/src/Testnet/Cardano.hs b/cardano-testnet/src/Testnet/Cardano.hs index dafe29ff79c..9a8757958ea 100644 --- a/cardano-testnet/src/Testnet/Cardano.hs +++ b/cardano-testnet/src/Testnet/Cardano.hs @@ -190,17 +190,16 @@ testnet testnetOptions H.Conf {..} = do startTime <- H.noteShow $ DTC.addUTCTime startTimeOffsetSeconds currentTime configurationTemplate <- H.noteShow $ base "configuration/defaults/byron-mainnet/configuration.yaml" configurationFile <- H.noteShow $ tempAbsPath "configuration.yaml" - let bftNodesN = [1 .. numBftNodes testnetOptions] - let poolNodesN = [1 .. numPoolNodes testnetOptions] - let bftNodes = ("node-bft" <>) . show @Int <$> bftNodesN - let poolNodes = ("node-pool" <>) . show @Int <$> poolNodesN - let allNodes = bftNodes <> poolNodes - let initSupply = 1000000000 - let maxSupply = 1000000000 - let fundsPerGenesisAddress = initSupply `div` numBftNodes testnetOptions - let fundsPerByronAddress = fundsPerGenesisAddress * 9 `div` 10 - let userPoolN = poolNodesN + poolNodesN = [1 .. numPoolNodes testnetOptions] + bftNodes = ("node-bft" <>) . show @Int <$> bftNodesN + poolNodes = ("node-pool" <>) . show @Int <$> poolNodesN + allNodes = bftNodes <> poolNodes + maxByronSupply = 10020000000 + fundsPerGenesisAddress = maxByronSupply `div` numBftNodes testnetOptions + fundsPerByronAddress = fundsPerGenesisAddress - 100000000 + userPoolN = poolNodesN + maxShelleySupply = 1000000000000 allPorts <- H.noteShowIO $ IO.allocateRandomPorts (L.length allNodes) nodeToPort <- H.noteShow (M.fromList (L.zip allNodes allPorts)) @@ -307,7 +306,7 @@ testnet testnetOptions H.Conf {..} = do , "--k", show @Int securityParam , "--n-poor-addresses", "0" , "--n-delegate-addresses", show @Int (numBftNodes testnetOptions) - , "--total-balance", show @Int initSupply + , "--total-balance", show @Int maxByronSupply , "--delegate-share", "1" , "--avvm-entry-count", "0" , "--avvm-entry-balance", "0" @@ -357,7 +356,7 @@ testnet testnetOptions H.Conf {..} = do , "--tx", tempAbsPath "tx0.tx" , "--wallet-key", tempAbsPath "byron/delegate-keys.000.key" , "--rich-addr-from", richAddrFrom - , "--txout", show @(String, Int) (txAddr, fundsPerGenesisAddress) + , "--txout", show @(String, Int) (txAddr, fundsPerByronAddress) ] -- Update Proposal and votes @@ -428,14 +427,16 @@ testnet testnetOptions H.Conf {..} = do -- and K=10, but we'll keep long KES periods so we don't have to bother -- cycling KES keys H.rewriteJsonFile (tempAbsPath "shelley/genesis.spec.json") . J.rewriteObject - $ HM.insert "slotLength" (J.toJSON @Double 0.2) - . HM.insert "activeSlotsCoeff" (J.toJSON @Double (activeSlotsCoeff testnetOptions)) + $ HM.insert "activeSlotsCoeff" (J.toJSON @Double (activeSlotsCoeff testnetOptions)) . HM.insert "securityParam" (J.toJSON @Int 10) . HM.insert "epochLength" (J.toJSON @Int (epochLength testnetOptions)) - . HM.insert "slotLength" (J.toJSON @Double 0.2) - . HM.insert "maxLovelaceSupply" (J.toJSON @Int maxSupply) + . HM.insert "slotLength" (J.toJSON @Double (slotLength testnetOptions)) + . HM.insert "maxLovelaceSupply" (J.toJSON @Int maxShelleySupply) . flip HM.adjust "protocolParams" - ( J.rewriteObject (HM.insert "decentralisationParam" (J.toJSON @Double 0.7)) + ( J.rewriteObject ( HM.insert "decentralisationParam" (J.toJSON @Double 0.7) + . HM.insert "rho" (J.toJSON @Double 0.1) + . HM.insert "tau" (J.toJSON @Double 0.1) + ) ) -- Now generate for real: @@ -455,7 +456,7 @@ testnet testnetOptions H.Conf {..} = do $ flip HM.adjust "protocolParams" ( J.rewriteObject ( flip HM.adjust "protocolVersion" - ( J.rewriteObject (HM.insert "major" (J.toJSON @Int 2)) + ( J.rewriteObject (HM.insert "major" (J.toJSON @Int 6)) ) ) ) @@ -518,8 +519,9 @@ testnet testnetOptions H.Conf {..} = do -- pool-owner1..n: will be the owner of the pools and we'll use their reward -- account for pool rewards let userAddrs = ("user" <>) . show @Int <$> userPoolN - let poolAddrs = ("pool-owner" <>) . show @Int <$> poolNodesN - let addrs = userAddrs <> poolAddrs + poolAddrs = ("pool-owner" <>) . show @Int <$> poolNodesN + addrs = userAddrs <> poolAddrs + H.createDirectoryIfMissing $ tempAbsPath "addresses" @@ -534,13 +536,30 @@ testnet testnetOptions H.Conf {..} = do , "--signing-key-file", paymentSKey ] - -- Stake address keys + void $ H.execCli + [ "address", "key-gen" + , "--verification-key-file", tempAbsPath "shelley/utxo-keys/utxo2.vkey" + , "--signing-key-file", tempAbsPath "shelley/utxo-keys/utxo2.skey" + ] + void $ H.execCli [ "stake-address", "key-gen" , "--verification-key-file", tempAbsPath "addresses/" <> addr <> "-stake.vkey" , "--signing-key-file", tempAbsPath "addresses/" <> addr <> "-stake.skey" ] + void $ H.execCli + [ "stake-address", "key-gen" + , "--verification-key-file", tempAbsPath "shelley/utxo-keys/utxo-stake.vkey" + , "--signing-key-file", tempAbsPath "shelley/utxo-keys/utxo-stake.skey" + ] + + void $ H.execCli + [ "stake-address", "key-gen" + , "--verification-key-file", tempAbsPath "shelley/utxo-keys/utxo2-stake.vkey" + , "--signing-key-file", tempAbsPath "shelley/utxo-keys/utxo2-stake.skey" + ] + -- Payment addresses void $ H.execCli [ "address", "build" @@ -628,7 +647,7 @@ testnet testnetOptions H.Conf {..} = do , "--invalid-hereafter", "1000" , "--fee", "0" , "--tx-in", txIn - , "--tx-out", user1Addr <> "+" <> show @Int maxSupply + , "--tx-out", user1Addr <> "+" <> show @Int maxShelleySupply , "--certificate-file", tempAbsPath "addresses/pool-owner1-stake.reg.cert" , "--certificate-file", tempAbsPath "node-pool1/registration.cert" , "--certificate-file", tempAbsPath "addresses/user1-stake.reg.cert" diff --git a/cardano-testnet/src/Testnet/Utils.hs b/cardano-testnet/src/Testnet/Utils.hs new file mode 100644 index 00000000000..b9da693c3d0 --- /dev/null +++ b/cardano-testnet/src/Testnet/Utils.hs @@ -0,0 +1,63 @@ +{-# LANGUAGE NumericUnderscores #-} +{-# LANGUAGE TypeApplications #-} + +module Testnet.Utils + ( waitUntilEpoch + ) where + +import Cardano.Api + +import Control.Concurrent (threadDelay) +import Control.Exception.Safe (MonadCatch) +import Control.Monad +import Control.Monad.IO.Class +import Data.Aeson (fromJSON) +import Data.Eq +import Data.Function +import Data.Int +import Data.Maybe +import GHC.Stack +import System.Directory (doesFileExist, removeFile) +import System.FilePath (FilePath) +import Text.Show + +import Cardano.CLI.Shelley.Output + +import qualified Hedgehog.Extras.Test.Base as H +import qualified Hedgehog.Extras.Test.File as H +import Hedgehog.Extras.Test.Process (ExecConfig) +import Hedgehog.Internal.Property (MonadTest) +import qualified Test.Process as H + +-- | Submit the desired epoch to wait to. +waitUntilEpoch + :: (MonadCatch m, MonadIO m, MonadTest m) + => FilePath + -- ^ Output file + -> Int + -- ^ Testnet magic + -> ExecConfig + -> EpochNo + -- ^ Desired epoch + -> m EpochNo +waitUntilEpoch fp testnetMagic execConfig desiredEpoch = do + exists <- liftIO $ doesFileExist fp + when exists $ liftIO $ removeFile fp + + void $ H.execCli' execConfig + [ "query", "tip" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", fp + ] + + tipJSON <- H.leftFailM $ H.readJsonFile fp + tip <- H.noteShowM $ H.jsonErrorFail $ fromJSON @QueryTipLocalStateOutput tipJSON + case mEpoch tip of + Nothing -> + H.failMessage + callStack "waitUntilEpoch: cardano-cli query tip returned Nothing for EpochNo" + Just currEpoch -> + if currEpoch == desiredEpoch + then return currEpoch + else do liftIO $ threadDelay 10_000_000 + waitUntilEpoch fp testnetMagic execConfig desiredEpoch diff --git a/cardano-testnet/test/Main.hs b/cardano-testnet/test/Main.hs index 728c4d2186c..e2cbb079aa1 100644 --- a/cardano-testnet/test/Main.hs +++ b/cardano-testnet/test/Main.hs @@ -14,6 +14,7 @@ import qualified Test.Tasty as T import qualified Test.Tasty.Hedgehog as H import qualified Test.Tasty.Ingredients as T +import qualified Spec.Plutus.Direct.CertifyingAndWithdrawingPlutus import qualified Spec.Plutus.Direct.ScriptContextEquality import qualified Spec.Plutus.Direct.ScriptContextEqualityMint import qualified Spec.Plutus.Direct.TxInLockingPlutus @@ -24,7 +25,8 @@ tests :: IO TestTree tests = do pure $ T.testGroup "test/Spec.hs" [ T.testGroup "Spec" - [ H.testProperty "Spec.Plutus.Direct.TxInLockingPlutus" Spec.Plutus.Direct.TxInLockingPlutus.hprop_plutus + [ H.testProperty "Spec.Plutus.Direct.CertifyingAndWithdrawingPlutus" Spec.Plutus.Direct.CertifyingAndWithdrawingPlutus.hprop_plutus_certifying_withdrawing + , H.testProperty "Spec.Plutus.Direct.TxInLockingPlutus" Spec.Plutus.Direct.TxInLockingPlutus.hprop_plutus -- This hangs on Windows for an unknown reason , ignoreOnWindows "Spec.Plutus.Script.TxInLockingPlutus" Spec.Plutus.Script.TxInLockingPlutus.hprop_plutus , H.testProperty "Spec.Plutus.SubmitApi.TxInLockingPlutus" Spec.Plutus.SubmitApi.TxInLockingPlutus.hprop_plutus diff --git a/cardano-testnet/test/Spec/Plutus/Direct/CertifyingAndWithdrawingPlutus.hs b/cardano-testnet/test/Spec/Plutus/Direct/CertifyingAndWithdrawingPlutus.hs new file mode 100644 index 00000000000..598555a6a7e --- /dev/null +++ b/cardano-testnet/test/Spec/Plutus/Direct/CertifyingAndWithdrawingPlutus.hs @@ -0,0 +1,715 @@ +{-# LANGUAGE DisambiguateRecordFields #-} +{-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE ScopedTypeVariables #-} +{-# LANGUAGE TypeApplications #-} + +module Spec.Plutus.Direct.CertifyingAndWithdrawingPlutus + ( hprop_plutus_certifying_withdrawing + ) where + + +import Prelude + +import Cardano.Api +import Cardano.Api.Shelley + +import Control.Monad (void) +import qualified Data.Aeson as J +import qualified Data.Map.Strict as Map +import Data.Monoid (Last (..)) +import Data.Set (Set) +import qualified Data.Set as Set +import qualified Data.Text as T +import GHC.Stack (callStack) +import qualified System.Directory as IO +import System.Environment (getEnvironment) +import System.FilePath (()) +import System.Info (os) + +import Cardano.CLI.Shelley.Output +import Cardano.CLI.Shelley.Run.Query + +import Hedgehog (Property, (/==), (===)) +import qualified Hedgehog as H +import qualified Hedgehog.Extras.Stock.IO.Network.Sprocket as IO +import qualified Hedgehog.Extras.Test.Base as H +import qualified Hedgehog.Extras.Test.Concurrent as H +import qualified Hedgehog.Extras.Test.File as H +import qualified Hedgehog.Extras.Test.Process as H +import qualified Test.Base as H +import qualified Test.Process as H +import Testnet.Cardano (TestnetOptions (..), TestnetRuntime (..), defaultTestnetOptions, + testnet) +import qualified Testnet.Cardano as TC +import qualified Testnet.Conf as H +import Testnet.Utils (waitUntilEpoch) + + +{- +The aim is to test a Plutus certifying and rewarding script. Certifying in the sense of validating a certifiate +e.g in this case a delegating certificate and rewarding in the sense of validating a rewards withdrawal. +In this test, we delegate a Plutus script staking address to our stake pool. We must: + 1. Create a stake pool + 2. Delegate our Plutus script address to said staking pool + 3. Withdraw our rewards from our Plutus script staking address. +-} + +isLinux :: Bool +isLinux = os == "linux" + +hprop_plutus_certifying_withdrawing :: Property +hprop_plutus_certifying_withdrawing = H.integration . H.runFinallies . H.workspace "chairman" $ \tempAbsBasePath' -> do + projectBase <- H.note =<< H.noteIO . IO.canonicalizePath =<< H.getProjectBase + conf@H.Conf { H.tempBaseAbsPath, H.tempAbsPath } <- H.noteShowM $ H.mkConf tempAbsBasePath' Nothing + + let fastTestnetOptions = defaultTestnetOptions + { epochLength = 500 + , slotLength = 0.01 + , activeSlotsCoeff = 0.1 + } + TC.TestnetRuntime { bftSprockets, testnetMagic } <- testnet fastTestnetOptions conf + + env <- H.evalIO getEnvironment + + execConfig <- H.noteShow H.ExecConfig + { H.execConfigEnv = Last $ Just $ + [ ("CARDANO_NODE_SOCKET_PATH", IO.sprocketArgumentName (head bftSprockets)) + ] + -- The environment must be passed onto child process on Windows in order to + -- successfully start that process. + <> env + , H.execConfigCwd = Last $ Just tempBaseAbsPath + } + + -- First we note all the relevant files + base <- H.note projectBase + work <- H.note tempAbsPath + + -- We get our UTxOs from here + utxoVKeyFile <- H.note $ tempAbsPath "shelley/utxo-keys/utxo1.vkey" + utxoSKeyFile <- H.note $ tempAbsPath "shelley/utxo-keys/utxo1.skey" + utxoVKeyFile2 <- H.note $ tempAbsPath "shelley/utxo-keys/utxo2.vkey" + utxoSKeyFile2 <- H.note $ tempAbsPath "shelley/utxo-keys/utxo2.skey" + + utxoAddr <- H.execCli + [ "address", "build" + , "--testnet-magic", show @Int testnetMagic + , "--payment-verification-key-file", utxoVKeyFile + ] + + void $ H.execCli' execConfig + [ "query", "utxo" + , "--address", utxoAddr + , "--cardano-mode" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "utxo-1.json" + ] + + H.cat $ work "utxo-1.json" + + utxo1Json <- H.leftFailM . H.readJsonFile $ work "utxo-1.json" + UTxO utxo1 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(UTxO AlonzoEra) utxo1Json + txin <- H.noteShow $ head $ Map.keys utxo1 + + -- Staking keys + utxoStakingVkey2 <- H.note $ tempAbsPath "shelley/utxo-keys/utxo2-stake.vkey" + utxoStakingSkey2 <- H.note $ tempAbsPath "shelley/utxo-keys/utxo2-stake.skey" + + utxoaddrwithstaking <- H.execCli [ "address", "build" + , "--payment-verification-key-file", utxoVKeyFile2 + , "--stake-verification-key-file", utxoStakingVkey2 + , "--testnet-magic", show @Int testnetMagic + ] + + utxostakingaddr <- filter (/= '\n') + <$> H.execCli + [ "stake-address", "build" + , "--stake-verification-key-file", utxoStakingVkey2 + , "--testnet-magic", show @Int testnetMagic + ] + + -- Plutus related + plutusStakingScript <- H.note $ base "scripts/plutus/scripts/guess-42-stake.plutus" + plutusStakingScriptRedeemer <- H.note $ base "scripts/plutus/data/42.redeemer" + scriptPaymentAddressWithStaking <- H.execCli [ "address", "build" + , "--payment-verification-key-file", utxoVKeyFile + , "--stake-script-file", plutusStakingScript + , "--testnet-magic", show @Int testnetMagic + ] + plutusStakingAddr <- filter (/= '\n') <$> + H.execCli [ "stake-address", "build" + , "--testnet-magic", show @Int testnetMagic + , "--stake-script-file", plutusStakingScript + ] + -- Stake pool related + poolownerstakekey <- H.note $ tempAbsPath "addresses/pool-owner1-stake.vkey" + poolownerverkey <- H.note $ tempAbsPath "addresses/pool-owner1.vkey" + poolownerstakeaddr <- filter (/= '\n') + <$> H.execCli + [ "stake-address", "build" + , "--stake-verification-key-file", poolownerstakekey + , "--testnet-magic", show @Int testnetMagic + ] + + poolowneraddresswstakecred <- H.execCli [ "address", "build" + , "--payment-verification-key-file", poolownerverkey + , "--stake-verification-key-file", poolownerstakekey + , "--testnet-magic", show @Int testnetMagic + ] + poolcoldVkey <- H.note $ tempAbsPath "node-pool1/shelley/operator.vkey" + poolcoldSkey <- H.note $ tempAbsPath "node-pool1/shelley/operator.skey" + + stakePoolId <- filter ( /= '\n') <$> + H.execCli [ "stake-pool", "id" + , "--cold-verification-key-file", poolcoldVkey + ] + + -- REGISTER PLEDGER POOL + + -- Create pledger registration certificate + void $ H.execCli + [ "stake-address", "registration-certificate" + , "--stake-verification-key-file", poolownerstakekey + , "--out-file", work "pledger.regcert" + ] + + void $ H.execCli' execConfig + [ "transaction", "build" + , "--alonzo-era" + , "--testnet-magic", show @Int testnetMagic + , "--change-address", utxoAddr + , "--tx-in", T.unpack $ renderTxIn txin + , "--tx-out", scriptPaymentAddressWithStaking <> "+" <> show @Int 5000000 + , "--tx-out", poolowneraddresswstakecred <> "+" <> show @Int 5000000 + , "--tx-out", utxoaddrwithstaking <> "+" <> show @Int 5000000 + , "--witness-override", show @Int 3 + , "--certificate-file", work "pledger.regcert" + , "--out-file", work "pledge-registration-cert.txbody" + ] + + void $ H.execCli + [ "transaction", "sign" + , "--tx-body-file", work "pledge-registration-cert.txbody" + , "--testnet-magic", show @Int testnetMagic + , "--signing-key-file", utxoSKeyFile + , "--out-file", work "pledge-registration-cert.tx" + ] + + H.note_ "Submitting pool owner/pledge stake registration cert and funding stake pool owner address..." + + void $ H.execCli' execConfig + [ "transaction", "submit" + , "--tx-file", work "pledge-registration-cert.tx" + , "--testnet-magic", show @Int testnetMagic + ] + + -- Things take long on non-linux machines + if isLinux + then H.threadDelay 5000000 + else H.threadDelay 10000000 + + -- Check to see if pledge's stake address was registered + + void $ H.execCli' execConfig + [ "query", "stake-address-info" + , "--address", poolownerstakeaddr + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "pledgeownerregistration.json" + ] + + pledgerStakeInfo <- H.leftFailM . H.readJsonFile $ work "pledgeownerregistration.json" + delegsAndRewardsMap <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @DelegationsAndRewards pledgerStakeInfo + let delegsAndRewards = mergeDelegsAndRewards delegsAndRewardsMap + + length delegsAndRewards === 1 + + let (pledgerSAddr, _rewards, _poolId) = head delegsAndRewards + + -- Pledger and owner are and can be the same + T.unpack (serialiseAddress pledgerSAddr) === poolownerstakeaddr + + H.note_ $ "Register staking key: " <> show utxoStakingVkey2 + + void $ H.execCli' execConfig + [ "query", "utxo" + , "--address", utxoaddrwithstaking + , "--cardano-mode" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "utxo-addr-with-staking-1.json" + ] + + H.cat $ work "utxo-addr-with-staking-1.json" + + utxoWithStaking1Json <- H.leftFailM . H.readJsonFile $ work "utxo-addr-with-staking-1.json" + UTxO utxoWithStaking1 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(UTxO AlonzoEra) utxoWithStaking1Json + txinForStakeReg <- H.noteShow $ head $ Map.keys utxoWithStaking1 + + void $ H.execCli [ "stake-address", "registration-certificate" + , "--stake-verification-key-file", utxoStakingVkey2 + , "--out-file", work "stakekey.regcert" + ] + + void $ H.execCli' execConfig + [ "transaction", "build" + , "--alonzo-era" + , "--testnet-magic", show @Int testnetMagic + , "--change-address", utxoaddrwithstaking + , "--tx-in", T.unpack (renderTxIn txinForStakeReg) + , "--tx-out", utxoaddrwithstaking <> "+" <> show @Int 1000000 + , "--witness-override", show @Int 3 + , "--certificate-file", work "stakekey.regcert" + , "--out-file", work "key-registration-cert.txbody" + ] + + void $ H.execCli [ "transaction", "sign" + , "--tx-body-file", work "key-registration-cert.txbody" + , "--testnet-magic", show @Int testnetMagic + , "--signing-key-file", utxoStakingSkey2 + , "--signing-key-file", utxoSKeyFile2 + , "--out-file", work "key-registration-cert.tx" + ] + + + void $ H.execCli' execConfig + [ "transaction", "submit" + , "--tx-file", work "key-registration-cert.tx" + , "--testnet-magic", show @Int testnetMagic + ] + + H.note_ $ "Check to see if " <> utxoStakingVkey2 <> " was registered..." + H.threadDelay 10000000 + + void $ H.execCli' execConfig + [ "query", "stake-address-info" + , "--address", utxostakingaddr + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "stake-address-info-utxo-staking-vkey-2.json" + ] + + userStakeAddrInfoJSON <- H.leftFailM . H.readJsonFile $ work "stake-address-info-utxo-staking-vkey-2.json" + delegsAndRewardsMapUser <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @DelegationsAndRewards userStakeAddrInfoJSON + let delegsAndRewardsUser = mergeDelegsAndRewards delegsAndRewardsMapUser + userStakeAddrInfo = filter (\(sAddr,_,_) -> utxostakingaddr == T.unpack (serialiseAddress sAddr)) delegsAndRewardsUser + (userSAddr, _rewards, _poolId) = head userStakeAddrInfo + + + H.note_ $ "Check staking key: " <> show utxoStakingVkey2 <> " was registered" + T.unpack (serialiseAddress userSAddr) === utxostakingaddr + + H.note_ "Get updated UTxO" + + void $ H.execCli' execConfig + [ "query", "utxo" + , "--address", utxoAddr + , "--cardano-mode" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "utxo-2.json" + ] + + H.cat $ work "utxo-2.json" + + utxo2Json <- H.leftFailM . H.readJsonFile $ work "utxo-2.json" + UTxO utxo2 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(UTxO AlonzoEra) utxo2Json + txin2 <- H.noteShow $ head $ Map.keys utxo2 + + H.note_ "Create delegation certificate of pledger" + + void $ H.execCli + [ "stake-address", "delegation-certificate" + , "--stake-verification-key-file", poolownerstakekey + , "--cold-verification-key-file", poolcoldVkey + , "--out-file", work "pledger.delegcert" + ] + + H.note_ "Register stake pool and delegate pledger to stake pool in a single tx" + + void $ H.execCli' execConfig + [ "transaction", "build" + , "--alonzo-era" + , "--testnet-magic", show @Int testnetMagic + , "--change-address", utxoAddr + , "--tx-in", T.unpack $ renderTxIn txin2 + , "--tx-out", scriptPaymentAddressWithStaking <> "+" <> show @Int 5000000 + , "--tx-out", utxoAddr <> "+" <> show @Int 10000000 + , "--witness-override", show @Int 3 + , "--certificate-file", tempAbsPath "node-pool1/registration.cert" + , "--certificate-file", work "pledger.delegcert" + , "--out-file", work "register-stake-pool.txbody" + ] + + void $ H.execCli + [ "transaction", "sign" + , "--tx-body-file", work "register-stake-pool.txbody" + , "--testnet-magic", show @Int testnetMagic + , "--signing-key-file", utxoSKeyFile + , "--signing-key-file", poolcoldSkey + , "--signing-key-file", tempAbsPath "node-pool1/owner.skey" + , "--out-file", work "register-stake-pool.tx" + ] + + void $ H.execCli' execConfig + [ "transaction", "submit" + , "--tx-file", work "register-stake-pool.tx" + , "--testnet-magic", show @Int testnetMagic + ] + + if isLinux + then H.threadDelay 5000000 + else H.threadDelay 20000000 + + void $ H.execCli' execConfig + [ "query", "stake-pools" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "current-registered.pools.json" + ] + + currRegPools <- H.leftFailM . H.readJsonFile $ work "current-registered.pools.json" + poolIds <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(Set PoolId) currRegPools + poolId <- H.noteShow $ head $ Set.toList poolIds + + H.note_ "Check stake pool was successfully registered" + T.unpack (serialiseToBech32 poolId) === stakePoolId + + H.note_ "Check pledge was successfully delegated" + void $ H.execCli' execConfig + [ "query", "stake-address-info" + , "--address", poolownerstakeaddr + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "pledge-stake-address-info.json" + ] + + pledgeStakeAddrInfoJSON <- H.leftFailM . H.readJsonFile $ work "pledge-stake-address-info.json" + delegsAndRewardsMapPledge <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @DelegationsAndRewards pledgeStakeAddrInfoJSON + let delegsAndRewardsPledge = mergeDelegsAndRewards delegsAndRewardsMapPledge + pledgeStakeAddrInfo = filter (\(sAddr,_,_) -> poolownerstakeaddr == T.unpack (serialiseAddress sAddr)) delegsAndRewardsPledge + (pledgeSAddr, _rewards, pledgerDelegPoolId) = head pledgeStakeAddrInfo + + H.note_ "Check pledge has been delegated to pool" + case pledgerDelegPoolId of + Nothing -> H.failMessage callStack "Pledge was not delegated to pool" + Just pledgerDelagator -> T.unpack (serialiseToBech32 pledgerDelagator) === stakePoolId + T.unpack (serialiseAddress pledgeSAddr) === poolownerstakeaddr + + H.note_ "We have a fully functioning stake pool at this point. We now want to test Plutus staking script withdrawals." + + H.note_ "We now create the Plutus script staking registration certificate" + + H.note_ "Get updated UTxO" + + void $ H.execCli' execConfig + [ "query", "utxo" + , "--address", utxoAddr + , "--cardano-mode" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "utxo-3.json" + ] + + H.cat $ work "utxo-3.json" + + utxo3Json <- H.leftFailM . H.readJsonFile $ work "utxo-3.json" + UTxO utxo3 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(UTxO AlonzoEra) utxo3Json + txin3 <- H.noteShow . head $ Map.keys utxo3 + + void $ H.execCli + [ "stake-address", "registration-certificate" + , "--stake-script-file", plutusStakingScript + , "--out-file", work "script.regcert" + ] + + void $ H.execCli' execConfig + [ "transaction", "build" + , "--alonzo-era" + , "--testnet-magic", show @Int testnetMagic + , "--change-address", utxoAddr + , "--tx-in", T.unpack $ renderTxIn txin3 + , "--tx-out", scriptPaymentAddressWithStaking <> "+" <> show @Int 5000000 + , "--witness-override", show @Int 3 + , "--certificate-file", work "script.regcert" + , "--out-file", work "register-plutus-staking-script.txbody" + ] + + void $ H.execCli + [ "transaction", "sign" + , "--tx-body-file", work "register-plutus-staking-script.txbody" + , "--testnet-magic", show @Int testnetMagic + , "--signing-key-file", utxoSKeyFile + , "--out-file", work "register-plutus-staking-script.tx" + ] + + void $ H.execCli' execConfig + [ "transaction", "submit" + , "--tx-file", work "register-plutus-staking-script.tx" + , "--testnet-magic", show @Int testnetMagic + ] + + H.threadDelay 10000000 + + H.note_ "Check if Plutus staking script address was registered" + + void $ H.execCli' execConfig + [ "query", "stake-address-info" + , "--address", plutusStakingAddr + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "pledge-stake-address-info.json" + ] + + plutusStakeAddrInfoJSON <- H.leftFailM . H.readJsonFile $ work "pledge-stake-address-info.json" + delegsAndRewardsMapPlutus <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @DelegationsAndRewards plutusStakeAddrInfoJSON + let delegsAndRewardsPlutus = mergeDelegsAndRewards delegsAndRewardsMapPlutus + plutusStakeAddrInfo = filter (\(sAddr,_,_) -> plutusStakingAddr == T.unpack (serialiseAddress sAddr)) delegsAndRewardsPlutus + (plutusSAddr, _rewards, _poolId) = head plutusStakeAddrInfo + + H.note_ "Check if Plutus staking script has been registered" + T.unpack (serialiseAddress plutusSAddr) === plutusStakingAddr + + H.note_ "Create delegation certificate for Plutus staking script to stake pool" + + void $ H.execCli + [ "stake-address", "delegation-certificate" + , "--stake-script-file", plutusStakingScript + , "--cold-verification-key-file", poolcoldVkey + , "--out-file", work "plutus-script.delegcert" + ] + + H.note_ "Get updated UTxO" + + void $ H.execCli' execConfig + [ "query", "utxo" + , "--address", utxoAddr + , "--cardano-mode" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "utxo-4.json" + ] + + H.cat $ work "utxo-4.json" + + utxo4Json <- H.leftFailM . H.readJsonFile $ work "utxo-4.json" + UTxO utxo4 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(UTxO AlonzoEra) utxo4Json + txin4 <- H.noteShow . head $ Map.keys utxo4 + txinCollateral1 <- H.noteShow $ Map.keys utxo4 !! 1 + + H.note_ "Delegate Plutus staking script to stake pool" + + void $ H.execCli' execConfig + [ "query", "protocol-parameters" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "pparams.json" + ] + + void $ H.execCli' execConfig + [ "transaction", "build" + , "--alonzo-era" + , "--testnet-magic", show @Int testnetMagic + , "--change-address", utxoAddr + , "--tx-in", T.unpack $ renderTxIn txin4 + , "--tx-in-collateral", T.unpack $ renderTxIn txinCollateral1 + , "--tx-out", scriptPaymentAddressWithStaking <> "+" <> show @Int 5000000 + , "--witness-override", show @Int 3 + , "--certificate-file", work "plutus-script.delegcert" + , "--certificate-script-file", plutusStakingScript + , "--certificate-redeemer-file", plutusStakingScriptRedeemer + , "--protocol-params-file", work "pparams.json" + , "--out-file", work "delegate-staking-script.txbody" + ] + + void $ H.execCli + [ "transaction", "sign" + , "--tx-body-file", work "delegate-staking-script.txbody" + , "--testnet-magic", show @Int testnetMagic + , "--signing-key-file", utxoSKeyFile + , "--out-file", work "delegate-staking-script.tx" + ] + + void $ H.execCli' execConfig + [ "transaction", "submit" + , "--tx-file", work "delegate-staking-script.tx" + , "--testnet-magic", show @Int testnetMagic + ] + + -- Wait 5 seconds + H.threadDelay 5000000 + + H.note_ "Check to see if staking script was delegated" + + void $ H.execCli' execConfig + [ "query", "stake-address-info" + , "--address", plutusStakingAddr + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "plutus-staking-script-delegation.json" + ] + + stakingScriptAddrInfoJSON <- H.leftFailM . H.readJsonFile $ work "plutus-staking-script-delegation.json" + delegsAndRewardsMapStakingScript <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @DelegationsAndRewards stakingScriptAddrInfoJSON + let delegsAndRewardsStakingScript = mergeDelegsAndRewards delegsAndRewardsMapStakingScript + stakingScriptAddrInfo = filter (\(sAddr,_,_) -> plutusStakingAddr == T.unpack (serialiseAddress sAddr)) delegsAndRewardsStakingScript + (_stakingSAddr, _rewards, poolIdPlutusDeleg) = head stakingScriptAddrInfo + + H.note_ $ "Check plutus staking script: " <> (work "plutus-staking-script-delegation.json") <> " was delegated" + case poolIdPlutusDeleg of + Nothing -> H.failMessage callStack "Plutus script was not delegated to stake pool" + Just plutusDelegPoolId -> + T.unpack (serialiseToBech32 plutusDelegPoolId) === stakePoolId + + + H.note_ "Checking plutus staking script has ada at its corresponding payment address" + + void $ H.execCli' execConfig + [ "query", "utxo" + , "--address", scriptPaymentAddressWithStaking + , "--cardano-mode" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "utxo-plutus-staking-payment-address.json" + ] + + H.cat $ work "utxo-plutus-staking-payment-address.json" + + utxoPlutusPaymentAddrJson <- H.leftFailM . H.readJsonFile $ work "utxo-plutus-staking-payment-address.json" + UTxO utxoPlutus <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(UTxO AlonzoEra) utxoPlutusPaymentAddrJson + + utxoPlutus /== mempty + + H.note_ "Wait for rewards to be paid out. This will be current epoch + 4" + + void $ H.execCli' execConfig + [ "query", "tip" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "current-tip.json" + ] + + tipJSON <- H.leftFailM . H.readJsonFile $ work "current-tip.json" + tip <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @QueryTipLocalStateOutput tipJSON + currEpoch <- + case mEpoch tip of + Nothing -> + H.failMessage callStack "cardano-cli query tip returned Nothing for EpochNo" + Just currEpoch -> return currEpoch + + let rewardsEpoch = currEpoch + 4 + waitedEpoch <- waitUntilEpoch + (work "current-tip.json") + testnetMagic + execConfig + rewardsEpoch + + H.note_ "Check we have reached 4 epochs ahead" + waitedEpoch === rewardsEpoch + + + void $ H.execCli' execConfig + [ "query", "tip" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "current-tip-2.json" + ] + + tip2JSON <- H.leftFailM . H.readJsonFile $ work "current-tip-2.json" + tip2 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @QueryTipLocalStateOutput tip2JSON + + currEpoch2 <- + case mEpoch tip2 of + Nothing -> + H.failMessage callStack "cardano-cli query tip returned Nothing for EpochNo" + Just currEpoch2 -> return currEpoch2 + + H.note_ $ "Current Epoch: " <> show currEpoch2 + + H.note_ "Check rewards have been distributed to Plutus script staking address" + + void$ H.execCli' execConfig + [ "query", "ledger-state" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "ledger-state.json" + ] + + void $ H.execCli' execConfig + [ "query", "stake-address-info" + , "--address", plutusStakingAddr + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "plutus-staking-script-delegation-rewards.json" + ] + + stakingRewardsJSON <- H.leftFailM . H.readJsonFile $ work "plutus-staking-script-delegation-rewards.json" + delegsAndRewardsMapScriptRewards <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @DelegationsAndRewards stakingRewardsJSON + let delegsAndRewardsScriptRewards = mergeDelegsAndRewards delegsAndRewardsMapScriptRewards + stakingScriptRewardsAddrInfo = filter (\(sAddr,_,_) -> plutusStakingAddr == T.unpack (serialiseAddress sAddr)) delegsAndRewardsScriptRewards + (_, scriptRewards, _) = head stakingScriptRewardsAddrInfo + + pr@(Lovelace plutusRewards) <- + case scriptRewards of + Nothing -> H.failMessage callStack "Plutus staking script has no rewards" + Just rwds -> H.assert (rwds > 0) >> return rwds + + H.note_ $ "We now withdraw the rewards from our Plutus staking address: " <> show @Integer plutusRewards + + H.note_ "Get updated UTxO" + + void $ H.execCli' execConfig + [ "query", "utxo" + , "--address", utxoAddr + , "--cardano-mode" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "utxo-5.json" + ] + + H.cat $ work "utxo-5.json" + + utxo5Json <- H.leftFailM . H.readJsonFile $ work "utxo-5.json" + UTxO utxo5 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(UTxO AlonzoEra) utxo5Json + txin5 <- H.noteShow . head $ Map.keys utxo5 + txinCollateral2 <- H.noteShow $ Map.keys utxo5 !! 1 + + let minrequtxo = 999978 + void $ H.execCli' execConfig + [ "transaction", "build" + , "--alonzo-era" + , "--testnet-magic", show @Int testnetMagic + , "--change-address", utxoAddr + , "--tx-in", T.unpack $ renderTxIn txin5 + , "--tx-in-collateral", T.unpack $ renderTxIn txinCollateral2 + , "--tx-out", scriptPaymentAddressWithStaking <> "+" <> show @Integer (plutusRewards + minrequtxo) + , "--withdrawal", plutusStakingAddr <> "+" <> show @Integer plutusRewards + , "--withdrawal-script-file", plutusStakingScript + , "--withdrawal-redeemer-file", plutusStakingScriptRedeemer + , "--protocol-params-file", work "pparams.json" + , "--out-file", work "staking-script-withdrawal.txbody" + ] + + void $ H.execCli + [ "transaction", "sign" + , "--tx-body-file", work "staking-script-withdrawal.txbody" + , "--testnet-magic", show @Int testnetMagic + , "--signing-key-file", utxoSKeyFile + , "--out-file", work "staking-script-withdrawal.tx" + ] + + void $ H.execCli' execConfig + [ "transaction", "submit" + , "--tx-file", work "staking-script-withdrawal.tx" + , "--testnet-magic", show @Int testnetMagic + ] + + -- Things take long on non-linux machines + if isLinux + then H.threadDelay 5000000 + else H.threadDelay 10000000 + + H.note_ "Check UTxO at script staking address to see if withdrawal was successful" + + void $ H.execCli' execConfig + [ "query", "utxo" + , "--address", scriptPaymentAddressWithStaking + , "--cardano-mode" + , "--testnet-magic", show @Int testnetMagic + , "--out-file", work "utxo-plutus-staking-payment-address-2.json" + ] + + H.cat $ work "utxo-plutus-staking-payment-address-2.json" + + utxoPlutusPaymentAddrJson2 <- H.leftFailM . H.readJsonFile $ work "utxo-plutus-staking-payment-address-2.json" + UTxO utxoPlutus2 <- H.noteShowM $ H.jsonErrorFail $ J.fromJSON @(UTxO AlonzoEra) utxoPlutusPaymentAddrJson2 + -- Get total lovelace at plutus script address + + let lovelaceAtPlutusAddr = mconcat . map (\(TxOut _ v _) -> txOutValueToLovelace v) $ Map.elems utxoPlutus2 + + H.note_ "Check that the withdrawal from the Plutus staking address was successful" + lovelaceAtPlutusAddr === pr + 5000000 + 5000000 + 5000000 + 5000000 + Lovelace minrequtxo diff --git a/cardano-testnet/test/Spec/Plutus/Direct/TxInLockingPlutus.hs b/cardano-testnet/test/Spec/Plutus/Direct/TxInLockingPlutus.hs index 5bfde15d5e6..1f3449ab39e 100644 --- a/cardano-testnet/test/Spec/Plutus/Direct/TxInLockingPlutus.hs +++ b/cardano-testnet/test/Spec/Plutus/Direct/TxInLockingPlutus.hs @@ -243,4 +243,4 @@ hprop_plutus = H.integration . H.runFinallies . H.workspace "chairman" $ \tempAb , "--testnet-magic", show @Int testnetMagic ] - L.filter (not . T.null) (T.splitOn " " (T.lines result !! 2)) !! 2 === "100000000" + L.filter (not . T.null) (T.splitOn " " (T.lines result !! 2)) !! 2 === "100000000000" diff --git a/cardano-testnet/test/Spec/Plutus/SubmitApi/TxInLockingPlutus.hs b/cardano-testnet/test/Spec/Plutus/SubmitApi/TxInLockingPlutus.hs index 7f75b724df5..615bf1015d2 100644 --- a/cardano-testnet/test/Spec/Plutus/SubmitApi/TxInLockingPlutus.hs +++ b/cardano-testnet/test/Spec/Plutus/SubmitApi/TxInLockingPlutus.hs @@ -254,4 +254,4 @@ hprop_plutus = Test.integration . HE.runFinallies . HE.workspace "chairman" $ \t HE.note_ $ Text.unpack result - List.filter (not . Text.null) (Text.splitOn " " (Text.lines result !! 2)) !! 2 === "160000000" + List.filter (not . Text.null) (Text.splitOn " " (Text.lines result !! 2)) !! 2 === "299860000000" diff --git a/scripts/plutus/staking-example/deregister-stake-address.sh b/scripts/plutus/staking-example/deregister-stake-address.sh new file mode 100755 index 00000000000..40aa15c9993 --- /dev/null +++ b/scripts/plutus/staking-example/deregister-stake-address.sh @@ -0,0 +1,87 @@ +#!/usr/bin/env bash + +set -e +# Unoffiical bash strict mode. +# See: http://redsymbol.net/articles/unofficial-bash-strict-mode/ +set -u +set -o pipefail + + +export BASE="${BASE:-.}" +export WORK="${WORK:-example/work}" +export CARDANO_NODE_SOCKET_PATH="${CARDANO_NODE_SOCKET_PATH:-example/node-bft1/node.sock}" +export TESTNET_MAGIC="${TESTNET_MAGIC:-42}" +export UTXO_VKEY1="${UTXO_VKEY1:-example/shelley/utxo-keys/utxo1.vkey}" +export UTXO_SKEY1="${UTXO_SKEY1:-example/shelley/utxo-keys/utxo1.skey}" + +utxoaddr=$(cardano-cli address build --testnet-magic "$TESTNET_MAGIC" --payment-verification-key-file "$UTXO_VKEY1") +scriptpaymentaddrwithstakecred=$(cardano-cli address build --payment-verification-key-file "$UTXO_VKEY1" --stake-script-file "scripts/plutus/scripts/guess-42-stake.plutus" --testnet-magic 42) +stakingscriptaddr=$(cardano-cli stake-address build --stake-script-file scripts/plutus/scripts/guess-42-stake.plutus --testnet-magic 42) + +# DEREGISTRATION + +# Update UTxO again +echo "" +echo "Script staking address deregistration" +echo "" +cardano-cli query utxo \ + --address "$utxoaddr" \ + --cardano-mode \ + --testnet-magic "$TESTNET_MAGIC" \ + --out-file "$WORK/utxo-2.json" + +cat "$WORK/utxo-2.json" + +txinupdated3=$(jq -r 'keys[0]' "$WORK/utxo-2.json") +txincollateral=$(jq -r 'keys[1]' "$WORK/utxo-2.json") +echo "" +echo "Selected txin: $txinupdated3" + +# Create deregistration certificate +cardano-cli stake-address deregistration-certificate \ + --stake-script-file "scripts/plutus/scripts/guess-42-stake.plutus" \ + --out-file "$WORK/script.deregcert" + + + +# Get PParams + +cardano-cli query protocol-parameters --testnet-magic "$TESTNET_MAGIC" --out-file "$WORK/pparams.json" + +cardano-cli transaction build \ + --alonzo-era \ + --testnet-magic "$TESTNET_MAGIC" \ + --change-address "$utxoaddr" \ + --tx-in "$txinupdated3" \ + --tx-in-collateral "$txincollateral" \ + --tx-out "$scriptpaymentaddrwithstakecred+500" \ + --witness-override 3 \ + --certificate-file "$WORK/script.deregcert" \ + --certificate-script-file "scripts/plutus/scripts/guess-42-stake.plutus" \ + --certificate-redeemer-file "scripts/plutus/data/42.redeemer" \ + --protocol-params-file "$WORK/pparams.json" \ + --out-file "$WORK/script-deregistration-cert.txbody" + +cardano-cli transaction sign \ + --tx-body-file "$WORK/script-deregistration-cert.txbody" \ + --testnet-magic "$TESTNET_MAGIC" \ + --signing-key-file "$UTXO_SKEY1" \ + --out-file "$WORK/script-deregistration-cert.tx" + +cardano-cli transaction submit \ + --tx-file "$WORK/script-deregistration-cert.tx" \ + --testnet-magic "$TESTNET_MAGIC" + +echo "Staking script adress" +echo "$stakingscriptaddr" +echo "Waiting 5 seconds..." +sleep 5 +echo "Check to see if the script staking address was successfully deregistered" + +cardano-cli query stake-address-info \ + --address "$stakingscriptaddr" \ + --testnet-magic 42 \ + --out-file "$WORK/scriptderegistration.json" + +deregistered=$(jq -r '.[0]' "$WORK/scriptderegistration.json") +echo "$deregistered" \ No newline at end of file diff --git a/scripts/plutus/staking-example/register-stake-pool.sh b/scripts/plutus/staking-example/register-stake-pool.sh index 6a5f90d007f..869e2be8af7 100755 --- a/scripts/plutus/staking-example/register-stake-pool.sh +++ b/scripts/plutus/staking-example/register-stake-pool.sh @@ -20,7 +20,6 @@ export UTXO_STAKING_SKEY1="${UTXO_STAKING_SKEY1:=example/shelley/utxo-keys/utxo- export UTXO_STAKING_VKEY2="${UTXO_STAKING_VKEY2:=example/shelley/utxo-keys/utxo2-stake.vkey}" export UTXO_STAKING_SKEY2="${UTXO_STAKING_SKEY2:=example/shelley/utxo-keys/utxo2-stake.skey}" -# TODO: Left off here. You should try a staking key address to see if you are the problem! mkdir -p "$WORK" utxoaddr=$(cardano-cli address build --testnet-magic "$TESTNET_MAGIC" --payment-verification-key-file "$UTXO_VKEY1") @@ -39,11 +38,10 @@ echo "UTxO" cat "$WORK/utxo-1.json" echo "" -txin=$(jq -r 'keys[]' $WORK/utxo-1.json) -lovelaceattxin=$(jq -r ".[\"$txin\"].value.lovelace" $WORK/utxo-1.json) -lovelaceattxindiv3=$(expr $lovelaceattxin / 3) -scriptpaymentaddrwithstakecred=$(cardano-cli address build --payment-verification-key-file $UTXO_VKEY1 --stake-script-file "scripts/plutus/scripts/guess-42-stake.plutus" --testnet-magic 42) -#TODO: Look at stake-distbution cmd +txin=$(jq -r 'keys[]' "$WORK/utxo-1.json") +lovelaceattxin=$(jq -r ".[\"$txin\"].value.lovelace" "$WORK/utxo-1.json") +lovelaceattxindiv3=$((lovelaceattxin / 3)) +scriptpaymentaddrwithstakecred=$(cardano-cli address build --payment-verification-key-file "$UTXO_VKEY1" --stake-script-file "scripts/plutus/scripts/guess-42-stake.plutus" --testnet-magic 42) poolownerstakekey="example/addresses/pool-owner1-stake.vkey" poolowneraddresswstakecred=$(cardano-cli address build --payment-verification-key-file example/addresses/pool-owner1.vkey --stake-verification-key-file example/addresses/pool-owner1-stake.vkey --testnet-magic 42) poolcoldkey="example/node-pool1/shelley/operator.vkey" @@ -98,7 +96,7 @@ cardano-cli query stake-address-info \ --address "$poolownerstakeaddr" \ --testnet-magic 42 \ --out-file "$WORK/pledgeownerregistration.json" -registered=$(jq -r '.[0]' $WORK/pledgeownerregistration.json) +registered=$(jq -r '.[0]' "$WORK/pledgeownerregistration.json") echo "" echo "Registered pool owner/pledger address. If null it was not successfully registered" @@ -121,7 +119,7 @@ echo "Staking key UTxO" cat "$WORK/staking-key-utxo-1.json" echo "" -keytxin=$(jq -r 'keys[]' $WORK/staking-key-utxo-1.json) +keytxin=$(jq -r 'keys[]' "$WORK/staking-key-utxo-1.json") cardano-cli stake-address registration-certificate \ --stake-verification-key-file "$UTXO_STAKING_VKEY2" \ @@ -158,7 +156,7 @@ cardano-cli query stake-address-info \ --testnet-magic 42 \ --out-file "$WORK/keyregistration.json" -registeredkey=$(jq -r '.[0]' $WORK/keyregistration.json) +registeredkey=$(jq -r '.[0]' "$WORK/keyregistration.json") echo "" echo "Registered key staking address. If null it was not successfully registered" echo "$registeredkey" @@ -174,7 +172,7 @@ cardano-cli query utxo \ cat "$WORK/utxo-1.json" -txinupdated=$(jq -r 'keys[0]' $WORK/utxo-1.json) +txinupdated=$(jq -r 'keys[0]' "$WORK/utxo-1.json") # STEP 2 @@ -228,7 +226,7 @@ cardano-cli query stake-address-info \ --address "$poolownerstakeaddr" \ --testnet-magic 42 \ --out-file "$WORK/pledgeownerregistration.json" -delegated=$(jq -r '.[0]' $WORK/pledgeownerregistration.json) +delegated=$(jq -r '.[0]' "$WORK/pledgeownerregistration.json") echo "" echo "Currently registered stake pools" @@ -257,7 +255,7 @@ echo "Staking key UTxO" cat "$WORK/staking-key-utxo-2.json" echo "" -keytxin2=$(jq -r 'keys[0]' $WORK/staking-key-utxo-2.json) +keytxin2=$(jq -r 'keys[0]' "$WORK/staking-key-utxo-2.json") cardano-cli transaction build \ --alonzo-era \ @@ -291,7 +289,7 @@ cardano-cli query stake-address-info \ --testnet-magic 42 \ --out-file "$WORK/keydelegation.json" -delegatedkey=$(jq -r '.[0]' $WORK/keydelegation.json) +delegatedkey=$(jq -r '.[0]' "$WORK/keydelegation.json") echo "" echo "Delegating key staking address. If null it was not successfully registered" echo "$delegatedkey" @@ -311,16 +309,16 @@ cardano-cli query utxo \ cat "$WORK/utxo-2.json" -txinupdated2=$(jq -r 'keys[0]' $WORK/utxo-2.json) +txinupdated2=$(jq -r 'keys[0]' "$WORK/utxo-2.json") echo "" echo "Selected txin: $txinupdated2" # Step 1: Create registration certificate for the staking script # We also create collateral. -txin=$(jq -r 'keys[]' $WORK/utxo-2.json) -lovelaceattxin=$(jq -r ".[\"$txin\"].value.lovelace" $WORK/utxo-2.json) -lovelaceattxindiv3=$(expr $lovelaceattxin / 3) +txin=$(jq -r 'keys[]' "$WORK/utxo-2.json") +lovelaceattxin=$(jq -r ".[\"$txin\"].value.lovelace" "$WORK/utxo-2.json") +lovelaceattxindiv3=$((lovelaceattxin / 3)) cardano-cli stake-address registration-certificate \ --stake-script-file "scripts/plutus/scripts/guess-42-stake.plutus" \ @@ -361,7 +359,7 @@ cardano-cli query stake-address-info \ --testnet-magic 42 \ --out-file "$WORK/scriptregistration.json" -registeredscr=$(jq -r '.[0]' $WORK/scriptregistration.json) +registeredscr=$(jq -r '.[0]' "$WORK/scriptregistration.json") echo "$registeredscr" # We have successfully registered our script staking address. @@ -373,7 +371,7 @@ cardano-cli stake-address delegation-certificate \ --cold-verification-key-file "$poolcoldkey" \ --out-file "$WORK/script.delegcert" -cardano-cli query protocol-parameters --testnet-magic "$TESTNET_MAGIC" --out-file $WORK/pparams.json +cardano-cli query protocol-parameters --testnet-magic "$TESTNET_MAGIC" --out-file "$WORK/pparams.json" # We also need collateral @@ -385,8 +383,8 @@ cardano-cli query utxo \ cat "$WORK/utxo-2.json" -txinupdated3=$(jq -r 'keys[0]' $WORK/utxo-2.json) -txincollateral=$(jq -r 'keys[1]' $WORK/utxo-2.json) +txinupdated3=$(jq -r 'keys[0]' "$WORK/utxo-2.json") +txincollateral=$(jq -r 'keys[1]' "$WORK/utxo-2.json") echo "" echo "Selected txin: $txinupdated2" @@ -426,82 +424,8 @@ cardano-cli query stake-address-info \ --testnet-magic 42 \ --out-file "$WORK/scriptdelegation.json" -delegatedscript=$(jq -r '.[0]' $WORK/scriptdelegation.json) +delegatedscript=$(jq -r '.[0]' "$WORK/scriptdelegation.json") echo "$delegatedscript" echo "" echo "Stake payment address" echo "$scriptpaymentaddrwithstakecred" -# We have two scenarios to test, deregistration and rewards withdrawal -# SCENARIO 1: WITHDRAWAL - - -# SCENARIO 2: DEREGISTRATION -- THIS WORKS - -# Update UTxO again -#echo "" -#echo "Script staking address deregistration" -#echo "" -#cardano-cli query utxo \ -# --address "$utxoaddr" \ -# --cardano-mode \ -# --testnet-magic "$TESTNET_MAGIC" \ -# --out-file "$WORK/utxo-2.json" -# -#cat "$WORK/utxo-2.json" -# -#txinupdated3=$(jq -r 'keys[0]' $WORK/utxo-2.json) -#txincollateral=$(jq -r 'keys[1]' $WORK/utxo-2.json) -#echo "" -#echo "Selected txin: $txinupdated2" -# -## Create deregistration certificate -#cardano-cli stake-address deregistration-certificate \ -# --stake-script-file "scripts/plutus/scripts/guess-42-stake.plutus" \ -# --out-file "$WORK/script.deregcert" -# -# -## TODO: Then figure out why rewards aren't being dispersed. -# -## Get PParams -# -#cardano-cli query protocol-parameters --testnet-magic "$TESTNET_MAGIC" --out-file $WORK/pparams.json -# -#cardano-cli transaction build \ -# --alonzo-era \ -# --testnet-magic "$TESTNET_MAGIC" \ -# --change-address "$utxoaddr" \ -# --tx-in "$txinupdated3" \ -# --tx-in-collateral "$txincollateral" \ -# --tx-out "$scriptpaymentaddrwithstakecred+500" \ -# --witness-override 3 \ -# --certificate-file "$WORK/script.deregcert" \ -# --certificate-script-file "scripts/plutus/scripts/guess-42-stake.plutus" \ -# --certificate-redeemer-file "scripts/plutus/data/42.redeemer" \ -# --protocol-params-file "$WORK/pparams.json" \ -# --out-file "$WORK/script-deregistration-cert.txbody" -# -#cardano-cli transaction sign \ -# --tx-body-file "$WORK/script-deregistration-cert.txbody" \ -# --testnet-magic "$TESTNET_MAGIC" \ -# --signing-key-file "$UTXO_SKEY1" \ -# --out-file "$WORK/script-deregistration-cert.tx" -# -#cardano-cli transaction submit \ -# --tx-file "$WORK/script-deregistration-cert.tx" \ -# --testnet-magic "$TESTNET_MAGIC" -# -#echo "Staking script adress" -#echo "$stakingscriptaddr" -#echo "Waiting 5 seconds..." -#sleep 5 -#echo "Check to see if the script staking address was successfully deregistered" -# -#cardano-cli query stake-address-info \ -# --address "$stakingscriptaddr" \ -# --testnet-magic 42 \ -# --out-file "$WORK/scriptderegistration.json" -# -#deregistered=$(jq -r '.[0]' $WORK/scriptderegistration.json) -#echo "$deregistered" - -