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

Update build cmd to check tx inputs #3151

Merged
merged 2 commits into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions cardano-api/src/Cardano/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ module Cardano.Api (

-- ** Addresses in specific eras
AddressInEra(..),
isKeyAddress,
AddressTypeInEra(..),
byronAddressInEra,
shelleyAddressInEra,
Expand Down
19 changes: 17 additions & 2 deletions cardano-api/src/Cardano/Api/Address.hs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ module Cardano.Api.Address (

-- * Data family instances
AsType(AsByronAddr, AsShelleyAddr, AsByronAddress, AsShelleyAddress,
AsAddress, AsAddressAny, AsAddressInEra, AsStakeAddress)
AsAddress, AsAddressAny, AsAddressInEra, AsStakeAddress),

-- * Helpers
isKeyAddress
) where

import Prelude
Expand All @@ -74,8 +77,8 @@ import qualified Data.Text.Encoding as Text
import Control.Applicative

import Cardano.Api.Eras
import Cardano.Api.Hash
import Cardano.Api.HasTypeProxy
import Cardano.Api.Hash
import Cardano.Api.Key
import Cardano.Api.KeysByron
import Cardano.Api.KeysShelley
Expand Down Expand Up @@ -483,6 +486,18 @@ makeStakeAddress nw sc =
(toShelleyNetwork nw)
(toShelleyStakeCredential sc)

-- ----------------------------------------------------------------------------
-- Helpers
--

-- | Is the UTxO at the address only spendable via a key witness.
isKeyAddress :: AddressInEra era -> Bool
isKeyAddress (AddressInEra ByronAddressInAnyEra _) = True
isKeyAddress (AddressInEra (ShelleyAddressInEra _) (ShelleyAddress _ pCred _)) =
case fromShelleyPaymentCredential pCred of
PaymentCredentialByKey _ -> True
PaymentCredentialByScript _ -> False


-- ----------------------------------------------------------------------------
-- Internal conversion functions
Expand Down
5 changes: 3 additions & 2 deletions cardano-api/src/Cardano/Api/Query.hs
Original file line number Diff line number Diff line change
Expand Up @@ -75,14 +75,14 @@ import qualified Ouroboros.Consensus.HardFork.History.Qry as Qry

import Ouroboros.Consensus.BlockchainTime.WallClock.Types (RelativeTime, SlotLength)
import qualified Ouroboros.Consensus.Byron.Ledger as Consensus
import Ouroboros.Consensus.Cardano.Block (StandardCrypto, LedgerState(..))
import Ouroboros.Consensus.Cardano.Block (LedgerState (..), StandardCrypto)
import qualified Ouroboros.Consensus.Cardano.Block as Consensus
import qualified Ouroboros.Consensus.Ledger.Query as Consensus
import qualified Ouroboros.Consensus.Shelley.Ledger as Consensus
import Ouroboros.Network.Block (Serialised)

import Cardano.Binary
import Cardano.Slotting.Time (SystemStart(..))
import Cardano.Slotting.Time (SystemStart (..))

import qualified Cardano.Chain.Update.Validation.Interface as Byron.Update
import qualified Cardano.Ledger.Core as Core
Expand Down Expand Up @@ -684,3 +684,4 @@ fromConsensusQueryResultMismatch =
fromConsensusEraMismatch :: SListI xs
=> Consensus.MismatchEraInfo xs -> EraMismatch
fromConsensusEraMismatch = Consensus.mkEraMismatch

46 changes: 44 additions & 2 deletions cardano-cli/src/Cardano/CLI/Shelley/Run/Transaction.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Prelude (String, error)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as LBS
import Data.List (intersect, (\\))
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import qualified Data.Text as Text
Expand Down Expand Up @@ -112,6 +113,8 @@ data ShelleyTxCmdError
| ShelleyTxCmdEraConsensusModeMismatchQuery !AnyConsensusMode !AnyCardanoEra
| ShelleyTxCmdByronEraQuery
| ShelleyTxCmdLocalStateQueryError !ShelleyQueryCmdLocalStateQueryError
| ShelleyTxCmdExpectedKeyLockedTxIn ![TxIn]
| ShelleyTxCmdTxInsDoNotExist ![TxIn]

deriving Show

Expand Down Expand Up @@ -233,6 +236,14 @@ renderShelleyTxCmdError err =
ShelleyTxCmdByronEraQuery -> "Query not available in Byron era"
ShelleyTxCmdLocalStateQueryError err' -> renderLocalStateQueryError err'
ShelleyTxCmdBalanceTxBody err' -> Text.pack $ displayError err'
ShelleyTxCmdExpectedKeyLockedTxIn txins ->
"Expected key witnessed collateral tx inputs but got script witnessed tx inputs: " <>
Text.singleton '\n' <>
Text.intercalate (Text.singleton '\n') (map renderTxIn txins)
ShelleyTxCmdTxInsDoNotExist txins ->
"The following tx input(s) were not present in the UTxO: " <>
Jimbo4350 marked this conversation as resolved.
Show resolved Hide resolved
Text.singleton '\n' <>
Text.intercalate (Text.singleton '\n') (map renderTxIn txins)

renderEra :: AnyCardanoEra -> Text
renderEra (AnyCardanoEra ByronEra) = "Byron"
Expand Down Expand Up @@ -407,6 +418,7 @@ runTxBuild (AnyCardanoEra era) (AnyConsensusModeParams cModeParams) networkId mS
consensusMode = consensusModeOnly cModeParams
dummyFee = Just $ Lovelace 0
onlyInputs = [input | (input,_) <- txins]

case (consensusMode, cardanoEraStyle era) of
(CardanoMode, ShelleyBasedEra sbe) -> do
txBodyContent <-
Expand Down Expand Up @@ -441,10 +453,22 @@ runTxBuild (AnyCardanoEra era) (AnyConsensusModeParams cModeParams) networkId mS
left (ShelleyTxCmdEraConsensusModeMismatchTxBalance outBody
(AnyConsensusMode CardanoMode) (AnyCardanoEra era))

let utxoQuery = QueryInEra eInMode $ QueryInShelleyBasedEra sbe
let collateralUTxOQuery = QueryInEra eInMode $ QueryInShelleyBasedEra sbe
(QueryUTxO . QueryUTxOByTxIn $ Set.fromList txinsc)
utxoQuery = QueryInEra eInMode $ QueryInShelleyBasedEra sbe
(QueryUTxO . QueryUTxOByTxIn $ Set.fromList onlyInputs)
let pParamsQuery = QueryInEra eInMode $ QueryInShelleyBasedEra sbe QueryProtocolParameters
pParamsQuery = QueryInEra eInMode $ QueryInShelleyBasedEra sbe QueryProtocolParameters

if null txinsc
then return ()
else do
collateralUtxo <- executeQuery era cModeParams localConnInfo collateralUTxOQuery
txinsExist txinsc collateralUtxo
notScriptLockedTxIns collateralUtxo

utxo <- executeQuery era cModeParams localConnInfo utxoQuery
txinsExist onlyInputs utxo

pparams <- executeQuery era cModeParams localConnInfo pParamsQuery
(eraHistory, systemStart) <- firstExceptT ShelleyTxCmdAcquireFailure
$ newExceptT $ queryEraHistoryAndSystemStart localNodeConnInfo Nothing
Expand All @@ -468,6 +492,24 @@ runTxBuild (AnyCardanoEra era) (AnyConsensusModeParams cModeParams) networkId mS
(CardanoMode, LegacyByronEra) -> left ShelleyTxCmdByronEra

(wrongMode, _) -> left (ShelleyTxCmdUnsupportedMode (AnyConsensusMode wrongMode))
where
txinsExist :: [TxIn] -> UTxO era -> ExceptT ShelleyTxCmdError IO ()
txinsExist ins (UTxO utxo)
| null utxo = left $ ShelleyTxCmdTxInsDoNotExist ins
| otherwise = do
let utxoIns = Map.keys utxo
occursInUtxo = [ txin | txin <- ins, txin `elem` utxoIns ]
if length occursInUtxo == length ins
then return ()
else left . ShelleyTxCmdTxInsDoNotExist $ ins \\ ins `intersect` occursInUtxo

notScriptLockedTxIns :: UTxO era -> ExceptT ShelleyTxCmdError IO ()
notScriptLockedTxIns (UTxO utxo) = do
let scriptLockedTxIns =
filter (\(_, TxOut aInEra _ _) -> not $ isKeyAddress aInEra ) $ Map.assocs utxo
if null scriptLockedTxIns
then return ()
else left . ShelleyTxCmdExpectedKeyLockedTxIn $ map fst scriptLockedTxIns

queryEraHistoryAndSystemStart
:: LocalNodeConnectInfo CardanoMode
Expand Down