Skip to content

Commit

Permalink
wallet-api: AddressMap remembers TxOut
Browse files Browse the repository at this point in the history
* AddressMap now keeps track of the entire `TxOut` value, rather than
  just the data `Amount`. In particular we get the output's data script
  and can re-use in another transaction.
  • Loading branch information
j-mueller committed Jan 2, 2019
1 parent 595b723 commit cdbb0e8
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import qualified Data.Map as Map
import Data.Monoid (Sum (Sum), getSum)
import qualified Data.Set as Set
import qualified Data.Typeable as T
import Ledger.Types (Blockchain, PubKey (PubKey), Tx, Value (Value))
import Ledger.Types (Blockchain, PubKey (PubKey), Tx, TxOut (..), Value (Value))
import Playground.API (PlaygroundError (OtherError))
import Wallet.Emulator.Types (EmulatorEvent, EmulatorState (_chainNewestFirst, _emulatorLog), MockWallet,
Trace, Wallet (Wallet), ownFunds, processPending, runTraceTxPool,
Expand Down Expand Up @@ -43,7 +43,7 @@ runTrace wallets actions =
blockchain = _chainNewestFirst newState
emulatorLog = _emulatorLog newState
fundsDistribution =
Map.map (getSum . foldMap Sum . view ownFunds) .
Map.map (getSum . foldMap (Sum . txOutValue) . view ownFunds) .
view walletStates $
newState
in case eRes of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ collect cmp = register (collectFundsTrigger cmp) $ EventHandler $ \_ -> do
red = Ledger.RedeemerScript $ Ledger.lifted Collect
con (r, _) = scriptTxIn r scr red
ins = con <$> contributions
value = getSum $ foldMap (Sum . snd) contributions
value = getSum $ foldMap (Sum . Ledger.txOutValue . snd) contributions

oo <- ownPubKeyTxOut value
void $ signAndSubmit (Set.fromList ins) [oo]
Expand Down Expand Up @@ -201,7 +201,7 @@ refund txid cmp = EventHandler $ \_ -> do
red = Ledger.RedeemerScript $ Ledger.lifted Refund
i ref = scriptTxIn ref scr red
inputs = Set.fromList $ i . fst <$> ourUtxo
value = getSum $ foldMap (Sum . snd) ourUtxo
value = getSum $ foldMap (Sum . Ledger.txOutValue . snd) ourUtxo

out <- ownPubKeyTxOut value
void $ signAndSubmit inputs [out]
4 changes: 2 additions & 2 deletions wallet-api/src/Wallet/API.hs
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ collectFromScript scr red = do
outputs = am ^. at addr . to (Map.toList . fromMaybe Map.empty)
con (r, _) = scriptTxIn r scr red
ins = con <$> outputs
value = getSum $ foldMap (Sum . snd) outputs
value = getSum $ foldMap (Sum . txOutValue . snd) outputs

oo <- ownPubKeyTxOut value
void $ signAndSubmit (Set.fromList ins) [oo]
Expand All @@ -312,7 +312,7 @@ collectFromScriptTxn vls red txid = do
ourUtxo = Map.toList $ Map.filterWithKey (\k _ -> txid == Ledger.txOutRefId k) utxo
i ref = scriptTxIn ref vls red
inputs = Set.fromList $ i . fst <$> ourUtxo
value = getSum $ foldMap (Sum . snd) ourUtxo
value = getSum $ foldMap (Sum . txOutValue . snd) ourUtxo

out <- ownPubKeyTxOut value
void $ signAndSubmit inputs [out]
Expand Down
21 changes: 10 additions & 11 deletions wallet-api/src/Wallet/Emulator/AddressMap.hs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TypeFamilies #-}
module Wallet.Emulator.AddressMap(
AddressMap(..),
Expand Down Expand Up @@ -31,11 +30,11 @@ import qualified Data.Set as Set
import qualified Data.Text.Encoding as TE
import GHC.Generics (Generic)

import Ledger (Address', Tx (..), TxIn (..), TxIn', TxOut (..), TxOutRef (..), TxOutRef',
Value, hashTx)
import Ledger (Address', Tx (..), TxIn (..), TxIn', TxOut (..), TxOut', TxOutRef (..),
TxOutRef', Value, hashTx)

-- | A map of [[Address']]es and their unspent outputs
newtype AddressMap = AddressMap { getAddressMap :: Map Address' (Map TxOutRef' Value) }
newtype AddressMap = AddressMap { getAddressMap :: Map Address' (Map TxOutRef' TxOut') }
deriving Show
deriving stock (Generic)
deriving newtype (Serialise)
Expand All @@ -59,14 +58,14 @@ instance FromJSON AddressMap where

instance Semigroup AddressMap where
(AddressMap l) <> (AddressMap r) = AddressMap (Map.unionWith add l r) where
add = Map.unionWith (+)
add = Map.union

instance Monoid AddressMap where
mappend = (<>)
mempty = AddressMap Map.empty

type instance Index AddressMap = Address'
type instance IxValue AddressMap = Map TxOutRef' Value
type instance IxValue AddressMap = Map TxOutRef' TxOut'

instance Ixed AddressMap where
ix adr f (AddressMap mp) = AddressMap <$> ix adr f mp
Expand All @@ -80,7 +79,7 @@ instance At AddressMap where
-- nothing.
addAddress :: Address' -> AddressMap -> AddressMap
addAddress adr (AddressMap mp) = AddressMap $ Map.alter upd adr mp where
upd :: Maybe (Map TxOutRef' Value) -> Maybe (Map TxOutRef' Value)
upd :: Maybe (Map TxOutRef' TxOut') -> Maybe (Map TxOutRef' TxOut')
upd = maybe (Just Map.empty) Just

-- | Add a list of [[Address']]es with no unspent outputs
Expand All @@ -89,20 +88,20 @@ addAddresses = flip (foldr addAddress)

-- | The total value of unspent outputs at an address
values :: AddressMap -> Map Address' Value
values = Map.map (getSum . foldMap Sum) . getAddressMap
values = Map.map (getSum . foldMap (Sum . txOutValue)) . getAddressMap

-- | An [[AddressMap]] with the unspent outputs of a single transaction
fromTxOutputs :: Tx -> AddressMap
fromTxOutputs tx =
AddressMap . Map.fromListWith Map.union . fmap mkUtxo . zip [0..] . txOutputs $ tx where
mkUtxo (i, TxOut{..}) = (txOutAddress, Map.singleton (TxOutRef h i) txOutValue)
mkUtxo (i, t) = (txOutAddress t, Map.singleton (TxOutRef h i) t)
h = hashTx tx

-- | A map of unspent transaction outputs to their addresses (the "inverse" of
-- [[AddressMap]], without the values
knownAddresses :: AddressMap -> Map TxOutRef' Address'
knownAddresses = Map.fromList . unRef . Map.toList . getAddressMap where
unRef :: [(Address', Map TxOutRef' Value)] -> [(TxOutRef', Address')]
unRef :: [(Address', Map TxOutRef' TxOut')] -> [(TxOutRef', Address')]
unRef lst = do
(a, outRefs) <- lst
(rf, _) <- Map.toList outRefs
Expand All @@ -118,7 +117,7 @@ updateAddresses tx utxo = AddressMap $ Map.mapWithKey upd (getAddressMap utxo) w
upd adr mp = Map.union (producedAt adr) mp `Map.difference` consumedFrom adr

-- The TxOutRefs produced by the transaction, for a given address
producedAt :: Address' -> Map TxOutRef' Value
producedAt :: Address' -> Map TxOutRef' TxOut'
producedAt adr = Map.findWithDefault Map.empty adr outputs

-- The TxOutRefs consumed by the transaction, for a given address
Expand Down
13 changes: 7 additions & 6 deletions wallet-api/src/Wallet/Emulator/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,9 @@ import Prelude as P
import Servant.API (FromHttpApiData, ToHttpApiData)

import Data.Hashable (Hashable)
import Ledger (Address', Block, Blockchain, Height, Tx (..), TxId', TxOutRef', Value,
hashTx, height, pubKeyAddress, pubKeyTxIn, pubKeyTxOut, txOutAddress)
import Ledger (Address', Block, Blockchain, Height, Tx (..), TxId', TxOut (..), TxOut',
TxOutRef', Value, hashTx, height, pubKeyAddress, pubKeyTxIn, pubKeyTxOut,
txOutAddress)
import qualified Ledger.Index as Index
import Wallet.API (EventHandler (..), EventTrigger, KeyPair (..), WalletAPI (..),
WalletAPIError (..), WalletDiagnostics (..), WalletLog (..), addresses,
Expand Down Expand Up @@ -135,7 +136,7 @@ makeLenses ''WalletState
ownAddress :: WalletState -> Address'
ownAddress = pubKeyAddress . pubKey . view ownKeyPair

ownFunds :: Lens' WalletState (Map TxOutRef' Value)
ownFunds :: Lens' WalletState (Map TxOutRef' TxOut')
ownFunds = lens g s where
g ws = fromMaybe Map.empty $ ws ^. addressMap . at (ownAddress ws)
s ws utxo = ws & addressMap . at (ownAddress ws) ?~ utxo
Expand Down Expand Up @@ -207,7 +208,7 @@ instance WalletAPI MockWallet where
let fnds = ws ^. ownFunds
kp = view ownKeyPair ws
sig = signature kp
(spend, change) <- selectCoin (Map.toList fnds) vl
(spend, change) <- selectCoin (second txOutValue <$> Map.toList fnds) vl
let
txOutput = if change > 0 then Just (pubKeyTxOut change (pubKey kp)) else Nothing
ins = Set.fromList (flip pubKeyTxIn sig . fst <$> spend)
Expand Down Expand Up @@ -296,7 +297,7 @@ data EmulatorState = EmulatorState {
makeLenses ''EmulatorState

fundsDistribution :: EmulatorState -> Map Wallet Value
fundsDistribution = Map.map (getSum . foldMap Sum . view ownFunds) . view walletStates
fundsDistribution = Map.map (getSum . foldMap (Sum . txOutValue) . view ownFunds) . view walletStates

-- | The blockchain as a list of blocks, starting with the oldest (genesis)
-- block
Expand Down Expand Up @@ -324,7 +325,7 @@ ownFundsEqual wallet value = do
ws <- case Map.lookup wallet $ _walletStates es of
Nothing -> throwError $ AssertionError "Wallet not found"
Just ws -> pure ws
let total = getSum $ foldMap Sum $ ws ^. ownFunds
let total = getSum $ foldMap (Sum . txOutValue) $ ws ^. ownFunds
if value == total
then pure ()
else throwError . AssertionError $ T.unwords ["Funds in wallet", tshow wallet, "were", tshow total, ". Expected:", tshow value]
Expand Down
6 changes: 3 additions & 3 deletions wallet-api/test/Spec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ notifyWallet = property $ do
$ Gen.runTraceOn Gen.generatorModel
$ processPending >>= walletNotifyBlock w
let ttl = Map.lookup w st
Hedgehog.assert $ (getSum . foldMap Sum . view ownFunds <$> ttl) == Just initialBalance
Hedgehog.assert $ (getSum . foldMap (Sum . txOutValue) . view ownFunds <$> ttl) == Just initialBalance

eventTrace :: Property
eventTrace = property $ do
Expand All @@ -212,7 +212,7 @@ eventTrace = property $ do
let ttl = Map.lookup w st

-- if `mkPayment` was run then the funds of wallet 1 should be reduced by 100
Hedgehog.assert $ (getSum . foldMap Sum . view ownFunds <$> ttl) == Just (initialBalance - 100)
Hedgehog.assert $ (getSum . foldMap (Sum . txOutValue) . view ownFunds <$> ttl) == Just (initialBalance - 100)

payToPubKeyScript2 :: Property
payToPubKeyScript2 = property $ do
Expand Down Expand Up @@ -279,7 +279,7 @@ watchFundsAtAddress = property $ do
addBlocks 3 >>= traverse_ (walletNotifyBlock w)
void (processPending >>= walletNotifyBlock w)
let ttl = Map.lookup w st
Hedgehog.assert $ (getSum . foldMap Sum . view ownFunds <$> ttl) == Just (initialBalance - 200)
Hedgehog.assert $ (getSum . foldMap (Sum . txOutValue) . view ownFunds <$> ttl) == Just (initialBalance - 200)

genChainTxn :: Hedgehog.MonadGen m => m (Mockchain, Tx)
genChainTxn = do
Expand Down

0 comments on commit cdbb0e8

Please sign in to comment.