-
Notifications
You must be signed in to change notification settings - Fork 721
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
4446: Refactor transaction build command r=Jimbo4350 a=Jimbo4350 Co-authored-by: Jordan Millar <jordan.millar@iohk.io>
- Loading branch information
Showing
42 changed files
with
631 additions
and
377 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{-# LANGUAGE GADTs #-} | ||
{-# LANGUAGE RankNTypes #-} | ||
|
||
-- | Constraint satisfaction functions. These are used to avoid propagating constraints. | ||
-- | ||
module Cardano.Api.Convenience.Constraints ( | ||
getIsCardanoEraConstraint | ||
) where | ||
|
||
|
||
import Cardano.Api.Eras | ||
|
||
getIsCardanoEraConstraint :: CardanoEra era -> (IsCardanoEra era => a) -> a | ||
getIsCardanoEraConstraint ByronEra f = f | ||
getIsCardanoEraConstraint ShelleyEra f = f | ||
getIsCardanoEraConstraint AllegraEra f = f | ||
getIsCardanoEraConstraint MaryEra f = f | ||
getIsCardanoEraConstraint AlonzoEra f = f | ||
getIsCardanoEraConstraint BabbageEra f = f |
102 changes: 102 additions & 0 deletions
102
cardano-api/src/Cardano/Api/Convenience/Construction.hs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
-- | Convenience transaction construction functions | ||
-- | ||
module Cardano.Api.Convenience.Construction ( | ||
constructBalancedTx, | ||
|
||
-- * Misc | ||
TxInsExistError(..), | ||
NotScriptLockedTxInsError(..), | ||
notScriptLockedTxIns, | ||
renderNotScriptLockedTxInsError, | ||
renderTxInsExistError, | ||
txInsExistInUTxO, | ||
|
||
) where | ||
|
||
import Prelude | ||
|
||
import qualified Data.List as List | ||
import qualified Data.Map.Strict as Map | ||
import Data.Set (Set) | ||
import qualified Data.Set as Set | ||
import Data.Text (Text) | ||
import qualified Data.Text as Text | ||
|
||
import Cardano.Api.Address | ||
import Cardano.Api.Certificate | ||
import Cardano.Api.Eras | ||
import Cardano.Api.Fees | ||
import Cardano.Api.IPC | ||
import Cardano.Api.Modes | ||
import Cardano.Api.ProtocolParameters | ||
import Cardano.Api.Query | ||
import Cardano.Api.Tx | ||
import Cardano.Api.TxBody | ||
import Cardano.Api.Utils | ||
|
||
-- | Construct a balanced transaction. | ||
-- See Cardano.Api.Convenience.Query.queryStateForBalancedTx for a | ||
-- convenient way of querying the node to get the required arguements | ||
-- for constructBalancedTx. | ||
constructBalancedTx | ||
:: IsShelleyBasedEra era | ||
=> EraInMode era CardanoMode | ||
-> TxBodyContent BuildTx era | ||
-> AddressInEra era -- ^ Change address | ||
-> Maybe Word -- ^ Override key witnesses | ||
-> UTxO era -- ^ Just the transaction inputs, not the entire 'UTxO'. | ||
-> ProtocolParameters | ||
-> EraHistory CardanoMode | ||
-> SystemStart | ||
-> Set PoolId -- ^ The set of registered stake pools | ||
-> [ShelleyWitnessSigningKey] | ||
-> Either TxBodyErrorAutoBalance (Tx era) | ||
constructBalancedTx eInMode txbodcontent changeAddr mOverrideWits utxo pparams | ||
eraHistory systemStart stakePools shelleyWitSigningKeys = do | ||
BalancedTxBody txbody _txBalanceOutput _fee | ||
<- makeTransactionBodyAutoBalance | ||
eInMode systemStart eraHistory | ||
pparams stakePools utxo txbodcontent | ||
changeAddr mOverrideWits | ||
|
||
let keyWits = map (makeShelleyKeyWitness txbody) shelleyWitSigningKeys | ||
return $ makeSignedTransaction keyWits txbody | ||
|
||
data TxInsExistError | ||
= TxInsDoNotExist [TxIn] | ||
| EmptyUTxO | ||
|
||
renderTxInsExistError :: TxInsExistError -> Text | ||
renderTxInsExistError EmptyUTxO = | ||
"The UTxO is empty" | ||
renderTxInsExistError (TxInsDoNotExist txins) = | ||
"The following tx input(s) were not present in the UTxO: " <> | ||
Text.singleton '\n' <> | ||
Text.intercalate (Text.singleton '\n') (map renderTxIn txins) | ||
|
||
txInsExistInUTxO :: [TxIn] -> UTxO era -> Either TxInsExistError () | ||
txInsExistInUTxO ins (UTxO utxo) | ||
| null utxo = Left EmptyUTxO | ||
| otherwise = do | ||
let utxoIns = Map.keys utxo | ||
occursInUtxo = [ txin | txin <- ins, txin `elem` utxoIns ] | ||
if length occursInUtxo == length ins | ||
then return () | ||
else Left . TxInsDoNotExist $ ins List.\\ occursInUtxo | ||
|
||
newtype NotScriptLockedTxInsError = NotScriptLockedTxIns [TxIn] | ||
|
||
renderNotScriptLockedTxInsError :: NotScriptLockedTxInsError -> Text | ||
renderNotScriptLockedTxInsError (NotScriptLockedTxIns txins) = | ||
"The followings tx inputs are not script locked: " <> textShow (map renderTxIn txins) | ||
|
||
notScriptLockedTxIns :: [TxIn] -> UTxO era -> Either NotScriptLockedTxInsError () | ||
notScriptLockedTxIns collTxIns (UTxO utxo) = do | ||
let onlyCollateralUTxOs = Map.restrictKeys utxo $ Set.fromList collTxIns | ||
scriptLockedTxIns = | ||
filter (\(_, TxOut aInEra _ _ _) -> not $ isKeyAddress aInEra ) $ Map.assocs onlyCollateralUTxOs | ||
if null scriptLockedTxIns | ||
then return () | ||
else Left . NotScriptLockedTxIns $ map fst scriptLockedTxIns | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
{-# LANGUAGE ScopedTypeVariables #-} | ||
{-# LANGUAGE TypeFamilies #-} | ||
|
||
-- | Convenience query functions | ||
-- | ||
module Cardano.Api.Convenience.Query ( | ||
QueryConvenienceError(..), | ||
determineEra, | ||
-- * Simplest query related | ||
executeQueryCardanoMode, | ||
|
||
queryStateForBalancedTx, | ||
renderQueryConvenienceError, | ||
) where | ||
|
||
import Prelude | ||
|
||
import Data.Bifunctor (first) | ||
import Data.Set (Set) | ||
import qualified Data.Set as Set | ||
import Data.Text (Text) | ||
|
||
import Ouroboros.Consensus.HardFork.Combinator.AcrossEras (EraMismatch (..)) | ||
|
||
import Cardano.Api.Certificate | ||
import Cardano.Api.Convenience.Constraints | ||
import Cardano.Api.Environment | ||
import Cardano.Api.Eras | ||
import Cardano.Api.IPC | ||
import Cardano.Api.Modes | ||
import Cardano.Api.NetworkId | ||
import Cardano.Api.ProtocolParameters | ||
import Cardano.Api.Query | ||
import Cardano.Api.TxBody | ||
import Cardano.Api.Utils | ||
|
||
data QueryConvenienceError | ||
= AcqFailure AcquiringFailure | ||
| SockErr EnvSocketError | ||
| QueryEraMismatch EraMismatch | ||
| ByronEraNotSupported | ||
| EraConsensusModeMismatch !AnyConsensusMode !AnyCardanoEra | ||
|
||
renderQueryConvenienceError :: QueryConvenienceError -> Text | ||
renderQueryConvenienceError (AcqFailure e) = | ||
"Acquiring failure: " <> textShow e | ||
renderQueryConvenienceError (SockErr e) = | ||
renderEnvSocketError e | ||
renderQueryConvenienceError (QueryEraMismatch (EraMismatch ledgerEraName' otherEraName')) = | ||
"The era of the node and the tx do not match. " <> | ||
"The node is running in the " <> ledgerEraName' <> | ||
" era, but the transaction is for the " <> otherEraName' <> " era." | ||
renderQueryConvenienceError ByronEraNotSupported = | ||
"Byron era not supported" | ||
renderQueryConvenienceError (EraConsensusModeMismatch cMode anyCEra) = | ||
"Consensus mode and era mismatch. Consensus mode: " <> textShow cMode <> | ||
" Era: " <> textShow anyCEra | ||
|
||
-- | A convenience function to query the relevant information, from | ||
-- the local node, for Cardano.Api.Convenience.Construction.constructBalancedTx | ||
queryStateForBalancedTx | ||
:: CardanoEra era | ||
-> NetworkId | ||
-> [TxIn] | ||
-> IO (Either QueryConvenienceError (UTxO era, ProtocolParameters, EraHistory CardanoMode, SystemStart, Set PoolId)) | ||
queryStateForBalancedTx era networkId allTxIns = do | ||
eSocketPath <- first SockErr <$> readEnvSocketPath | ||
case eSocketPath of | ||
Left e -> return $ Left e | ||
Right (SocketPath sockPath) -> do | ||
let cModeParams = CardanoModeParams $ EpochSlots 21600 | ||
localNodeConnInfo = LocalNodeConnectInfo | ||
cModeParams | ||
networkId | ||
sockPath | ||
eSbe <- return . getSbe $ cardanoEraStyle era | ||
case eSbe of | ||
Left e -> return $ Left e | ||
Right qSbe -> do | ||
case toEraInMode era CardanoMode of | ||
Just qeInMode -> do | ||
|
||
-- Queries | ||
let utxoQuery = QueryInEra qeInMode $ QueryInShelleyBasedEra qSbe | ||
$ QueryUTxO (QueryUTxOByTxIn (Set.fromList allTxIns)) | ||
pparamsQuery = QueryInEra qeInMode | ||
$ QueryInShelleyBasedEra qSbe QueryProtocolParameters | ||
eraHistoryQuery = QueryEraHistory CardanoModeIsMultiEra | ||
systemStartQuery = QuerySystemStart | ||
stakePoolsQuery = QueryInEra qeInMode . QueryInShelleyBasedEra qSbe $ QueryStakePools | ||
|
||
-- Query execution | ||
eUtxo <- executeQueryCardanoMode era networkId utxoQuery | ||
ePparams <- executeQueryCardanoMode era networkId pparamsQuery | ||
eEraHistory <- queryNodeLocalState localNodeConnInfo Nothing eraHistoryQuery | ||
eSystemStart <- queryNodeLocalState localNodeConnInfo Nothing systemStartQuery | ||
eStakePools <- executeQueryCardanoMode era networkId stakePoolsQuery | ||
return $ do | ||
utxo <- eUtxo | ||
pparams <- ePparams | ||
eraHistory <- first AcqFailure eEraHistory | ||
systemStart <- first AcqFailure eSystemStart | ||
stakePools <- eStakePools | ||
Right (utxo, pparams, eraHistory, systemStart, stakePools) | ||
Nothing -> return $ Left $ EraConsensusModeMismatch | ||
(AnyConsensusMode CardanoMode) | ||
(getIsCardanoEraConstraint era $ AnyCardanoEra era) | ||
|
||
-- | Query the node to determine which era it is in. | ||
determineEra | ||
:: ConsensusModeParams mode | ||
-> LocalNodeConnectInfo mode | ||
-> IO (Either AcquiringFailure AnyCardanoEra) | ||
determineEra cModeParams localNodeConnInfo = | ||
case consensusModeOnly cModeParams of | ||
ByronMode -> return . Right $ AnyCardanoEra ByronEra | ||
ShelleyMode -> return . Right $ AnyCardanoEra ShelleyEra | ||
CardanoMode -> | ||
queryNodeLocalState localNodeConnInfo Nothing | ||
$ QueryCurrentEra CardanoModeIsMultiEra | ||
|
||
getSbe :: CardanoEraStyle era -> Either QueryConvenienceError (ShelleyBasedEra era) | ||
getSbe LegacyByronEra = Left ByronEraNotSupported | ||
getSbe (ShelleyBasedEra sbe) = return sbe | ||
|
||
-- | Execute a query against the local node. The local | ||
-- node must be in CardanoMode. | ||
executeQueryCardanoMode | ||
:: CardanoEra era | ||
-> NetworkId | ||
-> QueryInMode CardanoMode (Either EraMismatch result) | ||
-> IO (Either QueryConvenienceError result) | ||
executeQueryCardanoMode era nid q = do | ||
eSocketPath <- first SockErr <$> readEnvSocketPath | ||
case eSocketPath of | ||
Left e -> return $ Left e | ||
Right (SocketPath sockPath) -> do | ||
let localConnectInfo = | ||
LocalNodeConnectInfo | ||
{ localConsensusModeParams = CardanoModeParams (EpochSlots 21600) | ||
, localNodeNetworkId = nid | ||
, localNodeSocketPath = sockPath | ||
} | ||
executeQueryAnyMode era localConnectInfo q | ||
|
||
-- | Execute a query against the local node in any mode. | ||
executeQueryAnyMode | ||
:: forall result era mode. CardanoEra era | ||
-> LocalNodeConnectInfo mode | ||
-> QueryInMode mode (Either EraMismatch result) | ||
-> IO (Either QueryConvenienceError result) | ||
executeQueryAnyMode era localNodeConnInfo q = do | ||
let cMode = consensusModeOnly $ localConsensusModeParams localNodeConnInfo | ||
case toEraInMode era cMode of | ||
Just eraInMode -> | ||
case eraInMode of | ||
ByronEraInByronMode -> return $ Left ByronEraNotSupported | ||
_ -> execQuery | ||
Nothing -> return $ Left $ EraConsensusModeMismatch | ||
(AnyConsensusMode CardanoMode) | ||
(getIsCardanoEraConstraint era $ AnyCardanoEra era) | ||
where | ||
execQuery :: IO (Either QueryConvenienceError result) | ||
execQuery = collapse <$> queryNodeLocalState localNodeConnInfo Nothing q | ||
|
||
collapse | ||
:: Either AcquiringFailure (Either EraMismatch a) | ||
-> Either QueryConvenienceError a | ||
collapse res = do | ||
innerRes <- first AcqFailure res | ||
first QueryEraMismatch innerRes | ||
|
Oops, something went wrong.