Skip to content

Commit

Permalink
Merge #2177
Browse files Browse the repository at this point in the history
2177: Create API address derivation path type. r=jonathanknowles a=jonathanknowles

# Issue Number

#2174 

# Overview

This PR adds a new type `ApiAddressDerivationPath`. A value of this type serializes to a JSON **list**.

For example, the path `1852H/1815H/0H/0/0` has the following JSON representation:

```json
[
  {
    "derivation_index": 1852,
    "derivation_type": "hardened"
  },
  {
    "derivation_index": 1815,
    "derivation_type": "hardened"
  },
  {
    "derivation_index": 0,
    "derivation_type": "hardened"
  },
  {
    "derivation_index": 0,
    "derivation_type": "soft"
  },
  {
    "derivation_index": 0,
    "derivation_type": "soft"
  }
]
```

Each entry in the list has the following pair of fields:

| Field name | Type | Domain |
| -- | -- | -- |
| `derivation_index` | natural number | `[ 0 .. 2^31 − 1 ]` |
| `derivation_type` | enumeration | `[ hardened \| soft ]` | 

# Details

This PR also:

- Adds JSON roundtrip tests for `ApiAddressDerivationPath`.
- Adds a missing JSON roundtrip test for the `ApiAddress` type.
- Fixes the upper bound of `addressIndex` to be `2^32 - 1` rather than `2^32`.
- Adds a matching swagger definition for `ApiAddressDerivationPath`.

Co-authored-by: Jonathan Knowles <jonathan.knowles@iohk.io>
  • Loading branch information
iohk-bors[bot] and jonathanknowles committed Sep 25, 2020
2 parents f5f689e + 099e9fd commit d5d2898
Show file tree
Hide file tree
Showing 8 changed files with 973 additions and 6 deletions.
77 changes: 74 additions & 3 deletions lib/core/src/Cardano/Wallet/Api/Types.hs
Expand Up @@ -8,6 +8,7 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedLabels #-}
Expand Down Expand Up @@ -41,6 +42,10 @@ module Cardano.Wallet.Api.Types

-- * API Types
, ApiAddress (..)
, ApiAddressDerivationPath (..)
, ApiAddressDerivationSegment (..)
, ApiAddressDerivationType (..)
, ApiRelativeAddressIndex (..)
, ApiEpochInfo (..)
, ApiSelectCoinsData (..)
, ApiCoinSelection (..)
Expand Down Expand Up @@ -142,7 +147,6 @@ import Cardano.Mnemonic
)
import Cardano.Wallet.Primitive.AddressDerivation
( Depth (..)
, DerivationType (..)
, Index (..)
, NetworkDiscriminant (..)
, Passphrase (..)
Expand Down Expand Up @@ -198,7 +202,7 @@ import Control.Applicative
import Control.Arrow
( left )
import Control.Monad
( guard, (>=>) )
( guard, (<=<), (>=>) )
import Data.Aeson
( FromJSON (..)
, SumEncoding (..)
Expand Down Expand Up @@ -272,6 +276,7 @@ import Web.HttpApiData
( FromHttpApiData (..), ToHttpApiData (..) )

import qualified Cardano.Crypto.Wallet as CC
import qualified Cardano.Wallet.Primitive.AddressDerivation as AD
import qualified Data.Aeson as Aeson
import qualified Data.Aeson.Types as Aeson
import qualified Data.ByteString.Lazy as BL
Expand Down Expand Up @@ -357,6 +362,33 @@ data ApiAddress (n :: NetworkDiscriminant) = ApiAddress
, state :: !(ApiT AddressState)
} deriving (Eq, Generic, Show)

newtype ApiAddressDerivationPath = ApiAddressDerivationPath
{ unApiAddressDerivationPath :: NonEmpty ApiAddressDerivationSegment
} deriving (Eq, Generic, Show)

data ApiAddressDerivationSegment = ApiAddressDerivationSegment
{ derivationIndex :: !ApiRelativeAddressIndex
, derivationType :: !ApiAddressDerivationType
} deriving (Eq, Generic, Show)

-- | Represents a type of address derivation.
--
-- Note that the values of this type are a strict subset of those provided
-- by 'DerivationType' from 'Cardano.Wallet.Primitive.AddressDerivation'.
--
data ApiAddressDerivationType
= Hardened
| Soft
deriving (Bounded, Enum, Eq, Generic, Show)

-- | Represents a relative address index.
--
-- The range of this type is exactly half that of a 'Word32'.
--
newtype ApiRelativeAddressIndex = ApiRelativeAddressIndex
{ unApiRelativeAddressIndex :: Word31
} deriving (Bounded, Enum, Eq, Generic, Show)

data ApiEpochInfo = ApiEpochInfo
{ epochNumber :: !(ApiT EpochNo)
, epochStartTime :: !UTCTime
Expand Down Expand Up @@ -648,7 +680,7 @@ newtype ApiNetworkClock = ApiNetworkClock

data ApiPostRandomAddressData = ApiPostRandomAddressData
{ passphrase :: !(ApiT (Passphrase "lenient"))
, addressIndex :: !(Maybe (ApiT (Index 'Hardened 'AddressK)))
, addressIndex :: !(Maybe (ApiT (Index 'AD.Hardened 'AddressK)))
} deriving (Eq, Generic, Show)

data ApiWalletMigrationPostData (n :: NetworkDiscriminant) (s :: Symbol) =
Expand Down Expand Up @@ -903,6 +935,45 @@ instance DecodeAddress n => FromJSON (ApiAddress n) where
instance EncodeAddress n => ToJSON (ApiAddress n) where
toJSON = genericToJSON defaultRecordTypeOptions

instance ToJSON ApiAddressDerivationPath where
toJSON = toJSON . unApiAddressDerivationPath
instance FromJSON ApiAddressDerivationPath where
parseJSON = fmap ApiAddressDerivationPath . parseJSON

instance ToJSON ApiAddressDerivationSegment where
toJSON = genericToJSON defaultRecordTypeOptions
instance FromJSON ApiAddressDerivationSegment where
parseJSON = genericParseJSON defaultRecordTypeOptions

instance ToJSON (ApiAddressDerivationType) where
toJSON = genericToJSON defaultSumTypeOptions
instance FromJSON (ApiAddressDerivationType) where
parseJSON = genericParseJSON defaultSumTypeOptions

instance ToJSON ApiRelativeAddressIndex where
toJSON = toJSON . fromEnum
instance FromJSON ApiRelativeAddressIndex where
parseJSON = eitherToParser . integerToIndex <=< parseJSON
where
integerToIndex :: Integer -> Either String ApiRelativeAddressIndex
integerToIndex i
| i < minIntegerBound = Left errorMessage
| i > maxIntegerBound = Left errorMessage
| otherwise = Right
$ ApiRelativeAddressIndex
$ fromIntegral i

minIntegerBound = toInteger $ unApiRelativeAddressIndex minBound
maxIntegerBound = toInteger $ unApiRelativeAddressIndex maxBound

errorMessage = mconcat
[ "A relative address index must be a natural number between "
, show minIntegerBound
, " and "
, show maxIntegerBound
, "."
]

instance FromJSON ApiEpochInfo where
parseJSON = genericParseJSON defaultRecordTypeOptions
instance ToJSON ApiEpochInfo where
Expand Down

0 comments on commit d5d2898

Please sign in to comment.