{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Cardano.Wallet.Primitive.AddressDiscovery
(
IsOurs(..)
, IsOwned(..)
, GenChange(..)
, CompareDiscovery(..)
, KnownAddresses(..)
, GetPurpose (..)
, GetAccount (..)
, coinTypeAda
, MaybeLight (..)
, DiscoverTxs (..)
, PendingIxs
, emptyPendingIxs
, pendingIxsToList
, pendingIxsFromList
, nextChangeIndex
, updatePendingIxs
) where
import Prelude
import Cardano.Crypto.Wallet
( XPrv, XPub )
import Cardano.Wallet.Primitive.AddressDerivation
( Depth (..)
, DerivationIndex (..)
, DerivationType (..)
, Index (..)
, KeyFingerprint (..)
, RewardAccount
)
import Cardano.Wallet.Primitive.BlockSummary
( ChainEvents )
import Cardano.Wallet.Primitive.Passphrase
( Passphrase (..) )
import Cardano.Wallet.Primitive.Types.Address
( Address (..), AddressState (..) )
import Cardano.Wallet.Util
( invariant )
import Control.DeepSeq
( NFData )
import Data.Kind
( Type )
import Data.List.NonEmpty
( NonEmpty )
import GHC.Generics
( Generic )
import qualified Cardano.Wallet.Address.Pool as AddressPool
import qualified Data.List as L
class IsOurs s entity where
isOurs
:: entity
-> s
-> (Maybe (NonEmpty DerivationIndex), s)
class IsOurs s Address => IsOwned s key where
isOwned
:: s
-> (key 'RootK XPrv, Passphrase "encryption")
-> Address
-> Maybe (key 'AddressK XPrv, Passphrase "encryption")
class GenChange s where
type ArgGenChange s :: Type
genChange
:: ArgGenChange s
-> s
-> (Address, s)
class CompareDiscovery s where
compareDiscovery
:: s
-> Address
-> Address
-> Ordering
class KnownAddresses s where
knownAddresses
:: s
-> [(Address, AddressState, NonEmpty DerivationIndex)]
coinTypeAda :: Index 'Hardened 'CoinTypeK
coinTypeAda :: Index 'Hardened 'CoinTypeK
coinTypeAda = Int -> Index 'Hardened 'CoinTypeK
forall a. Enum a => Int -> a
toEnum Int
0x80000717
class GetPurpose (key :: Depth -> Type -> Type) where
getPurpose :: Index 'Hardened 'PurposeK
class GetAccount s (key :: Depth -> Type -> Type) | s -> key where
getAccount :: s -> key 'AccountK XPub
class MaybeLight s where
maybeDiscover :: Maybe (LightDiscoverTxs s)
type LightDiscoverTxs s =
DiscoverTxs (Either Address RewardAccount) ChainEvents s
newtype DiscoverTxs addr txs s = DiscoverTxs
{ DiscoverTxs addr txs s
-> forall (m :: * -> *).
Monad m =>
(addr -> m txs) -> s -> m (txs, s)
discoverTxs
:: forall m. Monad m
=> (addr -> m txs) -> s -> m (txs, s)
}
newtype PendingIxs k = PendingIxs
{ PendingIxs k -> [Index 'Soft k]
pendingIxsToList :: [Index 'Soft k] }
deriving stock ((forall x. PendingIxs k -> Rep (PendingIxs k) x)
-> (forall x. Rep (PendingIxs k) x -> PendingIxs k)
-> Generic (PendingIxs k)
forall x. Rep (PendingIxs k) x -> PendingIxs k
forall x. PendingIxs k -> Rep (PendingIxs k) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall (k :: Depth) x. Rep (PendingIxs k) x -> PendingIxs k
forall (k :: Depth) x. PendingIxs k -> Rep (PendingIxs k) x
$cto :: forall (k :: Depth) x. Rep (PendingIxs k) x -> PendingIxs k
$cfrom :: forall (k :: Depth) x. PendingIxs k -> Rep (PendingIxs k) x
Generic, Int -> PendingIxs k -> ShowS
[PendingIxs k] -> ShowS
PendingIxs k -> String
(Int -> PendingIxs k -> ShowS)
-> (PendingIxs k -> String)
-> ([PendingIxs k] -> ShowS)
-> Show (PendingIxs k)
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
forall (k :: Depth). Int -> PendingIxs k -> ShowS
forall (k :: Depth). [PendingIxs k] -> ShowS
forall (k :: Depth). PendingIxs k -> String
showList :: [PendingIxs k] -> ShowS
$cshowList :: forall (k :: Depth). [PendingIxs k] -> ShowS
show :: PendingIxs k -> String
$cshow :: forall (k :: Depth). PendingIxs k -> String
showsPrec :: Int -> PendingIxs k -> ShowS
$cshowsPrec :: forall (k :: Depth). Int -> PendingIxs k -> ShowS
Show, PendingIxs k -> PendingIxs k -> Bool
(PendingIxs k -> PendingIxs k -> Bool)
-> (PendingIxs k -> PendingIxs k -> Bool) -> Eq (PendingIxs k)
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
forall (k :: Depth). PendingIxs k -> PendingIxs k -> Bool
/= :: PendingIxs k -> PendingIxs k -> Bool
$c/= :: forall (k :: Depth). PendingIxs k -> PendingIxs k -> Bool
== :: PendingIxs k -> PendingIxs k -> Bool
$c== :: forall (k :: Depth). PendingIxs k -> PendingIxs k -> Bool
Eq)
instance NFData (PendingIxs k)
emptyPendingIxs :: PendingIxs k
emptyPendingIxs :: PendingIxs k
emptyPendingIxs = [Index 'Soft k] -> PendingIxs k
forall (k :: Depth). [Index 'Soft k] -> PendingIxs k
PendingIxs [Index 'Soft k]
forall a. Monoid a => a
mempty
pendingIxsFromList :: [Index 'Soft k] -> PendingIxs k
pendingIxsFromList :: [Index 'Soft k] -> PendingIxs k
pendingIxsFromList = [Index 'Soft k] -> PendingIxs k
forall (k :: Depth). [Index 'Soft k] -> PendingIxs k
PendingIxs ([Index 'Soft k] -> PendingIxs k)
-> ([Index 'Soft k] -> [Index 'Soft k])
-> [Index 'Soft k]
-> PendingIxs k
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Index 'Soft k] -> [Index 'Soft k]
forall a. [a] -> [a]
reverse ([Index 'Soft k] -> [Index 'Soft k])
-> ([Index 'Soft k] -> [Index 'Soft k])
-> [Index 'Soft k]
-> [Index 'Soft k]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Index 'Soft k] -> Index 'Soft k)
-> [[Index 'Soft k]] -> [Index 'Soft k]
forall a b. (a -> b) -> [a] -> [b]
map [Index 'Soft k] -> Index 'Soft k
forall a. [a] -> a
head ([[Index 'Soft k]] -> [Index 'Soft k])
-> ([Index 'Soft k] -> [[Index 'Soft k]])
-> [Index 'Soft k]
-> [Index 'Soft k]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Index 'Soft k] -> [[Index 'Soft k]]
forall a. Eq a => [a] -> [[a]]
L.group ([Index 'Soft k] -> [[Index 'Soft k]])
-> ([Index 'Soft k] -> [Index 'Soft k])
-> [Index 'Soft k]
-> [[Index 'Soft k]]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Index 'Soft k] -> [Index 'Soft k]
forall a. Ord a => [a] -> [a]
L.sort
nextChangeIndex
:: forall (key :: Depth -> Type -> Type) k.
AddressPool.Pool (KeyFingerprint "payment" key) (Index 'Soft k)
-> PendingIxs k
-> (Index 'Soft k, PendingIxs k)
nextChangeIndex :: Pool (KeyFingerprint "payment" key) (Index 'Soft k)
-> PendingIxs k -> (Index 'Soft k, PendingIxs k)
nextChangeIndex Pool (KeyFingerprint "payment" key) (Index 'Soft k)
pool (PendingIxs [Index 'Soft k]
ixs) =
let
poolLen :: Int
poolLen = Pool (KeyFingerprint "payment" key) (Index 'Soft k) -> Int
forall addr ix. Pool addr ix -> Int
AddressPool.size Pool (KeyFingerprint "payment" key) (Index 'Soft k)
pool
(Index 'Soft k
firstUnused, Index 'Soft k
lastUnused) =
( Int -> Index 'Soft k
forall a. Enum a => Int -> a
toEnum (Int -> Index 'Soft k) -> Int -> Index 'Soft k
forall a b. (a -> b) -> a -> b
$ Int
poolLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Pool (KeyFingerprint "payment" key) (Index 'Soft k) -> Int
forall addr ix. Pool addr ix -> Int
AddressPool.gap Pool (KeyFingerprint "payment" key) (Index 'Soft k)
pool
, Int -> Index 'Soft k
forall a. Enum a => Int -> a
toEnum (Int -> Index 'Soft k) -> Int -> Index 'Soft k
forall a b. (a -> b) -> a -> b
$ Int
poolLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
)
(Index 'Soft k
ix, PendingIxs k
ixs') = case [Index 'Soft k]
ixs of
[] ->
(Index 'Soft k
firstUnused, [Index 'Soft k] -> PendingIxs k
forall (k :: Depth). [Index 'Soft k] -> PendingIxs k
PendingIxs [Index 'Soft k
firstUnused])
Index 'Soft k
h:[Index 'Soft k]
_ | [Index 'Soft k] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [Index 'Soft k]
ixs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Pool (KeyFingerprint "payment" key) (Index 'Soft k) -> Int
forall addr ix. Pool addr ix -> Int
AddressPool.gap Pool (KeyFingerprint "payment" key) (Index 'Soft k)
pool ->
(Index 'Soft k -> Index 'Soft k
forall a. Enum a => a -> a
succ Index 'Soft k
h, [Index 'Soft k] -> PendingIxs k
forall (k :: Depth). [Index 'Soft k] -> PendingIxs k
PendingIxs (Index 'Soft k -> Index 'Soft k
forall a. Enum a => a -> a
succ Index 'Soft k
hIndex 'Soft k -> [Index 'Soft k] -> [Index 'Soft k]
forall a. a -> [a] -> [a]
:[Index 'Soft k]
ixs))
Index 'Soft k
h:[Index 'Soft k]
q ->
(Index 'Soft k
h, [Index 'Soft k] -> PendingIxs k
forall (k :: Depth). [Index 'Soft k] -> PendingIxs k
PendingIxs ([Index 'Soft k]
q[Index 'Soft k] -> [Index 'Soft k] -> [Index 'Soft k]
forall a. [a] -> [a] -> [a]
++[Index 'Soft k
h]))
in
String
-> (Index 'Soft k, PendingIxs k)
-> ((Index 'Soft k, PendingIxs k) -> Bool)
-> (Index 'Soft k, PendingIxs k)
forall a. HasCallStack => String -> a -> (a -> Bool) -> a
invariant String
"index is within first unused and last unused" (Index 'Soft k
ix, PendingIxs k
ixs')
(\(Index 'Soft k
i,PendingIxs k
_) -> Index 'Soft k
i Index 'Soft k -> Index 'Soft k -> Bool
forall a. Ord a => a -> a -> Bool
>= Index 'Soft k
firstUnused Bool -> Bool -> Bool
&& Index 'Soft k
i Index 'Soft k -> Index 'Soft k -> Bool
forall a. Ord a => a -> a -> Bool
<= Index 'Soft k
lastUnused)
updatePendingIxs
:: Index 'Soft k
-> PendingIxs k
-> PendingIxs k
updatePendingIxs :: Index 'Soft k -> PendingIxs k -> PendingIxs k
updatePendingIxs Index 'Soft k
ix (PendingIxs [Index 'Soft k]
ixs) =
[Index 'Soft k] -> PendingIxs k
forall (k :: Depth). [Index 'Soft k] -> PendingIxs k
PendingIxs ([Index 'Soft k] -> PendingIxs k)
-> [Index 'Soft k] -> PendingIxs k
forall a b. (a -> b) -> a -> b
$ (Index 'Soft k -> Bool) -> [Index 'Soft k] -> [Index 'Soft k]
forall a. (a -> Bool) -> [a] -> [a]
L.filter (Index 'Soft k -> Index 'Soft k -> Bool
forall a. Ord a => a -> a -> Bool
> Index 'Soft k
ix) [Index 'Soft k]
ixs