Skip to content

Commit

Permalink
Also provide datum with external inputs.
Browse files Browse the repository at this point in the history
  It is actually needed to lookup redeemer pointers of spending redeemers.
  • Loading branch information
KtorZ committed Oct 14, 2021
1 parent 5ccbfad commit c1620e3
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 24 deletions.
1 change: 1 addition & 0 deletions lib/core-integration/src/Test/Integration/Plutus.hs
Expand Up @@ -47,6 +47,7 @@ pingPong_2 =
"id": "{{transactionId}}",
"index": 1,
"address": "addr1w9xh9n6kngee5x98myczxyuc8atwpktv63daevwk2yku56sce02jy",
"datum": "923918e403bf43c34b4ef6b48eb2ee04babed17320d8d1b9ff9ad086e86f44ec",
"amount": {
"quantity": 2000000,
"unit": "lovelace"
Expand Down
41 changes: 28 additions & 13 deletions lib/core/src/Cardano/Wallet/Api/Server.hs
Expand Up @@ -2236,13 +2236,13 @@ balanceTransaction ctx genChange (ApiT wid) body = do

let (outputs, txWithdrawal, txMetadata) = extractFromTx partialTx

(delta, resolveInput, extraInputs, extraCollateral, extraOutputs) <-
(delta, extraInputs, extraCollateral, extraOutputs) <-
withWorkerCtx ctx wid liftE liftE $ \wrk -> do
(internalUtxoAvailable, wallet, pendingTxs) <-
liftHandler $ W.readWalletUTxOIndex @_ @s @k wrk wid

let externalSelectedUtxo =
UTxOIndex.fromSequence (toTxInTxOut <$> (body ^. #inputs))
let externalSelectedUtxo = UTxOIndex.fromSequence
((\(a,b,_)-> (a,b)) <$> externalInputs)

let utxoAvailableForInputs = UTxOSelection.fromIndexPair
(internalUtxoAvailable, externalSelectedUtxo)
Expand Down Expand Up @@ -2280,7 +2280,6 @@ balanceTransaction ctx genChange (ApiT wid) body = do
let (sel', _) = W.assignChangeAddresses genChange sel s
inputs = F.toList (sel' ^. #inputs)
in ( selectionDelta txOutCoin sel'
, \i -> snd <$> L.find ((== i) . fst) inputs
, fst <$> inputs
, fst <$> (sel' ^. #collateral)
, sel' ^. #change
Expand Down Expand Up @@ -2313,7 +2312,7 @@ balanceTransaction ctx genChange (ApiT wid) body = do
-- doing such thing is considered bonkers and this is not a behavior we
-- ought to support.

candidateTx <- assembleTransaction nodePParams resolveInput $ TxUpdate
candidateTx <- assembleTransaction nodePParams $ TxUpdate
{ extraInputs
, extraCollateral
, extraOutputs
Expand All @@ -2323,7 +2322,7 @@ balanceTransaction ctx genChange (ApiT wid) body = do
evaluateMinimumFee tl nodePParams candidateTx

let surplus = delta `Coin.distance` candidateMinFee
finalTx <- assembleTransaction nodePParams resolveInput $ TxUpdate
finalTx <- assembleTransaction nodePParams $ TxUpdate
{ extraInputs
, extraCollateral
, extraOutputs = mapFirst (txOutAddCoin surplus) extraOutputs
Expand All @@ -2349,20 +2348,21 @@ balanceTransaction ctx genChange (ApiT wid) body = do
redeemers :: [Redeemer]
redeemers = fromApiRedeemer <$> body ^. #redeemers

externalInputs :: [(TxIn, TxOut, Maybe (Hash "Datum"))]
externalInputs = fromExternalInput <$> body ^. #inputs

assembleTransaction
:: Cardano.ProtocolParameters
-> (TxIn -> Maybe TxOut)
-> TxUpdate
-> Handler SealedTx
assembleTransaction nodePParams resolveInput update = do
assembleTransaction nodePParams update = do
tx' <- asHandler $ updateTx tl partialTx update
liftHandler $ ExceptT $ assignScriptRedeemers
tl nodePParams ti resolveInput redeemers tx'

toTxInTxOut (ApiExternalInput (ApiT tid) ix (ApiT addr, _) (Quantity amt) (ApiT assets)) =
( TxIn tid ix
, TxOut addr (TokenBundle (Coin $ fromIntegral amt) assets)
)
where
resolveInput :: TxIn -> Maybe (TxOut, Maybe (Hash "Datum"))
resolveInput i =
(\(_,a,b) -> (a,b)) <$> L.find (\(i',_,_) -> i == i') externalInputs

extractFromTx tx =
let (Tx _id _fee _coll _inps outs wdrlMap meta _vldt) = decodeTx tl tx
Expand Down Expand Up @@ -3454,6 +3454,21 @@ getWalletTip
-> m ApiBlockReference
getWalletTip ti = makeApiBlockReferenceFromHeader ti . currentTip

fromExternalInput :: ApiExternalInput n -> (TxIn, TxOut, Maybe (Hash "Datum"))
fromExternalInput ApiExternalInput
{ id = ApiT tid
, index = ix
, address = (ApiT addr, _)
, amount = Quantity amt
, assets = ApiT assets
, datum
}
=
( TxIn tid ix
, TxOut addr (TokenBundle (Coin $ fromIntegral amt) assets)
, getApiT <$> datum
)

fromApiRedeemer :: ApiRedeemer n -> Redeemer
fromApiRedeemer = \case
ApiRedeemerSpending (ApiBytesT bytes) (ApiT i) ->
Expand Down
6 changes: 6 additions & 0 deletions lib/core/src/Cardano/Wallet/Api/Types.hs
Expand Up @@ -1006,6 +1006,7 @@ data ApiExternalInput (n :: NetworkDiscriminant) = ApiExternalInput
, address :: !(ApiT Address, Proxy n)
, amount :: !(Quantity "lovelace" Natural)
, assets :: !(ApiT W.TokenMap)
, datum :: !(Maybe (ApiT (Hash "Datum")))
} deriving (Eq, Generic, Show, Typeable)
deriving anyclass NFData

Expand Down Expand Up @@ -3006,6 +3007,11 @@ instance FromJSON (ApiT (Hash "Tx")) where
instance ToJSON (ApiT (Hash "Tx")) where
toJSON = toTextJSON

instance FromJSON (ApiT (Hash "Datum")) where
parseJSON = fromTextJSON "Datum Hash"
instance ToJSON (ApiT (Hash "Datum")) where
toJSON = toTextJSON

instance FromJSON (ApiT Direction) where
parseJSON = fmap ApiT . genericParseJSON defaultSumTypeOptions
instance ToJSON (ApiT Direction) where
Expand Down
4 changes: 3 additions & 1 deletion lib/core/src/Cardano/Wallet/Transaction.hs
Expand Up @@ -61,6 +61,8 @@ import Cardano.Wallet.Primitive.Types.Address
( Address (..) )
import Cardano.Wallet.Primitive.Types.Coin
( Coin (..) )
import Cardano.Wallet.Primitive.Types.Hash
( Hash )
import Cardano.Wallet.Primitive.Types.Redeemer
( Redeemer )
import Cardano.Wallet.Primitive.Types.RewardAccount
Expand Down Expand Up @@ -213,7 +215,7 @@ data TransactionLayer k tx = TransactionLayer
-- Current protocol parameters
-> TimeInterpreter (ExceptT PastHorizonException IO)
-- Time interpreter in the Monad m
-> (TxIn -> Maybe TxOut)
-> (TxIn -> Maybe (TxOut, Maybe (Hash "Datum")))
-- A input resolver for transactions' inputs containing scripts.
-> [Redeemer]
-- A list of redeemers to set on the transaction.
Expand Down
4 changes: 4 additions & 0 deletions lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs
Expand Up @@ -2076,6 +2076,7 @@ instance Arbitrary (ApiExternalInput n) where
<*> arbitrary
<*> arbitrary
<*> arbitrary
<*> arbitrary

instance Arbitrary (ApiBalanceTransactionPostData n) where
arbitrary = ApiBalanceTransactionPostData
Expand Down Expand Up @@ -2395,6 +2396,9 @@ instance Arbitrary (Quantity "slot" Natural) where
instance Arbitrary (Hash "Tx") where
arbitrary = Hash . B8.pack <$> replicateM 32 arbitrary

instance Arbitrary (Hash "Datum") where
arbitrary = Hash . B8.pack <$> replicateM 32 arbitrary

instance Arbitrary Direction where
arbitrary = genericArbitrary
shrink = genericShrink
Expand Down
30 changes: 23 additions & 7 deletions lib/shelley/src/Cardano/Wallet/Shelley/Compatibility/Ledger.hs
@@ -1,5 +1,7 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE TypeApplications #-}

Expand Down Expand Up @@ -43,6 +45,8 @@ import Prelude

import Cardano.Crypto.Hash
( hashFromBytes, hashToBytes )
import Cardano.Ledger.SafeHash
( unsafeMakeSafeHash )
import Cardano.Wallet.Primitive.Types
( MinimumUTxOValue (..) )
import Cardano.Wallet.Primitive.Types.Address
Expand All @@ -61,6 +65,8 @@ import Cardano.Wallet.Primitive.Types.TokenQuantity
( TokenQuantity (..) )
import Cardano.Wallet.Primitive.Types.Tx
( TxOut (..) )
import Data.ByteString.Short
( toShort )
import Data.Function
( (&) )
import Data.Generics.Internal.VL.Lens
Expand All @@ -76,8 +82,9 @@ import GHC.Stack
import Ouroboros.Consensus.Shelley.Eras
( StandardCrypto )

import qualified Cardano.Ledger.Address as Ledger

import qualified Cardano.Crypto.Hash.Class as Crypto
import qualified Cardano.Ledger.Address as Ledger
import qualified Cardano.Ledger.Alonzo as Alonzo
import qualified Cardano.Ledger.Alonzo.Rules.Utxo as Alonzo
import qualified Cardano.Ledger.Alonzo.TxBody as Alonzo
Expand Down Expand Up @@ -280,12 +287,20 @@ instance Convert Address (Ledger.Addr StandardCrypto) where

toAlonzoTxOut
:: TxOut
-> Maybe (Hash "Datum")
-> Alonzo.TxOut (Alonzo.AlonzoEra StandardCrypto)
toAlonzoTxOut (TxOut addr bundle) =
Alonzo.TxOut
(toLedger addr)
(toLedger bundle)
Ledger.SNothing
toAlonzoTxOut (TxOut addr bundle) = \case
Nothing ->
Alonzo.TxOut
(toLedger addr)
(toLedger bundle)
Ledger.SNothing
Just (Hash bytes) ->
Alonzo.TxOut
(toLedger addr)
(toLedger bundle)
(Ledger.SJust $ unsafeMakeSafeHash $ Crypto.UnsafeHash $ toShort bytes)


--------------------------------------------------------------------------------
-- Internal functions
Expand Down Expand Up @@ -319,7 +334,8 @@ computeMinimumAdaQuantityInternal (MinimumUTxOValue protocolMinimum) bundle =
(toLedgerCoin protocolMinimum)
computeMinimumAdaQuantityInternal (MinimumUTxOValueCostPerWord (Coin perWord)) bundle =
let
outputSize = Alonzo.utxoEntrySize (toAlonzoTxOut (TxOut dummyAddr bundle))
outputSize = Alonzo.utxoEntrySize $
toAlonzoTxOut (TxOut dummyAddr bundle) Nothing
in
Coin $ fromIntegral outputSize * perWord
where
Expand Down
8 changes: 5 additions & 3 deletions lib/shelley/src/Cardano/Wallet/Shelley/Transaction.hs
Expand Up @@ -104,6 +104,8 @@ import Cardano.Wallet.Primitive.Types.Address
( Address (..) )
import Cardano.Wallet.Primitive.Types.Coin
( Coin (..) )
import Cardano.Wallet.Primitive.Types.Hash
( Hash )
import Cardano.Wallet.Primitive.Types.Redeemer
( Redeemer, redeemerData )
import Cardano.Wallet.Primitive.Types.TokenBundle
Expand Down Expand Up @@ -863,7 +865,7 @@ _assignScriptRedeemers
=> NetworkId
-> Cardano.ProtocolParameters
-> TimeInterpreter (ExceptT PastHorizonException m)
-> (TxIn -> Maybe TxOut)
-> (TxIn -> Maybe (TxOut, Maybe (Hash "Datum")))
-> [Redeemer]
-> SealedTx
-> m (Either ErrAssignRedeemers SealedTx)
Expand Down Expand Up @@ -923,13 +925,13 @@ _assignScriptRedeemers ntwrk (toAlonzoPParams -> pparams) ti resolveInput redeem
let
inputs = Alonzo.inputs (Alonzo.body alonzoTx)
utxo = flip mapMaybe (F.toList inputs) $ \i -> do
o <- resolveInput (fromShelleyTxIn i)
(o, dt) <- resolveInput (fromShelleyTxIn i)
-- NOTE: 'toAlonzoTxOut' only partially represent the information
-- because the wallet internal types aren't capturing datum at
-- the moment. It is _okay_ in this context because the
-- resulting UTxO is only used by 'evaluateTransactionExecutionUnits'
-- to lookup addresses corresponding to inputs.
pure (i, toAlonzoTxOut o)
pure (i, toAlonzoTxOut o dt)
in
Ledger.UTxO (Map.fromList utxo)

Expand Down
9 changes: 9 additions & 0 deletions specifications/api/swagger.yaml
Expand Up @@ -1023,6 +1023,14 @@ x-transactionId: &transactionId
minLength: 64
example: 1423856bc91c49e928f6f30f4e8d665d53eb4ab6028bd0ac971809d514c92db1

x-datum: &datum
description: A datum hash.
type: string
format: hex
maxLength: 64
minLength: 64
example: 16f30f4e8d665d53eb4ab6028bd0ac971809d514c92d423856bc91c49e928faf

x-transactionInsertedAt: &transactionInsertedAt
description: |
<span style="position: relative; left: 35px; top: -21px; vertical-align: middle; background-color: rgba(142, 142, 220, 0.05); color: rgba(50, 50, 159, 0.9); margin: 0 5px; padding: 0 5px; border: 1px solid rgba(50, 50, 159, 0.1); line-height: 20px; font-size: 13px; border-radius: 2px;">
Expand Down Expand Up @@ -3363,6 +3371,7 @@ components:
minimum: 0
address: *addressId
amount: *amount
datum: *datum
assets: *walletAssets
redeemers:
description: A list of redeemers data with their purpose.
Expand Down

0 comments on commit c1620e3

Please sign in to comment.