Skip to content

Commit 16a5f0c

Browse files
committed
remove duplicated logic with regards to selections for join/quit
The code was becoming quite convoluted here with function names that were screaming for refactoring (when we start adding `'` to functions, it's usually a good sign that something can be simplified). The main change is actually in 'selectCoinsExternal' which is now parameterized over the selection to run. This way, the steps of assigning change addresses are factored out in this function and a lot of the duplication goes away. Another important change is that I've moved the signing and submission of join/quit outside of the body of the function. So that each step can be ran independently. This avoid the need for weird intermediate product types aggregating more and more information. Now, both functions are returning a 'DelegationAction' and merely checking that a join or quit is possible.
1 parent 16451e5 commit 16a5f0c

File tree

3 files changed

+145
-260
lines changed

3 files changed

+145
-260
lines changed

lib/core/src/Cardano/Wallet.hs

Lines changed: 46 additions & 227 deletions
Original file line numberDiff line numberDiff line change
@@ -125,9 +125,7 @@ module Cardano.Wallet
125125
-- ** Delegation
126126
, PoolRetirementEpochInfo (..)
127127
, joinStakePool
128-
, joinStakePoolUnsigned
129128
, quitStakePool
130-
, quitStakePoolUnsigned
131129
, selectCoinsForDelegation
132130
, estimateFeeForDelegation
133131
, signDelegation
@@ -183,7 +181,7 @@ import Prelude hiding
183181
( log )
184182

185183
import Cardano.Address.Derivation
186-
( XPrv, XPub )
184+
( XPrv )
187185
import Cardano.BM.Data.Severity
188186
( Severity (..) )
189187
import Cardano.BM.Data.Tracer
@@ -222,7 +220,6 @@ import Cardano.Wallet.Primitive.AddressDerivation
222220
, NetworkDiscriminant (..)
223221
, Passphrase
224222
, PaymentAddress (..)
225-
, SoftDerivation
226223
, ToChimericAccount (..)
227224
, WalletKey (..)
228225
, checkPassphrase
@@ -997,18 +994,20 @@ readChimericAccount
997994
)
998995
=> ctx
999996
-> WalletId
1000-
-> ExceptT ErrReadChimericAccount IO ChimericAccount
997+
-> ExceptT ErrReadChimericAccount IO (ChimericAccount, NonEmpty DerivationIndex)
1001998
readChimericAccount ctx wid = db & \DBLayer{..} -> do
1002999
cp <- withExceptT ErrReadChimericAccountNoSuchWallet
10031000
$ mapExceptT atomically
10041001
$ withNoSuchWallet wid
10051002
$ readCheckpoint (PrimaryKey wid)
10061003
case testEquality (typeRep @s) (typeRep @shelley) of
1007-
Nothing -> throwE ErrReadChimericAccountNotAShelleyWallet
1008-
Just Refl -> pure
1009-
$ toChimericAccount
1010-
$ Seq.rewardAccountKey
1011-
$ getState cp
1004+
Nothing ->
1005+
throwE ErrReadChimericAccountNotAShelleyWallet
1006+
Just Refl -> do
1007+
let s = getState cp
1008+
let acct = toChimericAccount $ Seq.rewardAccountKey s
1009+
let path = stakeDerivationPath $ Seq.derivationPrefix s
1010+
pure (acct, path)
10121011
where
10131012
db = ctx ^. dbLayer @s @k
10141013

@@ -1051,7 +1050,7 @@ manageRewardBalance _ ctx wid = db & \DBLayer{..} -> do
10511050
watchNodeTip $ \bh -> do
10521051
traceWith tr $ MsgRewardBalanceQuery bh
10531052
query <- runExceptT $ do
1054-
acct <- withExceptT ErrFetchRewardsReadChimericAccount $
1053+
(acct, _) <- withExceptT ErrFetchRewardsReadChimericAccount $
10551054
readChimericAccount @ctx @s @k @n ctx wid
10561055
queryRewardBalance @ctx @t ctx acct
10571056
traceWith tr $ MsgRewardBalanceResult query
@@ -1663,34 +1662,28 @@ signTx ctx wid pwd md (UnsignedTx inpsNE outsNE) = db & \DBLayer{..} -> do
16631662

16641663
-- | Makes a fully-resolved coin selection for the given set of payments.
16651664
selectCoinsExternal
1666-
:: forall ctx s t k e.
1665+
:: forall ctx s k e resolvedInput.
16671666
( GenChange s
16681667
, HasDBLayer s k ctx
1669-
, HasLogger WalletLog ctx
1670-
, HasTransactionLayer t k ctx
1671-
, e ~ ErrValidateSelection t
16721668
, IsOurs s Address
1669+
, resolvedInput ~ (TxIn, TxOut, NonEmpty DerivationIndex)
16731670
)
16741671
=> ctx
16751672
-> WalletId
16761673
-> ArgGenChange s
1677-
-> NonEmpty TxOut
1678-
-> Quantity "lovelace" Word64
1679-
-> Maybe TxMetadata
1680-
-> ExceptT
1681-
(ErrSelectCoinsExternal e)
1682-
IO
1683-
(UnsignedTx (TxIn, TxOut, NonEmpty DerivationIndex))
1684-
selectCoinsExternal ctx wid argGenChange payments withdrawal md = do
1685-
cs <- withExceptT ErrSelectCoinsExternalUnableToMakeSelection $
1686-
selectCoinsForPayment @ctx @s @t @k @e ctx wid payments withdrawal md
1674+
-> ExceptT (ErrSelectCoinsExternal e) IO CoinSelection
1675+
-> ExceptT (ErrSelectCoinsExternal e) IO (UnsignedTx resolvedInput)
1676+
selectCoinsExternal ctx wid argGenChange selectCoins = do
1677+
cs <- selectCoins
1678+
16871679
(cs', s') <- db & \DBLayer{..} ->
16881680
withExceptT ErrSelectCoinsExternalNoSuchWallet $
16891681
mapExceptT atomically $ do
16901682
cp <- withNoSuchWallet wid $ readCheckpoint $ PrimaryKey wid
16911683
(cs', s') <- assignChangeAddresses argGenChange cs (getState cp)
16921684
putCheckpoint (PrimaryKey wid) (updateState s' cp)
16931685
pure (cs', s')
1686+
16941687
UnsignedTx
16951688
<$> (fullyQualifiedInputs s' cs'
16961689
(ErrSelectCoinsExternalUnableToAssignInputs cs'))
@@ -1701,7 +1694,8 @@ selectCoinsExternal ctx wid argGenChange payments withdrawal md = do
17011694

17021695
data ErrSelectCoinsExternal e
17031696
= ErrSelectCoinsExternalNoSuchWallet ErrNoSuchWallet
1704-
| ErrSelectCoinsExternalUnableToMakeSelection (ErrSelectForPayment e)
1697+
| ErrSelectCoinsExternalForPayment (ErrSelectForPayment e)
1698+
| ErrSelectCoinsExternalForDelegation ErrSelectForDelegation
17051699
| ErrSelectCoinsExternalUnableToAssignInputs CoinSelection
17061700
| ErrSelectCoinsExternalUnableToAssignOutputs CoinSelection
17071701
deriving (Eq, Show)
@@ -1947,55 +1941,10 @@ getTransaction ctx wid tid = db & \DBLayer{..} -> do
19471941
Delegation
19481942
-------------------------------------------------------------------------------}
19491943

1950-
-- | Get the coin selection and certificate info for joining a stake pool.
1951-
-- Don't create a signed transaction.
1952-
joinStakePoolUnsigned
1953-
:: forall ctx s t k n.
1954-
( HasDBLayer s k ctx
1955-
, HasLogger WalletLog ctx
1956-
, HasTransactionLayer t k ctx
1957-
, SoftDerivation k
1958-
, s ~ SeqState n k
1959-
, MkKeyFingerprint k Address
1960-
, MkKeyFingerprint k (Proxy n, k 'AddressK XPub)
1961-
)
1962-
=> ctx
1963-
-> W.EpochNo
1964-
-> Set PoolId
1965-
-> PoolId
1966-
-> PoolLifeCycleStatus
1967-
-> WalletId
1968-
-> ArgGenChange s
1969-
-> ExceptT ErrJoinStakePool IO (UnsignedTx (TxIn, TxOut, NonEmpty DerivationIndex), DelegationAction, NonEmpty DerivationIndex)
1970-
joinStakePoolUnsigned ctx currentEpoch knownPools pid poolStatus wid argGenChange =
1971-
db & \DBLayer{..} -> do
1972-
(wal, _, _) <- withExceptT
1973-
ErrJoinStakePoolNoSuchWallet (readWallet @ctx @s @k ctx wid)
1974-
(cs, action, sPath) <-
1975-
joinStakePoolUnsigned' @ctx @s @t @k @n
1976-
ctx currentEpoch knownPools pid poolStatus wid
1977-
1978-
coinSel' <- mapExceptT atomically $ do
1979-
(coinSel', s') <- assignChangeAddresses argGenChange cs (getState wal)
1980-
1981-
withExceptT ErrJoinStakePoolNoSuchWallet $
1982-
putCheckpoint (PrimaryKey wid) (updateState s' wal)
1983-
pure coinSel'
1984-
1985-
utx <- UnsignedTx
1986-
<$> (fullyQualifiedInputs (getState wal) coinSel'
1987-
(ErrJoinStakePoolUnableToAssignInputs coinSel'))
1988-
<*> ensureNonEmpty (outputs coinSel')
1989-
(ErrJoinStakePoolUnableToAssignOutputs coinSel')
1990-
pure (utx, action, sPath)
1991-
where
1992-
db = ctx ^. dbLayer @s @k
1993-
1994-
joinStakePoolUnsigned'
1995-
:: forall ctx s t k n.
1944+
joinStakePool
1945+
:: forall ctx s k n.
19961946
( HasDBLayer s k ctx
19971947
, HasLogger WalletLog ctx
1998-
, HasTransactionLayer t k ctx
19991948
, s ~ SeqState n k
20001949
)
20011950
=> ctx
@@ -2004,14 +1953,16 @@ joinStakePoolUnsigned'
20041953
-> PoolId
20051954
-> PoolLifeCycleStatus
20061955
-> WalletId
2007-
-> ExceptT ErrJoinStakePool IO (CoinSelection, DelegationAction, NonEmpty DerivationIndex)
2008-
joinStakePoolUnsigned' ctx currentEpoch knownPools pid poolStatus wid =
1956+
-> ExceptT ErrJoinStakePool IO DelegationAction
1957+
joinStakePool ctx currentEpoch knownPools pid poolStatus wid =
20091958
db & \DBLayer{..} -> do
2010-
(wal, walMeta, _) <- withExceptT
2011-
ErrJoinStakePoolNoSuchWallet (readWallet @ctx @s @k ctx wid)
2012-
isKeyReg <- mapExceptT atomically
2013-
$ withExceptT ErrJoinStakePoolNoSuchWallet
2014-
$ isStakeKeyRegistered (PrimaryKey wid)
1959+
(walMeta, isKeyReg) <- mapExceptT atomically $ do
1960+
walMeta <- withExceptT ErrJoinStakePoolNoSuchWallet
1961+
$ withNoSuchWallet wid
1962+
$ readWalletMeta (PrimaryKey wid)
1963+
isKeyReg <- withExceptT ErrJoinStakePoolNoSuchWallet
1964+
$ isStakeKeyRegistered (PrimaryKey wid)
1965+
pure (walMeta, isKeyReg)
20151966

20161967
let mRetirementEpoch = view #retirementEpoch <$>
20171968
W.getPoolRetirementCertificate poolStatus
@@ -2021,172 +1972,40 @@ joinStakePoolUnsigned' ctx currentEpoch knownPools pid poolStatus wid =
20211972
withExceptT ErrJoinStakePoolCannotJoin $ except $
20221973
guardJoin knownPools (walMeta ^. #delegation) pid retirementInfo
20231974

2024-
let action = if isKeyReg then Join pid else RegisterKeyAndJoin pid
20251975
liftIO $ traceWith tr $ MsgIsStakeKeyRegistered isKeyReg
20261976

2027-
cs <- withExceptT ErrJoinStakePoolSelectCoin $
2028-
selectCoinsForDelegation @ctx @s @t @k ctx wid action
2029-
2030-
let s = getState wal
2031-
dprefix = Seq.derivationPrefix s
2032-
sPath = stakeDerivationPath dprefix
2033-
2034-
pure (cs, action, sPath)
2035-
1977+
return $ if isKeyReg
1978+
then Join pid
1979+
else RegisterKeyAndJoin pid
20361980
where
20371981
db = ctx ^. dbLayer @s @k
20381982
tr = ctx ^. logger
20391983

2040-
-- | Helper function to factor necessary logic for joining a stake pool.
2041-
joinStakePool
2042-
:: forall ctx s t k n.
2043-
( HasDBLayer s k ctx
2044-
, HasLogger WalletLog ctx
2045-
, HasTransactionLayer t k ctx
2046-
, IsOwned s k
2047-
, IsOurs s ChimericAccount
2048-
, GenChange s
2049-
, AddressIndexDerivationType k ~ 'Soft
2050-
, WalletKey k
2051-
, s ~ SeqState n k
2052-
, SoftDerivation k
2053-
, HasNetworkLayer t ctx
2054-
)
2055-
=> ctx
2056-
-> W.EpochNo
2057-
-> Set PoolId
2058-
-> PoolId
2059-
-> PoolLifeCycleStatus
2060-
-> WalletId
2061-
-> ArgGenChange s
2062-
-> Passphrase "raw"
2063-
-> ExceptT ErrJoinStakePool IO (Tx, TxMeta, UTCTime)
2064-
joinStakePool ctx currentEpoch knownPools pid poolStatus wid argGenChange pwd =
2065-
db & \DBLayer{..} -> do
2066-
(selection, action, _) <- joinStakePoolUnsigned' @ctx @s @t @k
2067-
ctx currentEpoch knownPools pid poolStatus wid
2068-
2069-
(tx, txMeta, txTime, sealedTx) <-
2070-
withExceptT ErrJoinStakePoolSignDelegation $
2071-
signDelegation
2072-
@ctx @s @t @k ctx wid argGenChange pwd selection action
2073-
2074-
withExceptT ErrJoinStakePoolSubmitTx $
2075-
submitTx @ctx @s @t @k ctx wid (tx, txMeta, sealedTx)
2076-
2077-
pure (tx, txMeta, txTime)
2078-
where
2079-
db = ctx ^. dbLayer @s @k
2080-
2081-
-- | Quit stake pool and return the coin selection and certificates.
2082-
-- Don't create a signed transaction.
2083-
quitStakePoolUnsigned
2084-
:: forall ctx s t k n.
2085-
( HasDBLayer s k ctx
2086-
, HasLogger WalletLog ctx
2087-
, HasTransactionLayer t k ctx
2088-
, SoftDerivation k
2089-
, s ~ SeqState n k
2090-
, MkKeyFingerprint k Address
2091-
, MkKeyFingerprint k (Proxy n, k 'AddressK XPub)
2092-
)
2093-
=> ctx
2094-
-> WalletId
2095-
-> ArgGenChange s
2096-
-> ExceptT ErrQuitStakePool IO
2097-
(UnsignedTx (TxIn, TxOut, NonEmpty DerivationIndex),
2098-
DelegationAction, NonEmpty DerivationIndex)
2099-
quitStakePoolUnsigned ctx wid argGenChange = db & \DBLayer{..} -> do
2100-
(wal, _, _) <- withExceptT
2101-
ErrQuitStakePoolNoSuchWallet (readWallet @ctx @s @k ctx wid)
2102-
(cs, action, sPath) <- quitStakePoolUnsigned' @ctx @s @t @k @n ctx wid
2103-
2104-
coinSel' <- mapExceptT atomically $ do
2105-
(coinSel', s') <- assignChangeAddresses argGenChange cs (getState wal)
2106-
2107-
withExceptT ErrQuitStakePoolNoSuchWallet $
2108-
putCheckpoint (PrimaryKey wid) (updateState s' wal)
2109-
pure coinSel'
2110-
2111-
utx <- UnsignedTx
2112-
<$> (fullyQualifiedInputs (getState wal) coinSel'
2113-
(ErrQuitStakePoolUnableToAssignInputs coinSel'))
2114-
<*> ensureNonEmpty (outputs coinSel')
2115-
(ErrQuitStakePoolUnableToAssignOutputs coinSel')
2116-
pure (utx, action, sPath)
2117-
where
2118-
db = ctx ^. dbLayer @s @k
2119-
2120-
quitStakePoolUnsigned'
2121-
:: forall ctx s t k n.
2122-
( HasDBLayer s k ctx
2123-
, HasLogger WalletLog ctx
2124-
, HasTransactionLayer t k ctx
2125-
, s ~ SeqState n k
2126-
)
2127-
=> ctx
2128-
-> WalletId
2129-
-> ExceptT ErrQuitStakePool IO (CoinSelection, DelegationAction, NonEmpty DerivationIndex)
2130-
quitStakePoolUnsigned' ctx wid = db & \DBLayer{..} -> do
2131-
walMeta <- mapExceptT atomically $ withExceptT ErrQuitStakePoolNoSuchWallet $
2132-
withNoSuchWallet wid $ readWalletMeta (PrimaryKey wid)
2133-
2134-
rewards <- liftIO $ fetchRewardBalance @ctx @s @k ctx wid
2135-
withExceptT ErrQuitStakePoolCannotQuit $ except $
2136-
guardQuit (walMeta ^. #delegation) rewards
2137-
2138-
let action = Quit
2139-
2140-
cs <- withExceptT ErrQuitStakePoolSelectCoin $
2141-
selectCoinsForDelegation @ctx @s @t @k ctx wid action
2142-
2143-
cp <- mapExceptT atomically
2144-
$ withExceptT ErrQuitStakePoolNoSuchWallet
2145-
$ withNoSuchWallet wid
2146-
$ readCheckpoint (PrimaryKey wid)
2147-
let s = getState cp
2148-
dprefix = Seq.derivationPrefix s
2149-
sPath = stakeDerivationPath dprefix
2150-
2151-
pure (cs, action, sPath)
2152-
where
2153-
db = ctx ^. dbLayer @s @k
2154-
21551984
-- | Helper function to factor necessary logic for quitting a stake pool.
21561985
quitStakePool
2157-
:: forall ctx s t k n.
1986+
:: forall ctx s k n.
21581987
( HasDBLayer s k ctx
2159-
, HasLogger WalletLog ctx
2160-
, HasNetworkLayer t ctx
2161-
, HasTransactionLayer t k ctx
2162-
, IsOwned s k
2163-
, IsOurs s ChimericAccount
2164-
, GenChange s
2165-
, AddressIndexDerivationType k ~ 'Soft
2166-
, WalletKey k
21671988
, s ~ SeqState n k
2168-
, SoftDerivation k
21691989
)
21701990
=> ctx
21711991
-> WalletId
2172-
-> ArgGenChange s
2173-
-> Passphrase "raw"
2174-
-> ExceptT ErrQuitStakePool IO (Tx, TxMeta, UTCTime)
2175-
quitStakePool ctx wid argGenChange pwd = db & \DBLayer{..} -> do
2176-
(selection, action, _) <- quitStakePoolUnsigned' @ctx @s @t @k
2177-
ctx wid
1992+
-> ExceptT ErrQuitStakePool IO DelegationAction
1993+
quitStakePool ctx wid = db & \DBLayer{..} -> do
1994+
walMeta <- mapExceptT atomically
1995+
$ withExceptT ErrQuitStakePoolNoSuchWallet
1996+
$ withNoSuchWallet wid
1997+
$ readWalletMeta (PrimaryKey wid)
21781998

2179-
(tx, txMeta, txTime, sealedTx) <- withExceptT ErrQuitStakePoolSignDelegation $
2180-
signDelegation @ctx @s @t @k ctx wid argGenChange pwd selection action
1999+
rewards <- liftIO
2000+
$ fetchRewardBalance @ctx @s @k ctx wid
21812001

2182-
withExceptT ErrQuitStakePoolSubmitTx $
2183-
submitTx @ctx @s @t @k ctx wid (tx, txMeta, sealedTx)
2002+
withExceptT ErrQuitStakePoolCannotQuit $ except $
2003+
guardQuit (walMeta ^. #delegation) rewards
21842004

2185-
pure (tx, txMeta, txTime)
2005+
pure Quit
21862006
where
21872007
db = ctx ^. dbLayer @s @k
21882008

2189-
21902009
{-------------------------------------------------------------------------------
21912010
Fee Estimation
21922011
-------------------------------------------------------------------------------}

0 commit comments

Comments
 (0)