Skip to content

Commit 66df475

Browse files
Merge #2255
2255: do not enforce non-empty outputs for unsigned tx r=KtorZ a=KtorZ # Issue Number <!-- Put here a reference to the issue this PR relates to and which requirements it tackles --> #2200 # Overview <!-- Detail in a few bullet points the work accomplished in this PR --> - c60c5a6 📍 **do not enforce non-empty outputs for unsigned tx** Actually, there are many use-cases for empty outputs. This occurs in particular often when performing a selection for delegation: in this scenario, there are typically no outputs at all and, depending on the size of the selected input(s), there might be no change at all (because of the minUTxO value). # Comments <!-- Additional comments or screenshots to attach if any --> Should fix: - #2213 (comment) - #2213 (comment) Thanks @piotr-iohk 🙏 <!-- Don't forget to: ✓ Self-review your changes to make sure nothing unexpected slipped through ✓ Assign yourself to the PR ✓ Assign one or several reviewer(s) ✓ Once created, link this PR to its corresponding ticket ✓ Assign the PR to a corresponding milestone ✓ Acknowledge any changes required to the Wiki --> Co-authored-by: KtorZ <matthias.benkort@gmail.com>
2 parents 05e59a0 + 9572318 commit 66df475

File tree

7 files changed

+33
-37
lines changed

7 files changed

+33
-37
lines changed

default.nix

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ let
9393
ruby
9494
sqlite-interactive
9595
yq
96-
]) ++ attrValues hls;
96+
]);
9797
tools = {
9898
cabal = "3.2.0.0";
9999
ghcid = "0.8.7";
@@ -117,10 +117,12 @@ let
117117
'';
118118
};
119119

120-
# Build latest release of haskell-language-server from github
121-
hls = pkgs.callPackages ./nix/hls.nix {
122-
compiler-nix-name = haskellPackages._config.compiler.nix-name;
123-
};
120+
# FIXME: This is causing issue in CI, as described here: https://github.com/input-output-hk/haskell.nix/issues/884
121+
#
122+
# # Build latest release of haskell-language-server from github
123+
# hls = pkgs.callPackages ./nix/hls.nix {
124+
# compiler-nix-name = haskellPackages._config.compiler.nix-name;
125+
# };
124126

125127
self = {
126128
inherit pkgs commonLib src haskellPackages profiledHaskellPackages coveredHaskellPackages;

lib/core/src/Cardano/Wallet.hs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,7 +1634,7 @@ signTx
16341634
-> Maybe TxMetadata
16351635
-> UnsignedTx (TxIn, TxOut)
16361636
-> ExceptT ErrSignPayment IO (Tx, TxMeta, UTCTime, SealedTx)
1637-
signTx ctx wid pwd md (UnsignedTx inpsNE outsNE) = db & \DBLayer{..} -> do
1637+
signTx ctx wid pwd md (UnsignedTx inpsNE outs) = db & \DBLayer{..} -> do
16381638
withRootKey @_ @s ctx wid pwd ErrSignPaymentWithRootKey $ \xprv scheme -> do
16391639
let pwdP = preparePassphrase scheme pwd
16401640
nodeTip <- withExceptT ErrSignPaymentNetwork $ currentNodeTip nl
@@ -1658,7 +1658,6 @@ signTx ctx wid pwd md (UnsignedTx inpsNE outsNE) = db & \DBLayer{..} -> do
16581658
tl = ctx ^. transactionLayer @t @k
16591659
nl = ctx ^. networkLayer @t
16601660
inps = NE.toList inpsNE
1661-
outs = NE.toList outsNE
16621661

16631662
-- | Makes a fully-resolved coin selection for the given set of payments.
16641663
selectCoinsExternal
@@ -1687,8 +1686,7 @@ selectCoinsExternal ctx wid argGenChange selectCoins = do
16871686
UnsignedTx
16881687
<$> (fullyQualifiedInputs s' cs'
16891688
(ErrSelectCoinsExternalUnableToAssignInputs cs'))
1690-
<*> ensureNonEmpty (outputs cs')
1691-
(ErrSelectCoinsExternalUnableToAssignOutputs cs')
1689+
<*> pure (outputs cs')
16921690
where
16931691
db = ctx ^. dbLayer @s @k
16941692

@@ -1697,7 +1695,6 @@ data ErrSelectCoinsExternal e
16971695
| ErrSelectCoinsExternalForPayment (ErrSelectForPayment e)
16981696
| ErrSelectCoinsExternalForDelegation ErrSelectForDelegation
16991697
| ErrSelectCoinsExternalUnableToAssignInputs CoinSelection
1700-
| ErrSelectCoinsExternalUnableToAssignOutputs CoinSelection
17011698
deriving (Eq, Show)
17021699

17031700
signDelegation
@@ -2265,7 +2262,6 @@ data ErrSelectForDelegation
22652262
= ErrSelectForDelegationNoSuchWallet ErrNoSuchWallet
22662263
| ErrSelectForDelegationFee ErrAdjustForFee
22672264
| ErrSelectForDelegationUnableToAssignInputs ErrNoSuchWallet
2268-
| ErrSelectForDelegationUnableToAssignOutputs ErrNoSuchWallet
22692265
deriving (Show, Eq)
22702266

22712267
-- | Errors that can occur when signing a delegation certificate.
@@ -2283,7 +2279,6 @@ data ErrJoinStakePool
22832279
| ErrJoinStakePoolSubmitTx ErrSubmitTx
22842280
| ErrJoinStakePoolCannotJoin ErrCannotJoin
22852281
| ErrJoinStakePoolUnableToAssignInputs CoinSelection
2286-
| ErrJoinStakePoolUnableToAssignOutputs CoinSelection
22872282
deriving (Generic, Eq, Show)
22882283

22892284
data ErrQuitStakePool
@@ -2293,7 +2288,6 @@ data ErrQuitStakePool
22932288
| ErrQuitStakePoolSubmitTx ErrSubmitTx
22942289
| ErrQuitStakePoolCannotQuit ErrCannotQuit
22952290
| ErrQuitStakePoolUnableToAssignInputs CoinSelection
2296-
| ErrQuitStakePoolUnableToAssignOutputs CoinSelection
22972291
deriving (Generic, Eq, Show)
22982292

22992293
-- | Errors that can occur when fetching the reward balance of a wallet

lib/core/src/Cardano/Wallet/Api/Server.hs

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1659,7 +1659,7 @@ migrateWallet ctx (ApiT wid) migrateData = do
16591659
ti
16601660
(txId tx)
16611661
(fmap Just <$> NE.toList (W.unsignedInputs cs))
1662-
(NE.toList (W.unsignedOutputs cs))
1662+
(W.unsignedOutputs cs)
16631663
(tx ^. #withdrawals)
16641664
(meta, time)
16651665
Nothing
@@ -1699,7 +1699,7 @@ assignMigrationAddresses addrs selections =
16991699
makeTx :: CoinSelection -> [Address] -> UnsignedTx (TxIn, TxOut)
17001700
makeTx sel addrsSelected = UnsignedTx
17011701
(NE.fromList (sel ^. #inputs))
1702-
(NE.fromList (zipWith TxOut addrsSelected (sel ^. #change)))
1702+
(zipWith TxOut addrsSelected (sel ^. #change))
17031703

17041704
{-------------------------------------------------------------------------------
17051705
Network
@@ -1916,6 +1916,7 @@ mkApiCoinSelection mcerts (UnsignedTx inputs outputs) =
19161916
]
19171917
where
19181918
apiStakePath = ApiT <$> xs
1919+
19191920
mkAddressAmount :: TxOut -> AddressAmount (ApiT Address, Proxy n)
19201921
mkAddressAmount (TxOut addr (Coin c)) =
19211922
AddressAmount (ApiT addr, Proxy @n) (Quantity $ fromIntegral c)
@@ -2273,11 +2274,8 @@ instance Buildable e => LiftHandler (ErrSelectCoinsExternal e) where
22732274
ErrSelectCoinsExternalUnableToAssignInputs e ->
22742275
apiError err500 UnableToAssignInputOutput $ mconcat
22752276
[ "I'm unable to assign inputs from coin selection: "
2276-
, pretty e]
2277-
ErrSelectCoinsExternalUnableToAssignOutputs e ->
2278-
apiError err500 UnableToAssignInputOutput $ mconcat
2279-
[ "I'm unable to assign outputs from coin selection: "
2280-
, pretty e]
2277+
, pretty e
2278+
]
22812279

22822280
instance Buildable e => LiftHandler (ErrCoinSelection e) where
22832281
handler = \case
@@ -2543,7 +2541,6 @@ instance LiftHandler ErrSelectForDelegation where
25432541
, "delegation certificate. I need: ", showT cost, " Lovelace."
25442542
]
25452543
ErrSelectForDelegationUnableToAssignInputs e -> handler e
2546-
ErrSelectForDelegationUnableToAssignOutputs e -> handler e
25472544

25482545
instance LiftHandler ErrSignDelegation where
25492546
handler = \case
@@ -2582,10 +2579,6 @@ instance LiftHandler ErrJoinStakePool where
25822579
apiError err500 UnableToAssignInputOutput $ mconcat
25832580
[ "I'm unable to assign inputs from coin selection: "
25842581
, pretty e]
2585-
ErrJoinStakePoolUnableToAssignOutputs e ->
2586-
apiError err500 UnableToAssignInputOutput $ mconcat
2587-
[ "I'm unable to assign outputs from coin selection: "
2588-
, pretty e]
25892582

25902583
instance LiftHandler ErrFetchRewards where
25912584
handler = \case
@@ -2626,10 +2619,6 @@ instance LiftHandler ErrQuitStakePool where
26262619
apiError err500 UnableToAssignInputOutput $ mconcat
26272620
[ "I'm unable to assign inputs from coin selection: "
26282621
, pretty e]
2629-
ErrQuitStakePoolUnableToAssignOutputs e ->
2630-
apiError err500 UnableToAssignInputOutput $ mconcat
2631-
[ "I'm unable to assign outputs from coin selection: "
2632-
, pretty e]
26332622

26342623
instance LiftHandler ErrCreateRandomAddress where
26352624
handler = \case

lib/core/src/Cardano/Wallet/Api/Types.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -410,7 +410,7 @@ data ApiCertificate
410410

411411
data ApiCoinSelection (n :: NetworkDiscriminant) = ApiCoinSelection
412412
{ inputs :: !(NonEmpty (ApiCoinSelectionInput n))
413-
, outputs :: !(NonEmpty (AddressAmount (ApiT Address, Proxy n)))
413+
, outputs :: ![AddressAmount (ApiT Address, Proxy n)]
414414
, certificates :: Maybe (NonEmpty ApiCertificate)
415415
} deriving (Eq, Generic, Show)
416416

lib/core/src/Cardano/Wallet/Primitive/Types.hs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,8 +969,20 @@ instance ToText TxStatus where
969969
data UnsignedTx input = UnsignedTx
970970
{ unsignedInputs
971971
:: NonEmpty input
972+
-- Inputs are *necessarily* non-empty because Cardano requires at least
973+
-- one UTxO input per transaction to prevent replayable transactions.
974+
-- (each UTxO being unique, including at least one UTxO in the
975+
-- transaction body makes it seemingly unique).
976+
972977
, unsignedOutputs
973-
:: NonEmpty TxOut
978+
:: [TxOut]
979+
-- Unlike inputs, it is perfectly reasonable to have empty outputs. The
980+
-- main scenario where this might occur is when constructing a
981+
-- delegation for the sake of submitting a certificate. This type of
982+
-- transaction does not typically include any target output and,
983+
-- depending on which input(s) get selected to fuel the transaction, it
984+
-- may or may not include a change output should its value be less than
985+
-- the minimal UTxO value set by the network.
974986
}
975987
deriving (Eq, Show)
976988

lib/core/test/unit/Cardano/Wallet/Primitive/CoinSelectionSpec.hs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ prop_coinValuesPreserved (CoinSelectionsSetup cs addrs) = do
145145
let selsCoinValue =
146146
sum $ getCoinValueFromInp . inputs . getCS <$> cs
147147
let getCoinValueFromTxOut (UnsignedTx _ txouts) =
148-
sum $ map (\(TxOut _ (Coin c)) -> c) $ NE.toList txouts
148+
sum $ map (\(TxOut _ (Coin c)) -> c) txouts
149149
let txsCoinValue =
150150
sum . map getCoinValueFromTxOut
151151
txsCoinValue (assignMigrationAddresses addrs sels) === selsCoinValue
@@ -164,7 +164,7 @@ prop_coinValuesPreservedPerTx f (CoinSelectionsSetup cs addrs) = do
164164
f . map (\(_, TxOut _ (Coin c)) -> c)
165165
let selsCoinValue = getCoinValueFromInp . inputs . getCS <$> cs
166166
let getCoinValueFromTxOut (UnsignedTx _ txouts) =
167-
f $ map (\(TxOut _ (Coin c)) -> c) $ NE.toList txouts
167+
f $ map (\(TxOut _ (Coin c)) -> c) txouts
168168
let txsCoinValue = map getCoinValueFromTxOut
169169
txsCoinValue (assignMigrationAddresses addrs sels) === selsCoinValue
170170

@@ -202,8 +202,7 @@ prop_fairAddressesRecycled
202202
prop_fairAddressesRecycled (CoinSelectionsSetup cs addrs) = do
203203
let sels = getCS <$> cs
204204
let getAllAddrPerTx (UnsignedTx _ txouts) =
205-
map (\(TxOut addr _) -> addr) $
206-
NE.toList txouts
205+
map (\(TxOut addr _) -> addr) txouts
207206
let getAllAddrCounts =
208207
Map.elems .
209208
foldr (\x -> Map.insertWith (+) x (1::Int)) Map.empty .

specifications/api/swagger.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,7 @@ x-transactionInputs: &transactionInputs
520520
x-transactionOutputs: &transactionOutputs
521521
description: A list of target outputs
522522
type: array
523-
minItems: 1
523+
minItems: 0
524524
items:
525525
type: object
526526
required:
@@ -2947,7 +2947,7 @@ paths:
29472947
Random-Improve coin selection algorithm</a>.
29482948
29492949
<b>Note: </b> Not supported for Byron random wallets.
2950-
2950+
29512951
parameters:
29522952
- *parametersWalletId
29532953
requestBody:

0 commit comments

Comments
 (0)