Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit a502bb8

Browse files
committed
[RCD-40] Throw error upon payment to Address with mismatched NetworkMagic in wallet-new API V1
(cherry picked from commit 5deea30)
1 parent 85edc58 commit a502bb8

File tree

6 files changed

+85
-5
lines changed

6 files changed

+85
-5
lines changed

core/test/Test/Pos/Core/Arbitrary.hs

+6
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module Test.Pos.Core.Arbitrary
1919
, SafeCoinPairSub (..)
2020
, UnreasonableEoS (..)
2121

22+
, genAddress
2223
, genSlotId
2324
, genLocalSlotIndex
2425
) where
@@ -259,6 +260,11 @@ instance Arbitrary Address where
259260
arbitrary = makeAddress <$> arbitrary <*> arbitrary
260261
shrink = genericShrink
261262

263+
genAddress :: NetworkMagic -> Gen Address
264+
genAddress nm = makeAddress <$> arbitrary <*> genAddrAttr
265+
where
266+
genAddrAttr = AddrAttributes <$> arbitrary <*> arbitrary <*> pure nm
267+
262268
----------------------------------------------------------------------------
263269
-- Attributes
264270
----------------------------------------------------------------------------

wallet-new/src/Cardano/Wallet/API/V1/ReifyWalletError.hs

+3
Original file line numberDiff line numberDiff line change
@@ -361,3 +361,6 @@ newPaymentError e = case e of
361361

362362
(NewPaymentUnknownAccountId e') ->
363363
unknownHdAccount e'
364+
365+
ex@(NewPaymentAddressBadNetworkMagic _ _) ->
366+
V1.UnknownError $ (sformat build ex)

wallet-new/src/Cardano/Wallet/WalletLayer.hs

+8
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ import Pos.Chain.Block (Blund)
3939
import Pos.Chain.Txp (Tx, TxId, Utxo)
4040
import Pos.Chain.Update (ConfirmedProposalState, SoftwareVersion)
4141
import Pos.Core (Coin, Timestamp)
42+
import qualified Pos.Core as Core (Address)
4243
import Pos.Core.Chrono (NE, NewestFirst (..), OldestFirst (..))
44+
import Pos.Core.NetworkMagic (NetworkMagic)
4345
import Pos.Crypto (EncryptedSecretKey, PassPhrase)
4446

4547
import Cardano.Wallet.API.Request (RequestParams (..))
@@ -569,6 +571,8 @@ data NewPaymentError =
569571
| NewPaymentTimeLimitReached TimeExecutionLimit
570572
| NewPaymentWalletIdDecodingFailed Text
571573
| NewPaymentUnknownAccountId Kernel.UnknownHdAccount
574+
-- | NewPaymentAddressBadNetworkMagic destinationAddress expectedNetworkMagic
575+
| NewPaymentAddressBadNetworkMagic Core.Address NetworkMagic
572576

573577
-- | Unsound show instance needed for the 'Exception' instance.
574578
instance Show NewPaymentError where
@@ -585,6 +589,10 @@ instance Buildable NewPaymentError where
585589
bprint ("NewPaymentWalletIdDecodingFailed " % build) txt
586590
build (NewPaymentUnknownAccountId err) =
587591
bprint ("NewPaymentUnknownAccountId " % build) err
592+
build (NewPaymentAddressBadNetworkMagic srcAddr expectedNM) =
593+
bprint ("NewPaymentAddressBadNetworkMagic " % build % " " % build)
594+
srcAddr
595+
expectedNM
588596

589597

590598
data EstimateFeesError =

wallet-new/src/Cardano/Wallet/WalletLayer/Kernel/Active.hs

+33-2
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,16 @@ module Cardano.Wallet.WalletLayer.Kernel.Active (
1111
import qualified Serokell.Util.Base16 as B16
1212
import Universum
1313

14+
import Control.Monad.Except (throwError)
1415
import Data.Coerce (coerce)
1516
import qualified Data.List.NonEmpty as NE
1617
import Data.Time.Units (Second)
1718

1819
import Pos.Binary.Class (decodeFull')
1920
import Pos.Chain.Txp (Tx (..), TxSigData (..))
20-
import Pos.Core (Address, Coin, TxFeePolicy)
21+
import Pos.Core (AddrAttributes (..), Address (..), Coin, TxFeePolicy)
22+
import Pos.Core.Attributes (Attributes (..))
23+
import Pos.Core.NetworkMagic (NetworkMagic, makeNetworkMagic)
2124
import Pos.Crypto (PassPhrase, PublicKey, Signature (..))
2225

2326
import Cardano.Crypto.Wallet (xsignature)
@@ -29,6 +32,7 @@ import Cardano.Wallet.Kernel.CoinSelection.FromGeneric
2932
InputGrouping, newOptions)
3033
import qualified Cardano.Wallet.Kernel.DB.HdWallet as HD
3134
import Cardano.Wallet.Kernel.DB.TxMeta.Types
35+
import Cardano.Wallet.Kernel.Internal (walletProtocolMagic)
3236
import qualified Cardano.Wallet.Kernel.NodeStateAdaptor as Node
3337
import qualified Cardano.Wallet.Kernel.Transactions as Kernel
3438
import Cardano.Wallet.WalletLayer (EstimateFeesError (..),
@@ -52,8 +56,35 @@ pay activeWallet pw grouping regulation payment = liftIO $ do
5256
runExceptT $ do
5357
(opts, accId, payees) <- withExceptT NewPaymentWalletIdDecodingFailed $
5458
setupPayment policy grouping regulation payment
59+
60+
-- Verify that all payee addresses are of the same `NetworkMagic`
61+
-- as our `ActiveWallet`.
62+
let nm = makeNetworkMagic $ Kernel.walletPassive activeWallet ^. walletProtocolMagic
63+
verifyPayeesNM nm payees
64+
65+
-- Pay the payees
5566
withExceptT NewPaymentError $ ExceptT $
56-
Kernel.pay activeWallet pw opts accId payees
67+
Kernel.pay activeWallet pw opts accId payees
68+
69+
-- | Verifies that the `NetworkMagic` of each payee address matches the
70+
-- provided `NetworkMagic`.
71+
verifyPayeesNM
72+
:: Monad m
73+
=> NetworkMagic
74+
-> NonEmpty (Address, Coin)
75+
-> ExceptT NewPaymentError m ()
76+
verifyPayeesNM nm payees = mapM_ verifyPayeeNM payees
77+
where
78+
addressHasValidMagic :: AddrAttributes -> Bool
79+
addressHasValidMagic addrAttrs = nm == (aaNetworkMagic addrAttrs)
80+
--
81+
verifyPayeeNM
82+
:: Monad m
83+
=> (Address, Coin)
84+
-> ExceptT NewPaymentError m ()
85+
verifyPayeeNM (addr, _) =
86+
unless (addressHasValidMagic ((attrData . addrAttributes) addr)) $
87+
throwError (NewPaymentAddressBadNetworkMagic addr nm )
5788

5889
-- | Estimates the fees for a payment.
5990
estimateFees :: MonadIO m

wallet-new/test/unit/Test/Spec/CoinSelection/Generators.hs

+33-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
module Test.Spec.CoinSelection.Generators (
33
genGroupedUtxo
44
, genPayee
5+
, genPayeeWithNM
56
, genPayees
67
, genFiddlyPayees
78
, genUtxo
@@ -26,11 +27,12 @@ import Test.QuickCheck (Gen, arbitrary, choose, suchThat)
2627

2728
import qualified Pos.Chain.Txp as Core
2829
import qualified Pos.Core as Core
30+
import Pos.Core.NetworkMagic (NetworkMagic)
2931

3032
import Cardano.Wallet.Kernel.Util.Core (paymentAmount, utxoBalance)
3133

3234
-- type class instances
33-
import Test.Pos.Core.Arbitrary ()
35+
import Test.Pos.Core.Arbitrary (genAddress)
3436

3537
{-------------------------------------------------------------------------------
3638
Useful types
@@ -92,6 +94,16 @@ arbitraryAddress opts = do
9294
not (Core.isRedeemAddress a)
9395
arbitrary `suchThat` (\a -> fiddlyCondition a && redeemCondition a)
9496

97+
arbitraryAddressWithNM :: NetworkMagic
98+
-> StakeGenOptions
99+
-> Gen Core.Address
100+
arbitraryAddressWithNM nm opts = do
101+
let fiddlyCondition a = not (fiddlyAddresses opts) ||
102+
(length (sformat F.build a) < 104)
103+
let redeemCondition a = allowRedeemAddresses opts ||
104+
not (Core.isRedeemAddress a)
105+
(genAddress nm) `suchThat` (\a -> fiddlyCondition a && redeemCondition a)
106+
95107

96108
-- | Finalise the generation of 'a' by transferring all the remaining \"slack\".
97109
finalise :: Semigroup a
@@ -257,6 +269,14 @@ genTxOut opts = fromStakeOptions opts genOne paymentAmount
257269
addr <- arbitraryAddress opts
258270
return (Core.TxOut addr coins :| [])
259271

272+
genTxOutWithNM :: NetworkMagic -> StakeGenOptions -> Gen (NonEmpty Core.TxOut)
273+
genTxOutWithNM nm opts = fromStakeOptions opts genOne paymentAmount
274+
where
275+
genOne :: Maybe (NonEmpty Core.TxOut) -> Core.Coin -> Gen (NonEmpty Core.TxOut)
276+
genOne _ coins = do
277+
addr <- arbitraryAddressWithNM nm opts
278+
return (Core.TxOut addr coins :| [])
279+
260280
utxoSmallestEntry :: Core.Utxo -> Core.Coin
261281
utxoSmallestEntry utxo =
262282
case sort (Map.toList utxo) of
@@ -320,6 +340,18 @@ genPayee _utxo payment = do
320340
, allowRedeemAddresses = False
321341
}
322342

343+
genPayeeWithNM :: NetworkMagic -> Core.Utxo -> Pay -> Gen (NonEmpty Core.TxOut)
344+
genPayeeWithNM nm _utxo payment = do
345+
let balance = toLovelaces payment
346+
genTxOutWithNM nm StakeGenOptions {
347+
stakeMaxValue = Nothing
348+
, stakeGenerationTarget = AtLeast
349+
, stakeNeeded = Core.mkCoin balance
350+
, stakeMaxEntries = Just 1
351+
, fiddlyAddresses = False
352+
, allowRedeemAddresses = False
353+
}
354+
323355
-- | Generates a single payee which has a redeem address inside.
324356
genRedeemPayee :: Core.Utxo -> Pay -> Gen (NonEmpty Core.TxOut)
325357
genRedeemPayee _utxo payment = do

wallet-new/test/unit/Test/Spec/NewPayment.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import Pos.Crypto (EncryptedSecretKey, ProtocolMagic,
3232
safeDeterministicKeyGen)
3333

3434
import Test.Spec.CoinSelection.Generators (InitialBalance (..),
35-
Pay (..), genPayee, genUtxoWithAtLeast)
35+
Pay (..), genPayeeWithNM, genUtxoWithAtLeast)
3636

3737
import qualified Cardano.Wallet.API.V1.Types as V1
3838
import qualified Cardano.Wallet.Kernel as Kernel
@@ -110,7 +110,7 @@ prepareFixtures nm initialBalance toPay = do
110110
(getHdAddressIx newIndex)
111111
return $ M.insert txIn (TxOutAux (TxOut addr coin)) acc
112112
) M.empty (M.toList utxo)
113-
payees <- fmap (\(TxOut addr coin) -> (addr, coin)) <$> pick (genPayee utxo toPay)
113+
payees <- fmap (\(TxOut addr coin) -> (addr, coin)) <$> pick (genPayeeWithNM nm utxo toPay)
114114

115115
return $ \keystore aw -> do
116116
liftIO $ Keystore.insert (WalletIdHdRnd newRootId) esk keystore

0 commit comments

Comments
 (0)