Skip to content

Commit

Permalink
remove migration selection handlers until rework
Browse files Browse the repository at this point in the history
  Most of the code doesn't apply anymore because of the way we've changed how coin-selection works. So I've simply thrown everything away. It'll be possible to look at git later when re-implementing this to get some inspiration. It is also very likely that we may want to do things slightly differently, the problem and context being now different.
  • Loading branch information
KtorZ committed Jan 27, 2021
1 parent 736ad57 commit 395b954
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 224 deletions.
136 changes: 0 additions & 136 deletions lib/core/src/Cardano/Wallet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ module Cardano.Wallet
, ErrWithdrawalNotWorth (..)

-- ** Migration
, selectCoinsForMigration
, ErrSelectForMigration (..)

-- ** Delegation
Expand Down Expand Up @@ -146,7 +145,6 @@ module Cardano.Wallet
, listTransactions
, getTransaction
, submitExternalTx
, signTx
, submitTx
, ErrMkTx (..)
, ErrSubmitTx (..)
Expand Down Expand Up @@ -261,8 +259,6 @@ import Cardano.Wallet.Primitive.CoinSelection
, ErrCoinSelection (..)
, feeBalance
)
import Cardano.Wallet.Primitive.CoinSelection.Migration
( depleteUTxO, idealBatchSize )
import Cardano.Wallet.Primitive.Fee
( ErrAdjustForFee (..), Fee (..), FeeOptions (..), adjustForFee )
import Cardano.Wallet.Primitive.Model
Expand Down Expand Up @@ -1434,89 +1430,6 @@ estimateFeeForDelegation ctx wid = db & \DBLayer{..} -> do
db = ctx ^. dbLayer @s @k
pid = PoolId (error "Dummy pool id for estimation. Never evaluated.")

-- | Constructs a set of coin selections that select all funds from the given
-- source wallet, returning them as change.
--
-- If the coin selections returned by this function are used to create
-- transactions from the given wallet to a target wallet, executing those
-- transactions will have the effect of migrating all funds from the given
-- source wallet to the specified target wallet.
selectCoinsForMigration
:: forall ctx s k n.
( HasTransactionLayer k ctx
, HasLogger WalletLog ctx
, HasDBLayer s k ctx
, PaymentAddress n ByronKey
)
=> ctx
-> WalletId
-- ^ The source wallet ID.
-> ExceptT ErrSelectForMigration IO ([CoinSelection], Coin)
selectCoinsForMigration ctx wid = do
(utxo, _, txp, minUtxo) <- withExceptT ErrSelectForMigrationNoSuchWallet $
selectCoinsSetup @ctx @s @k ctx wid
selectCoinsForMigrationFromUTxO @ctx @k @n ctx utxo txp minUtxo wid

selectCoinsForMigrationFromUTxO
:: forall ctx k n.
( HasTransactionLayer k ctx
, HasLogger WalletLog ctx
, PaymentAddress n ByronKey
)
=> ctx
-> W.UTxO
-> W.TxParameters
-> W.Coin
-> WalletId
-- ^ The source wallet ID.
-> ExceptT ErrSelectForMigration IO ([CoinSelection], Coin)
selectCoinsForMigrationFromUTxO ctx utxo txp minUtxo wid = do
let feePolicy@(LinearFee (Quantity a) _) = txp ^. #getFeePolicy
let feeOptions = (feeOpts tl Nothing Nothing txp minBound mempty)
{ estimateFee = minimumFee tl feePolicy Nothing Nothing . worstCase
, dustThreshold = max (Coin $ ceiling a) minUtxo
}
let selOptions = coinSelOpts tl (txp ^. #getTxMaxSize) Nothing
let previousDistribution = W.computeUtxoStatistics W.log10 utxo
liftIO $ traceWith tr $ MsgMigrationUTxOBefore previousDistribution
case depleteUTxO feeOptions (idealBatchSize selOptions) utxo of
cs | not (null cs) -> do
let resultDistribution = W.computeStatistics getCoins W.log10 cs
liftIO $ traceWith tr $ MsgMigrationUTxOAfter resultDistribution
liftIO $ traceWith tr $ MsgMigrationResult cs
let leftovers =
unCoin (TokenBundle.getCoin $ W.balance utxo)
-
W.balance' (concatMap inputs cs)
pure (cs, Coin leftovers)
_ -> throwE (ErrSelectForMigrationEmptyWallet wid)
where
tl = ctx ^. transactionLayer @k
tr = ctx ^. logger

getCoins :: CoinSelection -> [Word64]
getCoins CoinSelection{change,outputs} =
(unCoin <$> change) ++ (unCoin . txOutCoin <$> outputs)

-- When performing a selection for migration, at this stage, we do not know
-- exactly to which address we're going to assign which change. It could be
-- an Icarus address, a Byron address or anything else. But, depending on
-- the address, we get to pay more-or-less as fees!
--
-- Therefore, we assume the worse, which are byron payment addresses, this
-- will create __slightly__ overpriced selections but.. meh.
worstCase :: CoinSelection -> CoinSelection
worstCase cs = cs
{ change = mempty
, outputs = TxOut worstCaseAddress . TokenBundle.fromCoin <$> change cs
}
where
worstCaseAddress :: Address
worstCaseAddress = paymentAddress @n @ByronKey $ publicKey $
unsafeMkByronKeyFromMasterKey
(minBound, minBound)
(unsafeXPrv $ BS.replicate 128 0)

-- | Estimate fee for 'selectCoinsForPayment'.
estimateFeeForPayment
:: forall ctx s k.
Expand Down Expand Up @@ -1666,55 +1579,6 @@ getTxExpiry ti maybeTTL = do
defaultTTL :: NominalDiffTime
defaultTTL = 7200 -- that's 2 hours

-- | Very much like 'signPayment', but doesn't not generate change addresses.
signTx
:: forall ctx s k.
( HasTransactionLayer k ctx
, HasDBLayer s k ctx
, HasNetworkLayer ctx
, IsOurs s RewardAccount
, IsOwned s k
, HardDerivation k
, Bounded (Index (AddressIndexDerivationType k) 'AddressK)
, WalletKey k
)
=> ctx
-> WalletId
-> Passphrase "raw"
-> Maybe TxMetadata
-> Maybe NominalDiffTime
-- This function is currently only used in contexts where all change outputs
-- have been assigned with addresses and are included in the set of ordinary
-- outputs. We use the 'Void' type here to prevent callers from accidentally
-- passing change values into this function:
-> UnsignedTx (TxIn, TxOut) TxOut Void
-> ExceptT ErrSignPayment IO (Tx, TxMeta, UTCTime, SealedTx)
signTx ctx wid pwd md ttl (UnsignedTx inpsNE outs _change) = db & \DBLayer{..} -> do
txExp <- liftIO $ getTxExpiry ti ttl
era <- liftIO $ currentNodeEra nl
withRootKey @_ @s ctx wid pwd ErrSignPaymentWithRootKey $ \xprv scheme -> do
let pwdP = preparePassphrase scheme pwd
mapExceptT atomically $ do
cp <- withExceptT ErrSignPaymentNoSuchWallet $
withNoSuchWallet wid $
readCheckpoint (PrimaryKey wid)

let cs = mempty { inputs = inps, outputs = outs }
let keyFrom = isOwned (getState cp) (xprv, pwdP)
let rewardAcnt = getRawKey $ deriveRewardAccount @k pwdP xprv
(tx, sealedTx) <- withExceptT ErrSignPaymentMkTx $ ExceptT $
pure $ mkStdTx tl era (rewardAcnt, pwdP) keyFrom txExp md cs

(time, meta) <- liftIO $
mkTxMeta ti (currentTip cp) (getState cp) tx cs txExp
return (tx, meta, time, sealedTx)
where
db = ctx ^. dbLayer @s @k
tl = ctx ^. transactionLayer @k
nl = ctx ^. networkLayer
ti = timeInterpreter nl
inps = NE.toList inpsNE

-- | Makes a fully-resolved coin selection for the given set of payments.
selectCoinsExternal
:: forall ctx s k e input output change.
Expand Down
103 changes: 15 additions & 88 deletions lib/core/src/Cardano/Wallet/Api/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,6 @@ module Cardano.Wallet.Api.Server
, withLegacyLayer
, withLegacyLayer'
, rndStateChange
, assignMigrationAddresses
, withWorkerCtx
, getCurrentEpoch

Expand Down Expand Up @@ -1681,23 +1680,8 @@ getMigrationInfo
-> ApiT WalletId
-- ^ Source wallet
-> Handler ApiWalletMigrationInfo
getMigrationInfo ctx (ApiT wid) = do
(cs, leftovers) <- fmap coinToQuantity <$> getSelections
let migrationCost = costFromSelections cs
pure $ ApiWalletMigrationInfo{migrationCost,leftovers}
where
costFromSelections :: [CoinSelection] -> Quantity "lovelace" Natural
costFromSelections = Quantity
. fromIntegral
. sum
. fmap selectionFee

selectionFee :: CoinSelection -> Word64
selectionFee s = inputBalance s - changeBalance s

getSelections :: Handler ([CoinSelection], Coin)
getSelections = withWorkerCtx ctx wid liftE liftE $ \wrk -> liftHandler $
W.selectCoinsForMigration @_ @s @k @n wrk wid
getMigrationInfo _ctx _wid = do
throwE ErrTemporarilyDisabled

migrateWallet
:: forall s k n p.
Expand All @@ -1714,65 +1698,8 @@ migrateWallet
-- ^ Source wallet
-> ApiWalletMigrationPostData n p
-> Handler [ApiTransaction n]
migrateWallet ctx (ApiT wid) migrateData = do
-- TODO: check if addrs are not empty

migration <- do
withWorkerCtx ctx wid liftE liftE $ \wrk -> liftHandler $ do
(cs, _) <- W.selectCoinsForMigration @_ @_ @_ @n wrk wid
pure $ assignMigrationAddresses addrs cs

forM migration $ \cs -> do
(tx, meta, time, wit) <- withWorkerCtx ctx wid liftE liftE
$ \wrk -> liftHandler $ W.signTx @_ @s @k wrk wid pwd Nothing Nothing cs
withWorkerCtx ctx wid liftE liftE
$ \wrk -> liftHandler $ W.submitTx @_ @_ wrk wid (tx, meta, wit)
liftIO $ mkApiTransaction
(timeInterpreter (ctx ^. networkLayer))
(txId tx)
(tx ^. #fee)
(fmap Just <$> NE.toList (W.unsignedInputs cs))
(W.unsignedOutputs cs)
(tx ^. #withdrawals)
(meta, time)
Nothing
#pendingSince
where
pwd = coerce $ getApiT $ migrateData ^. #passphrase
addrs = getApiT . fst <$> migrateData ^. #addresses


-- | Transform the given set of migration coin selections (for a source wallet)
-- into a set of coin selections that will migrate funds to the specified
-- target addresses.
--
-- Each change entry in the specified set of coin selections is replaced with a
-- corresponding output entry in the returned set, where the output entry has a
-- address from specified addresses.
--
-- If the number of outputs in the specified coin selection is greater than
-- the number of addresses in the specified address list, addresses will be
-- recycled in order of their appearance in the original list.
assignMigrationAddresses
:: [Address]
-- ^ Target addresses
-> [CoinSelection]
-- ^ Migration data for the source wallet.
-> [UnsignedTx (TxIn, TxOut) TxOut Void]
-- ^ Unsigned transactions without change, indicated with Void.
assignMigrationAddresses addrs selections =
fst $ foldr accumulate ([], cycle addrs) selections
where
accumulate sel (txs, addrsAvailable) = first
(\addrsSelected -> makeTx sel addrsSelected : txs)
(splitAt (length $ view #change sel) addrsAvailable)

makeTx :: CoinSelection -> [Address] -> UnsignedTx (TxIn, TxOut) TxOut Void
makeTx sel addrsSelected = UnsignedTx
(NE.fromList (sel ^. #inputs))
(zipWith TxOut addrsSelected (TokenBundle.fromCoin <$> sel ^. #change))
-- We never return any change:
[]
migrateWallet _ctx _wid _migrateData = do
liftHandler $ throwE ErrTemporarilyDisabled

{-------------------------------------------------------------------------------
Network
Expand Down Expand Up @@ -2354,10 +2281,21 @@ data ErrCreateWallet
-- ^ Somehow, we couldn't create a worker or open a db connection
deriving (Eq, Show)

data ErrTemporarilyDisabled = ErrTemporarilyDisabled
deriving (Eq, Show)

-- | Small helper to easy show things to Text
showT :: Show a => a -> Text
showT = T.pack . show

instance LiftHandler ErrTemporarilyDisabled where
handler = \case
ErrTemporarilyDisabled ->
apiError err501 NotImplemented $ mconcat
[ "This endpoint is temporarily disabled. It'll be made "
, "accessible again in future releases."
]

instance LiftHandler ErrCurrentEpoch where
handler = \case
ErrUnableToDetermineCurrentEpoch ->
Expand All @@ -2374,17 +2312,6 @@ instance LiftHandler ErrUnexpectedPoolIdPlaceholder where
where
Left msg = fromText @PoolId "INVALID"

instance LiftHandler ErrSelectForMigration where
handler = \case
ErrSelectForMigrationNoSuchWallet e -> handler e
ErrSelectForMigrationEmptyWallet wid ->
apiError err403 NothingToMigrate $ mconcat
[ "I can't migrate the wallet with the given id: "
, toText wid
, ", because it's either empty or full of small coins "
, "which wouldn't be worth migrating."
]

instance LiftHandler ErrNoSuchWallet where
handler = \case
ErrNoSuchWallet wid ->
Expand Down

0 comments on commit 395b954

Please sign in to comment.