Skip to content

Commit

Permalink
[ADP-3244] Use AddressState from customer-deposit-wallet-pure (#4577
Browse files Browse the repository at this point in the history
)

This pull request changes the deposit wallet implementation in
`Cardano.Wallet.Deposit.Pure` to use the `AddressState` type from the
[customer-deposit-wallet-pure](https://github.com/cardano-foundation/cardano-wallet-agda).
This type was generated from Agda code using `agda2hs`.

### Comments

* The `AddressState` type uses `ByteString` to represent addresses. We
currently cannot depend on `cardano-wallet-read` in the other
repository, because this would cause a circular dependency between the
two repositories. This is not an issue for very basic types like
`Address`, but will be more complicated for more complex types such as
`UTxO` or `TxBody`. That said, in order to prove something about
functions involving those types, the `cardano-wallet-agda` repository
will need to have access to them. The interim solution will be to move
`cardano-wallet-read` to the `cardano-wallet-agda` repository.

### Issue Number

ADP-3244
  • Loading branch information
HeinrichApfelmus authored Jun 27, 2024
2 parents 925b037 + 1f03b06 commit 66c9a67
Show file tree
Hide file tree
Showing 10 changed files with 141 additions and 56 deletions.
11 changes: 11 additions & 0 deletions cabal.project
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,17 @@ source-repository-package
--sha256: 04q58c82wy6x9nkwqbvcxbv6s61fx08h5kf62sb511aqp08id4bb
subdir: generated

source-repository-package
type: git
location: https://github.com/cardano-foundation/cardano-wallet-agda
tag: 33702851de8b846cc0bb9c48ee24c987e6d02c01
--sha256: 0qiffp4dgz2c8wjjs4qk8g307a8li2lcczbdzinfcyxn0q01pcvy
subdir: lib/customer-deposit-wallet-pure

-- With (semi-circular) dependency on cardano-wallet-read:
-- tag: 1b2b22f68b7535d055b91753b68c92a2b2596038
-- --sha256: 0yqga8hv66xxmd724pwyr4jdd98s5w3mc35sfzkpaywivi8g3kxx

--------------------------------------------------------------------------------
-- BEGIN Constraints tweaking section

Expand Down
5 changes: 5 additions & 0 deletions lib/customer-deposit-wallet/customer-deposit-wallet.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,12 @@ library
, cardano-ledger-byron
, containers
, contra-tracer
, customer-deposit-wallet-pure
, delta-store
, delta-types
, io-classes
, iohk-monitoring-extra
, OddWord
, persistent
, sqlite-simple
, text
Expand All @@ -85,6 +87,8 @@ test-suite unit
main-is: test-suite-unit.hs
build-depends:
, base
, bytestring
, cardano-crypto
, cardano-wallet:cardano-wallet
, cardano-wallet-primitive
, cardano-wallet-test-utils
Expand All @@ -108,6 +112,7 @@ test-suite scenario
-pgmL markdown-unlit
build-depends:
, base
, bytestring
, cardano-crypto
, cardano-wallet-test-utils
, containers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,13 @@ deriving via ViaText Address instance ToJSON (ApiT Address)

-- Customer
instance FromHttpApiData (ApiT Customer) where
parseUrlPiece = fmap ApiT . fromText'
parseUrlPiece = fmap (ApiT . toEnum) . fromText'

instance FromJSON (ApiT Customer) where
parseJSON = fmap ApiT . parseJSON
parseJSON = fmap (ApiT . toEnum) . parseJSON

instance ToJSON (ApiT Customer) where
toJSON = toJSON . unApiT
toJSON = toJSON . fromEnum . unApiT

-- | 'fromText' but with a simpler error type.
fromText' :: FromText a => Text -> Either Text a
Expand Down
9 changes: 7 additions & 2 deletions lib/customer-deposit-wallet/src/Cardano/Wallet/Deposit/IO.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module Cardano.Wallet.Deposit.IO
-- * Operations
-- ** Initialization
, withWalletInit
, Word31
, withWalletLoad

-- ** Mapping between customers and addresses
Expand All @@ -32,9 +33,13 @@ import Prelude
import Cardano.Crypto.Wallet
( XPub
)
import Cardano.Wallet.Address.BIP32
( BIP32Path
)
import Cardano.Wallet.Deposit.Pure
( Customer
, WalletState
, Word31
)
import Cardano.Wallet.Deposit.Read
( Address
Expand Down Expand Up @@ -108,7 +113,7 @@ readWalletState WalletInstance{env,walletState} =
withWalletInit
:: WalletEnv IO
-> XPub
-> Integer
-> Word31
-> (WalletInstance -> IO a)
-> IO a
withWalletInit env@WalletEnv{..} xpub knownCustomerCount action = do
Expand Down Expand Up @@ -205,7 +210,7 @@ createPayment a w =
Wallet.createPayment a <$> readWalletState w

getBIP32PathsForOwnedInputs
:: Write.TxBody -> WalletInstance -> IO [()]
:: Write.TxBody -> WalletInstance -> IO [BIP32Path]
getBIP32PathsForOwnedInputs a w =
Wallet.getBIP32PathsForOwnedInputs a <$> readWalletState w

Expand Down
100 changes: 61 additions & 39 deletions lib/customer-deposit-wallet/src/Cardano/Wallet/Deposit/Pure.hs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ module Cardano.Wallet.Deposit.Pure

-- ** Reading from the blockchain
, fromXPubAndGenesis
, Word31
, getWalletTip
, availableBalance
, rollForwardMany
Expand All @@ -29,26 +30,32 @@ module Cardano.Wallet.Deposit.Pure

-- ** Writing to the blockchain
, createPayment
, BIP32Path (..)
, DerivationType (..)
, getBIP32PathsForOwnedInputs

, addTxSubmission
, listTxsInSubmission

-- * Internal
, fromGenesisUTxO
) where

import Prelude

import Cardano.Crypto.Wallet
( XPub
)
import Cardano.Wallet.Address.BIP32
( BIP32Path (..)
, DerivationType (..)
)
import Cardano.Wallet.Deposit.Pure.UTxOHistory
( UTxOHistory
)
import Cardano.Wallet.Deposit.Read
( Address
)
import Data.Bifunctor
( second
)
import Data.Foldable
( foldl'
)
Expand All @@ -59,39 +66,37 @@ import Data.Map.Strict
( Map
)
import Data.Maybe
( isJust
( mapMaybe
)
import Data.Set
( Set
)
import Numeric.Natural
( Natural
import Data.Word.Odd
( Word31
)

import qualified Cardano.Wallet.Deposit.Pure.Address as Address
import qualified Cardano.Wallet.Deposit.Pure.Balance as Balance
import qualified Cardano.Wallet.Deposit.Pure.Submissions as Sbm
import qualified Cardano.Wallet.Deposit.Pure.UTxO as UTxO
import qualified Cardano.Wallet.Deposit.Pure.UTxOHistory as UTxOHistory
import qualified Cardano.Wallet.Deposit.Read as Read
import qualified Cardano.Wallet.Deposit.Write as Write
import qualified Data.Delta as Delta
import qualified Data.Map.Strict as Map

{-----------------------------------------------------------------------------
Types
------------------------------------------------------------------------------}
type Customer = Natural
type Customer = Address.Customer

data WalletState = WalletState
{ customers :: !(Map Customer Address)
, changeAddress :: !Address
{ addresses :: !Address.AddressState
, utxoHistory :: !UTxOHistory.UTxOHistory
-- , txHistory :: [Read.Tx]
, submissions :: Sbm.TxSubmissions
-- , credentials :: Maybe (HashedCredentials (KeyOf s))
-- , info :: !WalletInfo
}
deriving (Eq, Show)

type DeltaWalletState = Delta.Replace WalletState

Expand All @@ -101,47 +106,50 @@ type DeltaWalletState = Delta.Replace WalletState
------------------------------------------------------------------------------}

listCustomers :: WalletState -> [(Customer, Address)]
listCustomers = Map.toList . customers
listCustomers =
map (second Read.fromRawAddress)
. Address.listCustomers . addresses

createAddress :: Customer -> WalletState -> (Address, WalletState)
createAddress customer w1 = (address, w2)
createAddress customer w0 =
(Read.fromRawAddress address, w0{addresses = s1})
where
address = deriveAddress w1 customer
w2 = w1{customers = Map.insert customer address (customers w1)}
(address, s1) = Address.createAddress customer (addresses w0)

-- depend on the private key only, not on the entire wallet state
deriveAddress :: WalletState -> (Customer -> Address)
deriveAddress _ = Read.mockAddress
deriveAddress w =
Read.fromRawAddress
. Address.deriveAddress (Address.getXPub (addresses w))
. Address.DerivationCustomer

-- FIXME: More performant with a double index.
knownCustomer :: Customer -> WalletState -> Bool
knownCustomer c = (c `Map.member`) . customers
knownCustomer c = (c `elem`) . map fst . listCustomers

knownCustomerAddress :: Address -> WalletState -> Bool
knownCustomerAddress address = isJust . isCustomerAddress address
knownCustomerAddress address =
Address.knownCustomerAddress (Read.toRawAddress address) . addresses

isCustomerAddress :: Address -> WalletState -> Maybe Customer
isCustomerAddress address w =
case filter ((== address) . snd) (Map.toList $ customers w) of
[(customer,_address)] -> Just customer
_ -> Nothing
isCustomerAddress :: Address -> WalletState -> Bool
isCustomerAddress address =
flip Address.isCustomerAddress (Read.toRawAddress address) . addresses

{-----------------------------------------------------------------------------
Operations
Reading from the blockchain
------------------------------------------------------------------------------}

fromXPubAndGenesis :: XPub -> Integer -> Read.GenesisData -> WalletState
fromXPubAndGenesis _xpub _knownCustomerCount _ = fromGenesisUTxO mempty
-- FIXME: This is a mock implementation

fromGenesisUTxO :: Read.UTxO -> WalletState
fromGenesisUTxO utxo =
fromXPubAndGenesis :: XPub -> Word31 -> Read.GenesisData -> WalletState
fromXPubAndGenesis xpub knownCustomerCount _ =
WalletState
{ customers = Map.empty
, changeAddress = Read.dummyAddress
, utxoHistory = UTxOHistory.empty utxo
{ addresses =
Address.fromXPubAndCount xpub knownCustomerCount
, utxoHistory = UTxOHistory.empty initialUTxO
, submissions = Sbm.empty
}
where
initialUTxO = mempty

getWalletTip :: WalletState -> Read.ChainPoint
getWalletTip = error "getWalletTip"
Expand All @@ -157,9 +165,7 @@ rollForwardOne block w =
}
where
isOurs :: Address -> Bool
isOurs addr =
( addr == changeAddress w ) || knownCustomerAddress addr w
-- FIXME: Consider payment part only, ignore staking part.
isOurs = Address.isOurs (addresses w) . Read.toRawAddress

rollForwardUTxO
:: (Address -> Bool) -> Read.Block -> UTxOHistory -> UTxOHistory
Expand All @@ -176,8 +182,11 @@ rollBackward
rollBackward point w = (w, point) -- FIXME: This is a mock implementation

availableBalance :: WalletState -> Read.Value
availableBalance w =
UTxO.balance $ Balance.availableUTxO utxo pending
availableBalance = UTxO.balance . availableUTxO

availableUTxO :: WalletState -> UTxO.UTxO
availableUTxO w =
Balance.availableUTxO utxo pending
where
pending = listTxsInSubmission w
utxo = UTxOHistory.getUTxO $ utxoHistory w
Expand Down Expand Up @@ -216,8 +225,21 @@ createPayment = undefined
-- needs balanceTx
-- needs to sign the transaction

getBIP32PathsForOwnedInputs :: Write.TxBody -> WalletState -> [()]
getBIP32PathsForOwnedInputs = undefined
getBIP32PathsForOwnedInputs :: Write.TxBody -> WalletState -> [BIP32Path]
getBIP32PathsForOwnedInputs txbody w =
getBIP32Paths w
. resolveInputAddresses
$ Write.spendInputs txbody <> Write.collInputs txbody
where
resolveInputAddresses :: Set Read.TxIn -> [Read.Address]
resolveInputAddresses ins =
map (Read.address . snd)
. UTxO.toList
$ UTxO.restrictedBy (availableUTxO w) ins

getBIP32Paths :: WalletState -> [Read.Address] -> [BIP32Path]
getBIP32Paths w =
mapMaybe $ Address.getBIP32Path (addresses w) . Read.toRawAddress

addTxSubmission :: Write.Tx -> WalletState -> WalletState
addTxSubmission _tx _w = undefined
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ module Cardano.Wallet.Deposit.Pure.UTxO
( UTxO
, balance
, excluding
, restrictedBy
, filterByAddress
, toList

, DeltaUTxO
, excludingD
Expand Down
13 changes: 13 additions & 0 deletions lib/customer-deposit-wallet/src/Cardano/Wallet/Deposit/Read.hs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ module Cardano.Wallet.Deposit.Read

, Addr
, Address
, fromRawAddress
, toRawAddress
, mockAddress

, Ix
, TxIn
, TxOut
, address
, Value
, UTxO

Expand Down Expand Up @@ -57,6 +60,7 @@ import qualified Cardano.Wallet.Primitive.Types.Address as W
import qualified Cardano.Wallet.Primitive.Types.TokenBundle as W
import qualified Cardano.Wallet.Primitive.Types.Tx as W
import qualified Cardano.Wallet.Primitive.Types.Tx.TxIn as W
import qualified Cardano.Wallet.Primitive.Types.Tx.TxOut as TxOut
import qualified Cardano.Wallet.Primitive.Types.Tx.TxOut as W
import qualified Cardano.Wallet.Primitive.Types.UTxO as W
import qualified Data.ByteString as BS
Expand Down Expand Up @@ -86,6 +90,12 @@ type Addr = W.Address
-- Byron addresses are represented by @Addr_bootstrap@.
type Address = Addr

fromRawAddress :: BS.ByteString -> Address
fromRawAddress = W.Address

toRawAddress :: Address -> BS.ByteString
toRawAddress (W.Address a) = a

mockAddress :: Show a => a -> Address
mockAddress = W.Address . B8.pack . show

Expand All @@ -100,6 +110,9 @@ type TxIn = W.TxIn
-- type TxOut = (Addr, Value)
type TxOut = W.TxOut

address :: TxOut -> Address
address = TxOut.address

type Value = W.TokenBundle

-- type UTxO = Map TxIn TxOut
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ import Cardano.Wallet.Deposit.IO.Network.Mock
import Cardano.Wallet.Deposit.IO.Network.Type
( NetworkEnv (..)
)
import Cardano.Wallet.Deposit.Pure
( BIP32Path
)
import Control.Tracer
( nullTracer
)
Expand Down Expand Up @@ -128,9 +131,8 @@ payFromFaucet env destinations =
{-----------------------------------------------------------------------------
Transaction submission
------------------------------------------------------------------------------}
type Path = ()

signTx :: XPrv -> [Path] -> Write.TxBody -> Write.Tx
signTx :: XPrv -> [BIP32Path] -> Write.TxBody -> Write.Tx
signTx _ _ txbody =
Write.Tx
{ Write.txbody = txbody
Expand Down
Loading

0 comments on commit 66c9a67

Please sign in to comment.