Skip to content
Permalink
Browse files

GenChange instance

  • Loading branch information...
paweljakubas committed Aug 13, 2019
1 parent a9e69c2 commit 3d6dd2db8a0ed86b70d03a4df0df1b024647bd55
@@ -284,7 +284,7 @@ data WalletLayer s t k = WalletLayer
-- ^ List the wallet's UTxO statistics.

, signTx
:: (Show s, NFData s, IsOwned s k, GenChange s)
:: (Show s, NFData s, IsOwned s k, GenChange t s)
=> WalletId
-> Passphrase "encryption"
-> CoinSelection
@@ -857,15 +857,15 @@ newWalletLayer tracer bp db nw tl = do
txTime = slotStartTime epochLength slotLength startTime

_signTx
:: (Show s, NFData s, IsOwned s k, GenChange s)
:: (Show s, NFData s, IsOwned s k, GenChange t s)
=> WalletId
-> Passphrase "encryption"
-> CoinSelection
-> ExceptT ErrSignTx IO (Tx t, TxMeta, [TxWitness])
_signTx wid pwd (CoinSelection ins outs chgs) = DB.withLock db $ do
(w, _) <- withExceptT ErrSignTxNoSuchWallet $ _readWallet wid
let (changeOuts, s') = flip runState (getState w) $ forM chgs $ \c -> do
addr <- state genChange
addr <- state (genChange @t)
return $ TxOut addr c
allShuffledOuts <- liftIO $ shuffle (outs ++ changeOuts)
withRootKey wid pwd ErrSignTxWithRootKey $ \xprv -> do
@@ -82,7 +82,7 @@ class IsOurs s => IsOwned s key where
-- abstractions allows for defining an heuristic to pick new change address. For
-- instance, in BIP-44, change addresses belong to a particular change chain
-- (also called "Internal Chain").
class GenChange s where
class GenChange target s where
genChange
:: s
-> (Address, s)
@@ -20,6 +20,7 @@ module Cardano.Wallet.Primitive.AddressDiscovery.Random
RndState (..)
, emptyChangeState
, updateChangeState
, incrementChangeState
, ChangeState (..)
) where

@@ -28,16 +29,11 @@ import Prelude
import Cardano.Byron.Codec.Cbor
( decodeAddressDerivationPath, decodeAddressPayload, deserialiseCbor )
import Cardano.Wallet.Primitive.AddressDerivation
( Depth (..), DerivationType (..), Index (..), XPrv )
( Depth (..), DerivationType (..), Index (..), Passphrase (..), XPrv )
import Cardano.Wallet.Primitive.AddressDerivation.Random
( RndKey (..), deriveAccountPrivateKey, deriveAddressPrivateKey )
import Cardano.Wallet.Primitive.AddressDiscovery
( CompareDiscovery (..)
, GenChange (..)
, IsOurs (..)
, IsOwned (..)
, KnownAddresses (..)
)
( CompareDiscovery (..), IsOurs (..), IsOwned (..), KnownAddresses (..) )
import Cardano.Wallet.Primitive.Types
( Address (..) )
import Control.DeepSeq
@@ -48,6 +44,8 @@ import Data.Maybe
( isJust )
import Data.Set
( Set )
import Data.Word
( Word32 )
import GHC.Generics
( Generic )

@@ -89,30 +87,35 @@ addressToPath (Address addr) key = do
-- already used addresses (not only change addresses but ever known, ie. generated)
data ChangeState = ChangeState
{ forbidenAddresses :: Set Address
, nextIndex :: (Index 'Soft 'AccountK, Index 'Soft 'AddressK)
, passphrase :: Passphrase "encryption"
, nextIndex :: (Index 'Hardened 'AccountK, Index 'Hardened 'AddressK)
} deriving (Generic, Show, Eq)

instance NFData ChangeState

emptyChangeState :: ChangeState
emptyChangeState = ChangeState Set.empty (minBound, minBound)
emptyChangeState = ChangeState Set.empty (Passphrase "") (minBound, minBound)

incrementIndices :: Word32 -> Word32 -> (Word32, Word32)
incrementIndices accIx addrIx =
if (addrIx == maxBound && accIx == maxBound) then
(minBound, minBound)
else if (addrIx == maxBound) then
(accIx + 1, minBound)
else (accIx, addrIx + 1)

updateChangeState
:: Address
-> ChangeState
-> ChangeState
updateChangeState addr (ChangeState addrs (Index accIx, Index addrIx)) =
let (accIx', addrIx') =
if (addrIx == maxBound && accIx == maxBound) then
(minBound, minBound)
else if (addrIx == maxBound) then
(accIx + 1, minBound)
else (accIx, addrIx + 1)
in ChangeState (Set.insert addr addrs) (Index accIx', Index addrIx')


instance GenChange RndState where
genChange s = (error "GenChange RndState unimplemented", s)
updateChangeState addr (ChangeState addrs pwd (Index accIx, Index addrIx)) =
let (accIx', addrIx') = incrementIndices accIx addrIx
in ChangeState (Set.insert addr addrs) pwd (Index accIx', Index addrIx')

incrementChangeState :: ChangeState -> ChangeState
incrementChangeState (ChangeState addrs pwd (Index accIx, Index addrIx)) =
let (accIx', addrIx') = incrementIndices accIx addrIx
in ChangeState addrs pwd (Index accIx', Index addrIx')


-- Unlike sequential derivation, we can't derive an order from the index only
@@ -417,7 +417,7 @@ instance KeyToAddress t SeqKey => IsOurs (SeqState t) where
in
(ixs' `deepseq` ours `deepseq` ours, SeqState s1' s2' ixs')

instance KeyToAddress t SeqKey => GenChange (SeqState t) where
instance KeyToAddress t SeqKey => GenChange t (SeqState t) where
-- | We pick indexes in sequence from the first known available index (i.e.
-- @length addrs - gap@) but we do not generate _new change addresses_. As a
-- result, we can't generate more than @gap@ _pending_ change addresses and
@@ -39,6 +39,7 @@ library
, cardano-crypto
, cardano-wallet-core
, cborg
, containers
, cryptonite
, deepseq
, either
@@ -41,11 +41,20 @@ import Cardano.Wallet.DB.Sqlite
import Cardano.Wallet.HttpBridge.Environment
( KnownNetwork (..), Network (Mainnet, Testnet), protocolMagic )
import Cardano.Wallet.Primitive.AddressDerivation
( KeyToAddress (..), WalletKey (..) )
( Index (..), KeyToAddress (..), WalletKey (..) )
import Cardano.Wallet.Primitive.AddressDerivation.Random
( RndKey (..) )
( RndKey (..), deriveAccountPrivateKey, deriveAddressPrivateKey )
import Cardano.Wallet.Primitive.AddressDerivation.Sequential
( SeqKey )
import Cardano.Wallet.Primitive.AddressDiscovery
( GenChange (..) )
import Cardano.Wallet.Primitive.AddressDiscovery.Random
( ChangeState (..)
, RndState (..)
, emptyChangeState
, incrementChangeState
, updateChangeState
)
import Cardano.Wallet.Primitive.Fee
( FeePolicy (..) )
import Cardano.Wallet.Primitive.Types
@@ -86,6 +95,7 @@ import qualified Codec.CBOR.Read as CBOR
import qualified Codec.CBOR.Write as CBOR
import qualified Data.ByteArray as BA
import qualified Data.ByteString.Lazy as BL
import qualified Data.Set as Set
import qualified Data.Text.Encoding as T

-- | A type representing the http-bridge as a network target. This has an
@@ -139,6 +149,29 @@ instance KeyToAddress (HttpBridge 'Mainnet) RndKey where
(acctIx, addrIx) = derivationPath k
pwd = payloadPassphrase k


instance GenChange (HttpBridge n) RndState where
genChange rs = searchAddr @n rs

searchAddr
:: forall (n :: Network). RndState
-> (Address, RndState)
searchAddr rs@(RndState rk cs@(ChangeState forbidenAddrs _ _)) =
if (Set.member (deriveAddrKey rs) forbidenAddrs) then
searchAddr $ RndState rk (incrementChangeState cs)
else
let addr' = deriveAddrKey rs
in (addr', RndState rk $ updateChangeState addr' cs)

deriveAddrKey
:: forall (n :: Network). RndState
-> Address
deriveAddrKey (RndState rk (ChangeState _ pwd (accIx, addrIx))) =
let accKey = deriveAccountPrivateKey pwd rk accIx
addrKey = publicKey $ deriveAddressPrivateKey pwd accKey addrIx
in keyToAddress @(HttpBridge n) addrKey


-- | Encode an 'Address' to a human-readable format, in this case
--
-- [Base58](https://en.wikipedia.org/wiki/Base58)

Some generated files are not rendered by default. Learn more.

0 comments on commit 3d6dd2d

Please sign in to comment.
You can’t perform that action at this time.