Skip to content

Commit

Permalink
store seq-state derivation prefix in the database.
Browse files Browse the repository at this point in the history
  That prefixes tells us which account corresponds to which state and also, which purpose so can distinguish between Icarus and Shelley wallets. This will require a database migration
  which I'll add in a later commit.
  • Loading branch information
KtorZ committed Oct 6, 2020
1 parent 06797aa commit 2ef0111
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 154 deletions.
4 changes: 3 additions & 1 deletion lib/core/src/Cardano/Wallet.hs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ import Cardano.Wallet.Primitive.AddressDiscovery.Sequential
, defaultAddressPoolGap
, mkSeqStateFromRootXPrv
, mkUnboundedAddressPoolGap
, purposeBIP44
, shrinkPool
)
import Cardano.Wallet.Primitive.CoinSelection
Expand Down Expand Up @@ -604,7 +605,7 @@ createIcarusWallet
-> (k 'RootK XPrv, Passphrase "encryption")
-> ExceptT ErrWalletAlreadyExists IO WalletId
createIcarusWallet ctx wid wname credentials = db & \DBLayer{..} -> do
let s = mkSeqStateFromRootXPrv @n credentials $
let s = mkSeqStateFromRootXPrv @n credentials purposeBIP44 $
mkUnboundedAddressPoolGap 10000
let (hist, cp) = initWallet block0 gp s
let addrs = map address . concatMap (view #outputs . fst) $ hist
Expand All @@ -614,6 +615,7 @@ createIcarusWallet ctx wid wname credentials = db & \DBLayer{..} -> do
(shrinkPool @n (liftPaymentAddress @n) addrs g (Seq.externalPool s))
(Seq.pendingChangeIxs s)
(Seq.rewardAccountKey s)
(Seq.derivationPrefix s)
now <- lift getCurrentTime
let meta = WalletMetadata
{ name = wname
Expand Down
5 changes: 3 additions & 2 deletions lib/core/src/Cardano/Wallet/Api/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ import Cardano.Wallet.Primitive.AddressDiscovery.Sequential
, defaultAddressPoolGap
, mkSeqStateFromAccountXPub
, mkSeqStateFromRootXPrv
, purposeCIP1852
)
import Cardano.Wallet.Primitive.CoinSelection
( CoinSelection (..), changeBalance, inputBalance )
Expand Down Expand Up @@ -598,7 +599,7 @@ postShelleyWallet
-> WalletPostData
-> Handler ApiWallet
postShelleyWallet ctx generateKey body = do
let state = mkSeqStateFromRootXPrv (rootXPrv, pwd) g
let state = mkSeqStateFromRootXPrv (rootXPrv, pwd) purposeCIP1852 g
void $ liftHandler $ initWorker @_ @s @k ctx wid
(\wrk -> W.createWallet @(WorkerCtx ctx) @s @k wrk wid wName state)
(\wrk -> W.restoreWallet @(WorkerCtx ctx) @s @t @k wrk wid)
Expand Down Expand Up @@ -634,7 +635,7 @@ postAccountWallet
-> AccountPostData
-> Handler w
postAccountWallet ctx mkWallet liftKey coworker body = do
let state = mkSeqStateFromAccountXPub (liftKey accXPub) g
let state = mkSeqStateFromAccountXPub (liftKey accXPub) purposeCIP1852 g
void $ liftHandler $ initWorker @_ @s @k ctx wid
(\wrk -> W.createWallet @(WorkerCtx ctx) @s @k wrk wid wName state)
(\wrk -> W.restoreWallet @(WorkerCtx ctx) @s @t @k wrk wid)
Expand Down
5 changes: 3 additions & 2 deletions lib/core/src/Cardano/Wallet/DB/Sqlite.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,7 @@ instance
, seqStateInternalGap = iGap
, seqStateAccountXPub = serializeXPub accountXPub
, seqStateRewardXPub = serializeXPub (Seq.rewardAccountKey st)
, seqStateDerivationPrefix = Seq.derivationPrefix st
}
insertAddressPool @n wid sl intPool
insertAddressPool @n wid sl extPool
Expand All @@ -1587,13 +1588,13 @@ instance

selectState (wid, sl) = runMaybeT $ do
st <- MaybeT $ selectFirst [SeqStateWalletId ==. wid] []
let SeqState _ eGap iGap accountBytes rewardBytes = entityVal st
let SeqState _ eGap iGap accountBytes rewardBytes prefix = entityVal st
let accountXPub = unsafeDeserializeXPub accountBytes
let rewardXPub = unsafeDeserializeXPub rewardBytes
intPool <- lift $ selectAddressPool @n wid sl iGap accountXPub
extPool <- lift $ selectAddressPool @n wid sl eGap accountXPub
pendingChangeIxs <- lift $ selectSeqStatePendingIxs wid
pure $ Seq.SeqState intPool extPool pendingChangeIxs rewardXPub
pure $ Seq.SeqState intPool extPool pendingChangeIxs rewardXPub prefix

insertAddressPool
:: forall n k c. (PaymentAddress n k, Typeable c)
Expand Down
11 changes: 6 additions & 5 deletions lib/core/src/Cardano/Wallet/DB/Sqlite/TH.hs
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,12 @@ UTxO sql=utxo
-- Sequential scheme address discovery state
-- which does not belong to a particular checkpoint.
SeqState
seqStateWalletId W.WalletId sql=wallet_id
seqStateExternalGap W.AddressPoolGap sql=external_gap
seqStateInternalGap W.AddressPoolGap sql=internal_gap
seqStateAccountXPub B8.ByteString sql=account_xpub
seqStateRewardXPub B8.ByteString sql=reward_xpub
seqStateWalletId W.WalletId sql=wallet_id
seqStateExternalGap W.AddressPoolGap sql=external_gap
seqStateInternalGap W.AddressPoolGap sql=internal_gap
seqStateAccountXPub B8.ByteString sql=account_xpub
seqStateRewardXPub B8.ByteString sql=reward_xpub
seqStateDerivationPrefix W.DerivationPrefix sql=derivation_prefix

Primary seqStateWalletId
Foreign Wallet seq_state seqStateWalletId ! ON DELETE CASCADE
Expand Down
16 changes: 15 additions & 1 deletion lib/core/src/Cardano/Wallet/DB/Sqlite/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ import Cardano.Slotting.Slot
import Cardano.Wallet.Primitive.AddressDerivation
( AccountingStyle (..), Passphrase (..), PassphraseScheme (..) )
import Cardano.Wallet.Primitive.AddressDiscovery.Sequential
( AddressPoolGap (..), getAddressPoolGap, mkAddressPoolGap )
( AddressPoolGap (..)
, DerivationPrefix
, getAddressPoolGap
, mkAddressPoolGap
)
import Cardano.Wallet.Primitive.Types
( Address (..)
, AddressState (..)
Expand Down Expand Up @@ -631,3 +635,13 @@ instance PersistField AddressState where

instance PersistFieldSql AddressState where
sqlType _ = sqlType (Proxy @Text)

----------------------------------------------------------------------------
-- DerivationPrefix

instance PersistField DerivationPrefix where
toPersistValue = toPersistValue . toText
fromPersistValue = fromPersistValueFromText

instance PersistFieldSql DerivationPrefix where
sqlType _ = sqlType (Proxy @Text)
33 changes: 3 additions & 30 deletions lib/core/src/Cardano/Wallet/Primitive/AddressDerivation/Icarus.hs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import Cardano.Wallet.Primitive.AddressDerivation
import Cardano.Wallet.Primitive.AddressDiscovery
( IsOurs (..) )
import Cardano.Wallet.Primitive.AddressDiscovery.Sequential
( SeqState )
( SeqState, coinTypeAda, purposeBIP44 )
import Cardano.Wallet.Primitive.Types
( Address (..), Hash (..), invariant, testnetMagic )
import Control.Arrow
Expand Down Expand Up @@ -102,8 +102,6 @@ import Data.Proxy
( Proxy (..) )
import Data.Void
( Void )
import Data.Word
( Word32 )
import GHC.Generics
( Generic )
import GHC.TypeLits
Expand Down Expand Up @@ -132,31 +130,6 @@ newtype IcarusKey (depth :: Depth) key =

instance (NFData key) => NFData (IcarusKey depth key)

-- | Purpose is a constant set to 44' (or 0x8000002C) following the original
-- BIP-44 specification.
--
-- It indicates that the subtree of this node is used according to this
-- specification.
--
-- Hardened derivation is used at this level.
purposeIndex :: Word32
purposeIndex = 0x8000002C

-- | One master node (seed) can be used for unlimited number of independent
-- cryptocoins such as Bitcoin, Litecoin or Namecoin. However, sharing the
-- same space for various cryptocoins has some disadvantages.
--
-- This level creates a separate subtree for every cryptocoin, avoiding reusing
-- addresses across cryptocoins and improving privacy issues.
--
-- Coin type is a constant, set for each cryptocoin. For Cardano this constant
-- is set to 1815' (or 0x80000717). 1815 is the birthyear of our beloved Ada
-- Lovelace.
--
-- Hardened derivation is used at this level.
coinTypeIndex :: Word32
coinTypeIndex = 0x80000717

-- | The minimum seed length for 'generateKeyFromSeed' and 'unsafeGenerateKeyFromSeed'.
minSeedLengthBytes :: Int
minSeedLengthBytes = 16
Expand Down Expand Up @@ -324,9 +297,9 @@ instance HardDerivation IcarusKey where
(Passphrase pwd) (IcarusKey rootXPrv) (Index accIx) =
let
purposeXPrv = -- lvl1 derivation; hardened derivation of purpose'
deriveXPrv DerivationScheme2 pwd rootXPrv purposeIndex
deriveXPrv DerivationScheme2 pwd rootXPrv (getIndex purposeBIP44)
coinTypeXPrv = -- lvl2 derivation; hardened derivation of coin_type'
deriveXPrv DerivationScheme2 pwd purposeXPrv coinTypeIndex
deriveXPrv DerivationScheme2 pwd purposeXPrv (getIndex coinTypeAda)
acctXPrv = -- lvl3 derivation; hardened derivation of account' index
deriveXPrv DerivationScheme2 pwd coinTypeXPrv accIx
in
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ import Cardano.Wallet.Primitive.AddressDerivation
import Cardano.Wallet.Primitive.AddressDiscovery
( IsOurs (..) )
import Cardano.Wallet.Primitive.AddressDiscovery.Sequential
( SeqState, rewardAccountKey )
( SeqState, coinTypeAda, purposeCIP1852, rewardAccountKey )
import Cardano.Wallet.Primitive.Types
( Address (..), Hash (..), invariant )
import Control.DeepSeq
Expand All @@ -103,7 +103,7 @@ import Data.Proxy
import Data.Text.Class
( TextDecodingError (..) )
import Data.Word
( Word32, Word8 )
( Word8 )
import GHC.Generics
( Generic )
import GHC.Stack
Expand Down Expand Up @@ -143,33 +143,6 @@ addrSingleSize = 1 + publicKeySize
addrGroupedSize :: Int
addrGroupedSize = addrSingleSize + publicKeySize

-- | Purpose is a constant set to 1852' (or 0x8000073c) following the BIP-44
-- extension for Cardano:
--
-- https://github.com/input-output-hk/implementation-decisions/blob/e2d1bed5e617f0907bc5e12cf1c3f3302a4a7c42/text/1852-hd-chimeric.md
--
-- It indicates that the subtree of this node is used according to this
-- specification.
--
-- Hardened derivation is used at this level.
purposeIndex :: Word32
purposeIndex = 0x8000073c

-- | One master node (seed) can be used for unlimited number of independent
-- cryptocoins such as Bitcoin, Litecoin or Namecoin. However, sharing the
-- same space for various cryptocoins has some disadvantages.
--
-- This level creates a separate subtree for every cryptocoin, avoiding reusing
-- addresses across cryptocoins and improving privacy issues.
--
-- Coin type is a constant, set for each cryptocoin. For Cardano this constant
-- is set to 1815' (or 0x80000717). 1815 is the birthyear of our beloved Ada
-- Lovelace.
--
-- Hardened derivation is used at this level.
coinTypeIndex :: Word32
coinTypeIndex = 0x80000717

-- | The minimum seed length for 'generateKeyFromSeed' and
-- 'unsafeGenerateKeyFromSeed'.
minSeedLengthBytes :: Int
Expand Down Expand Up @@ -211,9 +184,9 @@ instance HardDerivation JormungandrKey where
(Passphrase pwd) (JormungandrKey rootXPrv) (Index accIx) =
let
purposeXPrv = -- lvl1 derivation; hardened derivation of purpose'
deriveXPrv DerivationScheme2 pwd rootXPrv purposeIndex
deriveXPrv DerivationScheme2 pwd rootXPrv (getIndex purposeCIP1852)
coinTypeXPrv = -- lvl2 derivation; hardened derivation of coin_type'
deriveXPrv DerivationScheme2 pwd purposeXPrv coinTypeIndex
deriveXPrv DerivationScheme2 pwd purposeXPrv (getIndex coinTypeAda)
acctXPrv = -- lvl3 derivation; hardened derivation of account' index
deriveXPrv DerivationScheme2 pwd coinTypeXPrv accIx
in
Expand Down
35 changes: 3 additions & 32 deletions lib/core/src/Cardano/Wallet/Primitive/AddressDerivation/Shelley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ import Cardano.Wallet.Primitive.AddressDerivation
import Cardano.Wallet.Primitive.AddressDiscovery
( IsOurs (..) )
import Cardano.Wallet.Primitive.AddressDiscovery.Sequential
( SeqState, rewardAccountKey )
( SeqState, coinTypeAda, purposeCIP1852, rewardAccountKey )
import Cardano.Wallet.Primitive.Types
( Address (..), Hash (..), invariant )
import Control.DeepSeq
Expand All @@ -104,8 +104,6 @@ import Data.Proxy
( Proxy (..) )
import Data.Text.Class
( TextDecodingError (..) )
import Data.Word
( Word32 )
import GHC.Generics
( Generic )

Expand All @@ -131,33 +129,6 @@ newtype ShelleyKey (depth :: Depth) key =

instance (NFData key) => NFData (ShelleyKey depth key)

-- | Purpose is a constant set to 1852' (or 0x8000073c) following the BIP-44
-- extension for Cardano:
--
-- https://github.com/input-output-hk/implementation-decisions/blob/e2d1bed5e617f0907bc5e12cf1c3f3302a4a7c42/text/1852-hd-chimeric.md
--
-- It indicates that the subtree of this node is used according to this
-- specification.
--
-- Hardened derivation is used at this level.
purposeIndex :: Word32
purposeIndex = 0x8000073c

-- | One master node (seed) can be used for unlimited number of independent
-- cryptocoins such as Bitcoin, Litecoin or Namecoin. However, sharing the
-- same space for various cryptocoins has some disadvantages.
--
-- This level creates a separate subtree for every cryptocoin, avoiding reusing
-- addresses across cryptocoins and improving privacy issues.
--
-- Coin type is a constant, set for each cryptocoin. For Cardano this constant
-- is set to 1815' (or 0x80000717). 1815 is the birthyear of our beloved Ada
-- Lovelace.
--
-- Hardened derivation is used at this level.
coinTypeIndex :: Word32
coinTypeIndex = 0x80000717

-- | The minimum seed length for 'generateKeyFromSeed' and
-- 'unsafeGenerateKeyFromSeed'.
minSeedLengthBytes :: Int
Expand Down Expand Up @@ -198,9 +169,9 @@ instance HardDerivation ShelleyKey where
(Passphrase pwd) (ShelleyKey rootXPrv) (Index accIx) =
let
purposeXPrv = -- lvl1 derivation; hardened derivation of purpose'
deriveXPrv DerivationScheme2 pwd rootXPrv purposeIndex
deriveXPrv DerivationScheme2 pwd rootXPrv (getIndex purposeCIP1852)
coinTypeXPrv = -- lvl2 derivation; hardened derivation of coin_type'
deriveXPrv DerivationScheme2 pwd purposeXPrv coinTypeIndex
deriveXPrv DerivationScheme2 pwd purposeXPrv (getIndex coinTypeAda)
acctXPrv = -- lvl3 derivation; hardened derivation of account' index
deriveXPrv DerivationScheme2 pwd coinTypeXPrv accIx
in
Expand Down
Loading

0 comments on commit 2ef0111

Please sign in to comment.