Skip to content
This repository was archived by the owner on Mar 1, 2019. It is now read-only.

Commit 03326ce

Browse files
Merge pull request input-output-hk/cardano-sl#3646 from input-output-hk/feature/cbr-401-validate-address
[CBR-401] For the validate-address API endpoint, add an IsOurs field …
2 parents 3ab6d39 + 72c0522 commit 03326ce

File tree

9 files changed

+79
-13
lines changed

9 files changed

+79
-13
lines changed

integration/Functions.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ runAction wc action = do
560560

561561
let changeAddress = toList (txOutputs newTx) \\ toList paymentDestinations
562562
-- NOTE: instead of this manual conversion we could filter WalletAddress from getAddressIndex
563-
pdToChangeAddress PaymentDistribution{..} = WalletAddress pdAddress True True
563+
pdToChangeAddress PaymentDistribution{..} = WalletAddress pdAddress True True (V1 AddressIsOurs)
564564
realChangeAddressId = map addrId addressesAfterTransaction \\ map addrId addressesBeforeTransaction
565565
changeWalletAddresses = filter ((`elem` realChangeAddressId) . addrId) addressesAfterTransaction
566566

src/Cardano/Wallet/API/V1/Migration/Types.hs

+6-1
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,13 @@ instance Migrate V0.CAddress V1.WalletAddress where
177177
eitherMigrate V0.CAddress{..} = do
178178
addrId <- eitherMigrate cadId
179179
let addrUsed = cadIsUsed
180-
let addrChangeAddress = cadIsChange
180+
addrChangeAddress = cadIsChange
181+
addrOwnership = addressOwnershipFromUsed addrUsed
181182
return V1.WalletAddress{..}
183+
where
184+
addressOwnershipFromUsed :: Bool -> V1 V1.AddressOwnership
185+
addressOwnershipFromUsed used =
186+
V1 $ if used then V1.AddressIsOurs else V1.AddressAmbiguousOwnership
182187

183188
-- | Migrates to a V1 `SyncProgress` by computing the percentage as
184189
-- coded here: https://github.com/input-output-hk/daedalus/blob/master/app/stores/NetworkStatusStore.js#L108

src/Cardano/Wallet/API/V1/Swagger.hs

+17-3
Original file line numberDiff line numberDiff line change
@@ -1051,6 +1051,19 @@ the `spendingPassword` field to `null` as that won't influence the fee
10511051
calculation.
10521052
|]
10531053

1054+
getAddressDescription :: Text
1055+
getAddressDescription = [text|
1056+
The previous version of this endpoint failed with an HTTP error when the given
1057+
address was unknown to the wallet.
1058+
1059+
This was misleading since an address that is unknown to the wallet may still
1060+
belong to the wallet (since it could be part of a pending transaction in
1061+
another instance of the same wallet).
1062+
1063+
To reflect this, the V1 endpoint does not fail when an address is not recognised
1064+
and returns a new field 'is-ours' which indicates either that an address is ours,
1065+
or that it is 'not-recognised' (which may mean 'not recognised yet').
1066+
|]
10541067

10551068
--
10561069
-- The API
@@ -1083,7 +1096,8 @@ api (compileInfo, curSoftwareVersion) walletAPI mkDescription = toSwagger wallet
10831096
, deSoftwareVersion = fromString $ show curSoftwareVersion
10841097
}
10851098
& info.license ?~ ("MIT" & url ?~ URL "https://raw.githubusercontent.com/input-output-hk/cardano-sl/develop/lib/LICENSE")
1086-
& paths %~ (POST, "/api/internal/apply-update") `setDescription` applyUpdateDescription
1087-
& paths %~ (POST, "/api/internal/postpone-update") `setDescription` postponeUpdateDescription
1099+
& paths %~ (POST, "/api/internal/apply-update") `setDescription` applyUpdateDescription
1100+
& paths %~ (POST, "/api/internal/postpone-update") `setDescription` postponeUpdateDescription
10881101
& paths %~ (DELETE, "/api/internal/reset-wallet-state") `setDescription` resetWalletStateDescription
1089-
& paths %~ (POST, "/api/v1/transactions/fees") `setDescription` estimateFeesDescription
1102+
& paths %~ (POST, "/api/v1/transactions/fees") `setDescription` estimateFeesDescription
1103+
& paths %~ (GET, "/api/v1/addresses/{address}") `setDescription` getAddressDescription

src/Cardano/Wallet/API/V1/Types.hs

+39-1
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module Cardano.Wallet.API.V1.Types (
4646
, NewExternalWallet (..)
4747
, WalletAndTxHistory (..)
4848
-- * Addresses
49+
, AddressOwnership (..)
4950
, AddressIndex
5051
, AddressValidity (..)
5152
, AddressAsBase58
@@ -183,7 +184,7 @@ import Cardano.Wallet.API.V1.Swagger.Example (Example, example,
183184
genExample)
184185
import Cardano.Wallet.Orphans.Aeson ()
185186
import Cardano.Wallet.Types.UtxoStatistics
186-
import Cardano.Wallet.Util (showApiUtcTime)
187+
import Cardano.Wallet.Util (mkJsonKey, showApiUtcTime)
187188

188189
import qualified Pos.Binary.Class as Bi
189190
import qualified Pos.Chain.Txp as Txp
@@ -265,6 +266,11 @@ genericSchemaDroppingPrefix prfx extraDoc proxy = do
265266
_ -> err
266267

267268

269+
optsADTCamelCase :: A.Options
270+
optsADTCamelCase =
271+
defaultOptions { A.constructorTagModifier = mkJsonKey }
272+
273+
268274
--
269275
-- Versioning
270276
--
@@ -1202,6 +1208,35 @@ instance BuildableSafeGen AddressValidity where
12021208
buildSafeGen _ AddressValidity{..} =
12031209
bprint ("{ valid="%build%" }") isValid
12041210

1211+
-- | An address is either recognised as "ours" or not. An address that is not
1212+
-- recognised may still be ours e.g. an address generated by another wallet instance
1213+
-- will not be considered "ours" until the relevant transaction is confirmed.
1214+
--
1215+
-- In other words, `AddressAmbiguousOwnership` makes an inconclusive statement about
1216+
-- an address, whereas `AddressOwnership` is unambiguous.
1217+
data AddressOwnership
1218+
= AddressIsOurs
1219+
| AddressAmbiguousOwnership
1220+
deriving (Show, Eq, Generic, Ord)
1221+
1222+
instance ToJSON (V1 AddressOwnership) where
1223+
toJSON = genericToJSON optsADTCamelCase . unV1
1224+
1225+
instance FromJSON (V1 AddressOwnership) where
1226+
parseJSON = fmap V1 . genericParseJSON optsADTCamelCase
1227+
1228+
instance ToSchema (V1 AddressOwnership) where
1229+
declareNamedSchema _ =
1230+
pure $ NamedSchema (Just "V1AddressOwnership") $ mempty
1231+
& type_ .~ SwaggerString
1232+
& enum_ ?~ ["isOurs", "ambiguousOwnership"]
1233+
1234+
instance Arbitrary (V1 AddressOwnership) where
1235+
arbitrary = fmap V1 $ oneof
1236+
[ pure AddressIsOurs
1237+
, pure AddressAmbiguousOwnership
1238+
]
1239+
12051240
--------------------------------------------------------------------------------
12061241
-- Accounts
12071242
--------------------------------------------------------------------------------
@@ -1211,6 +1246,7 @@ data WalletAddress = WalletAddress
12111246
{ addrId :: !(V1 Core.Address)
12121247
, addrUsed :: !Bool
12131248
, addrChangeAddress :: !Bool
1249+
, addrOwnership :: !(V1 AddressOwnership)
12141250
} deriving (Show, Eq, Generic, Ord)
12151251

12161252
deriveJSON Serokell.defaultOptions ''WalletAddress
@@ -1221,12 +1257,14 @@ instance ToSchema WalletAddress where
12211257
& ("id" --^ "Actual address.")
12221258
& ("used" --^ "True if this address has been used.")
12231259
& ("changeAddress" --^ "True if this address stores change from a previous transaction.")
1260+
& ("ownership" --^ "'isOurs' if this address is recognised as ours, 'ambiguousOwnership' if the node doesn't have information to make a unambiguous statement.")
12241261
)
12251262

12261263
instance Arbitrary WalletAddress where
12271264
arbitrary = WalletAddress <$> arbitrary
12281265
<*> arbitrary
12291266
<*> arbitrary
1267+
<*> arbitrary
12301268

12311269
newtype AccountIndex = AccountIndex { getAccIndex :: Word32 }
12321270
deriving (Show, Eq, Ord, Generic)

src/Cardano/Wallet/Util.hs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ headToLower [] = Nothing
2727
headToLower (x:xs) = Just $ toLower x : xs
2828

2929
stripFieldPrefix :: String -> String
30-
stripFieldPrefix = dropWhile (not . isUpper)
30+
stripFieldPrefix = dropWhile (not . isUpper) . drop 1
3131

3232
mkJsonKey :: String -> String
3333
mkJsonKey s = fromMaybe s . headToLower $ stripFieldPrefix s

src/Cardano/Wallet/WalletLayer/Kernel/Addresses.hs

+3-4
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ createAddress wallet
5151
-- | Creates a new 'WalletAddress'. As this is a brand new, fresh Address,
5252
-- it's fine to have 'False' for both 'isUsed' and 'isChange'.
5353
mkAddress :: Address -> WalletAddress
54-
mkAddress addr = WalletAddress (V1 addr) False False
54+
mkAddress addr = WalletAddress (V1 addr) False False (V1 V1.AddressIsOurs)
5555

5656

5757

@@ -179,11 +179,10 @@ validateAddress rawText db = runExcept $ do
179179
-- If the address is unknown to the wallet, it's possible that it's ours
180180
-- but not yet used (at least as far as we know, it may be pending in
181181
-- another instance of the same wallet of course), or it may be that
182-
-- it's not even ours. In both cases we return that it is "not used"
183-
-- (here) and "not a change address" (here). In the future we may want
184-
-- to extend this endpoint with an "is ours" field.
182+
-- it's not even ours. In both cases we return 'V1.AddressAmbiguousOwnership'.
185183
return V1.WalletAddress {
186184
addrId = V1 cardanoAddress
187185
, addrUsed = False
188186
, addrChangeAddress = False
187+
, addrOwnership = (V1 V1.AddressAmbiguousOwnership)
189188
}

src/Cardano/Wallet/WalletLayer/Kernel/Conv.hs

+10-2
Original file line numberDiff line numberDiff line change
@@ -193,9 +193,17 @@ toAddress acc hdAddress =
193193
V1.WalletAddress (V1 cardanoAddress)
194194
(addressMeta ^. addressMetaIsUsed)
195195
(addressMeta ^. addressMetaIsChange)
196+
(V1 addressOwnership)
196197
where
197-
cardanoAddress = hdAddress ^. HD.hdAddressAddress . fromDb
198-
addressMeta = acc ^. HD.hdAccountState . HD.hdAccountStateCurrent . cpAddressMeta cardanoAddress
198+
cardanoAddress = hdAddress ^. HD.hdAddressAddress . fromDb
199+
addressMeta = acc ^. HD.hdAccountState . HD.hdAccountStateCurrent . cpAddressMeta cardanoAddress
200+
-- NOTE
201+
-- In this particular case, the address had to be known by us. As a matter
202+
-- of fact, to construct a 'WalletAddress', we have to be aware of pieces
203+
-- of informations we can only have if the address is ours (like the
204+
-- parent's account derivation index required to build the 'HdAddress' in
205+
-- a first place)!
206+
addressOwnership = V1.AddressIsOurs
199207

200208
-- | Converts a Kernel 'HdAddress' into a Cardano 'Address'.
201209
toCardanoAddress :: HD.HdAddress -> Address

test/MarshallingSpec.hs

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ spec = parallel $ describe "Marshalling & Unmarshalling" $ do
8787
aesonRoundtripProp @SyncProgress Proxy
8888
aesonRoundtripProp @SyncThroughput Proxy
8989
aesonRoundtripProp @AccountIndex Proxy
90+
aesonRoundtripProp @(V1 AddressOwnership) Proxy
9091

9192
-- HttpApiData roundtrips
9293
httpApiDataRoundtripProp @AccountIndex Proxy

test/unit/Test/Spec/Addresses.hs

+1
Original file line numberDiff line numberDiff line change
@@ -534,6 +534,7 @@ spec = describe "Addresses" $ do
534534
addrId = V1.V1 randomAddr
535535
, addrUsed = False
536536
, addrChangeAddress = False
537+
, addrOwnership = V1.V1 V1.AddressAmbiguousOwnership
537538
}
538539
withAddressFixtures 1 $ \_ layer _ _ -> do
539540
res <- WalletLayer.validateAddress layer (sformat build randomAddr)

0 commit comments

Comments
 (0)