Skip to content

Commit

Permalink
Better computation of minAda (#841)
Browse files Browse the repository at this point in the history
  • Loading branch information
berewt committed Nov 29, 2022
1 parent 228b59a commit 4aa6273
Show file tree
Hide file tree
Showing 39 changed files with 217 additions and 142 deletions.
18 changes: 9 additions & 9 deletions doc/plutus/tutorials/Auction.hs
Expand Up @@ -217,15 +217,15 @@ instance ContractModel AuctionModel where
where
p = s ^. contractState . phase
b = s ^. contractState . currentBid
validBid = choose ((b+1) `max` Ada.getLovelace Ledger.minAdaTxOut,
validBid = choose ((b+1) `max` Ada.getLovelace Ledger.minAdaTxOutEstimated,
b + Ada.getLovelace (Ada.adaOf 100))
{- START precondition -}
precondition s Init = s ^. contractState . phase == NotStarted
precondition s (Bid w bid) =
-- In order to place a bid, we need to satisfy the constraint where
-- each tx output must have at least N Ada.
s ^. contractState . phase /= NotStarted &&
bid >= Ada.getLovelace (Ledger.minAdaTxOut) &&
bid >= Ada.getLovelace (Ledger.minAdaTxOutEstimated) &&
bid > s ^. contractState . currentBid
{-END precondition -}
{- START nextReactiveState -}
Expand All @@ -236,7 +236,7 @@ instance ContractModel AuctionModel where
w <- viewContractState winner
bid <- viewContractState currentBid
phase .= AuctionOver
deposit w $ Ada.toValue Ledger.minAdaTxOut <> theToken
deposit w $ Ada.toValue Ledger.minAdaTxOutEstimated <> theToken
deposit w1 $ Ada.lovelaceValueOf bid
{- END nextReactiveState -}

Expand All @@ -249,7 +249,7 @@ instance ContractModel AuctionModel where
w <- viewContractState winner
bid <- viewContractState currentBid
phase .= AuctionOver
deposit w $ Ada.toValue Ledger.minAdaTxOut <> theToken
deposit w $ Ada.toValue Ledger.minAdaTxOutEstimated <> theToken
deposit w1 $ Ada.lovelaceValueOf bid
-- NEW!!!
w1change <- viewModelState $ balanceChange w1 -- since the start of the test
Expand All @@ -264,7 +264,7 @@ instance ContractModel AuctionModel where
case cmd of
Init -> do
phase .= Bidding
withdraw w1 $ Ada.toValue Ledger.minAdaTxOut <> theToken
withdraw w1 $ Ada.toValue Ledger.minAdaTxOutEstimated <> theToken
wait 3
Bid w bid -> do
currentPhase <- viewContractState phase
Expand Down Expand Up @@ -368,17 +368,17 @@ tests =
(assertDone seller (Trace.walletInstanceTag w1) (const True) "seller should be done"
.&&. assertDone (buyer threadToken) (Trace.walletInstanceTag w2) (const True) "buyer should be done"
.&&. assertAccumState (buyer threadToken) (Trace.walletInstanceTag w2) ((==) trace1FinalState ) "wallet 2 final state should be OK"
.&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> Ada.toValue trace1WinningBid <> inv theToken)
.&&. walletFundsChange w2 (Ada.toValue Ledger.minAdaTxOut <> inv (Ada.toValue trace1WinningBid) <> theToken))
.&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOutEstimated) <> Ada.toValue trace1WinningBid <> inv theToken)
.&&. walletFundsChange w2 (Ada.toValue Ledger.minAdaTxOutEstimated <> inv (Ada.toValue trace1WinningBid) <> theToken))
auctionTrace1
, checkPredicateOptions options "run an auction with multiple bids"
(assertDone seller (Trace.walletInstanceTag w1) (const True) "seller should be done"
.&&. assertDone (buyer threadToken) (Trace.walletInstanceTag w2) (const True) "buyer should be done"
.&&. assertDone (buyer threadToken) (Trace.walletInstanceTag w3) (const True) "3rd party should be done"
.&&. assertAccumState (buyer threadToken) (Trace.walletInstanceTag w2) ((==) trace2FinalState) "wallet 2 final state should be OK"
.&&. assertAccumState (buyer threadToken) (Trace.walletInstanceTag w3) ((==) trace2FinalState) "wallet 3 final state should be OK"
.&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOut) <> Ada.toValue trace2WinningBid <> inv theToken)
.&&. walletFundsChange w2 (Ada.toValue Ledger.minAdaTxOut <> inv (Ada.toValue trace2WinningBid) <> theToken)
.&&. walletFundsChange w1 (Ada.toValue (-Ledger.minAdaTxOutEstimated) <> Ada.toValue trace2WinningBid <> inv theToken)
.&&. walletFundsChange w2 (Ada.toValue Ledger.minAdaTxOutEstimated <> inv (Ada.toValue trace2WinningBid) <> theToken)
.&&. walletFundsChange w3 mempty)
auctionTrace2
, testProperty "QuickCheck property" $
Expand Down
6 changes: 3 additions & 3 deletions doc/plutus/tutorials/Escrow.hs
Expand Up @@ -20,7 +20,7 @@ import Data.Foldable (Foldable (fold))
import Data.Map (Map)
import Data.Map qualified as Map

import Ledger (minAdaTxOut)
import Ledger (minAdaTxOutEstimated)
import Ledger.Ada qualified as Ada
import Ledger.Typed.Scripts qualified as Scripts
import Ledger.Value qualified as Value
Expand Down Expand Up @@ -169,14 +169,14 @@ precondition s a = case a of
Redeem _ -> (s ^. contractState . contributions . to fold)
`geq`
(s ^. contractState . targets . to fold)
Pay _ v -> Ada.adaValueOf (fromInteger v) `geq` Ada.toValue minAdaTxOut
Pay _ v -> Ada.adaValueOf (fromInteger v) `geq` Ada.toValue minAdaTxOutEstimated
{- END precondition2 -}
-}
precondition s a = case a of
Redeem _ -> (s ^. CM.contractState . contributions . to fold) `Value.geq` (s ^. CM.contractState . targets . to fold)
--Redeem _ -> (s ^. contractState . contributions . to fold) == (s ^. contractState . targets . to fold)
--Refund w -> Nothing /= (s ^. contractState . contributions . at w)
Pay _ v -> Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOut
Pay _ v -> Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOutEstimated

{- START perform -}
perform h _ _ a = case a of
Expand Down
6 changes: 3 additions & 3 deletions doc/plutus/tutorials/Escrow2.hs
Expand Up @@ -24,7 +24,7 @@ import Data.Foldable (fold)
import Data.Map (Map)
import Data.Map qualified as Map

import Ledger (minAdaTxOut)
import Ledger (minAdaTxOutEstimated)
import Ledger.Ada qualified as Ada
import Ledger.Value qualified as Value
import Plutus.Contract (Contract, selectList)
Expand Down Expand Up @@ -130,7 +130,7 @@ instance CM.ContractModel EscrowModel where
{- START tightprecondition -}
precondition s a = case a of
Init tgts-> currentPhase == Initial
&& and [Ada.adaValueOf (fromInteger n) `geq` Ada.toValue minAdaTxOut | (w,n) <- tgts]
&& and [Ada.adaValueOf (fromInteger n) `geq` Ada.toValue minAdaTxOutEstimated | (w,n) <- tgts]
...
{- END tightprecondition -}
-}
Expand All @@ -140,7 +140,7 @@ instance CM.ContractModel EscrowModel where
Redeem _ -> currentPhase == Running
&& (s ^. CM.contractState . contributions . to fold) `Value.geq` (s ^. CM.contractState . targets . to fold)
Pay _ v -> currentPhase == Running
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOut
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOutEstimated
where currentPhase = s ^. CM.contractState . phase

{-
Expand Down
8 changes: 4 additions & 4 deletions doc/plutus/tutorials/Escrow3.hs
Expand Up @@ -23,7 +23,7 @@ import Data.Foldable (fold)
import Data.Map (Map)
import Data.Map qualified as Map

import Ledger (minAdaTxOut)
import Ledger (minAdaTxOutEstimated)
import Ledger.Ada qualified as Ada
import Ledger.Value qualified as Value
import Plutus.Contract (Contract, selectList)
Expand Down Expand Up @@ -103,12 +103,12 @@ instance CM.ContractModel EscrowModel where

precondition s a = case a of
Init tgts -> currentPhase == Initial
&& and [Ada.adaValueOf (fromInteger n) `Value.geq` Ada.toValue minAdaTxOut | (_,n) <- tgts]
&& and [Ada.adaValueOf (fromInteger n) `Value.geq` Ada.toValue minAdaTxOutEstimated | (_,n) <- tgts]
Redeem _ -> currentPhase == Running
&& fold (s ^. CM.contractState . contributions) `Value.geq` fold (s ^. CM.contractState . targets)
-- && fold (s ^. contractState . contributions) == fold (s ^. contractState . targets)
Pay _ v -> currentPhase == Running
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOut
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOutEstimated
Refund w -> currentPhase == Running
&& w `Map.member` (s ^. CM.contractState . contributions)
where currentPhase = s ^. CM.contractState . phase
Expand Down Expand Up @@ -230,7 +230,7 @@ finishingStrategy w = do
currentContribs <- viewContractState contributions
let deficit = fold currentTargets <> inv (fold currentContribs)
when (deficit `gt` Ada.adaValueOf 0) $
action $ Pay w $ round $ Ada.getAda $ max minAdaTxOut $ Ada.fromValue deficit
action $ Pay w $ round $ Ada.getAda $ max minAdaTxOutEstimated $ Ada.fromValue deficit
action $ Redeem w
{- END finishingStrategy -}
Expand Down
6 changes: 3 additions & 3 deletions doc/plutus/tutorials/Escrow4.hs
Expand Up @@ -24,7 +24,7 @@ import Data.Foldable (fold)
import Data.Map (Map)
import Data.Map qualified as Map

import Ledger (POSIXTime (POSIXTime), Slot (Slot, getSlot), minAdaTxOut)
import Ledger (POSIXTime (POSIXTime), Slot (Slot, getSlot), minAdaTxOutEstimated)
import Ledger.Ada qualified as Ada
import Ledger.Value qualified as Value
import Plutus.Contract (Contract, selectList)
Expand Down Expand Up @@ -132,11 +132,11 @@ instance CM.ContractModel EscrowModel where
precondition s a = case a of
Init s tgts -> currentPhase == Initial
&& s > 1
&& and [Ada.adaValueOf (fromInteger n) `Value.geq` Ada.toValue minAdaTxOut | (_,n) <- tgts]
&& and [Ada.adaValueOf (fromInteger n) `Value.geq` Ada.toValue minAdaTxOutEstimated | (_,n) <- tgts]
Redeem _ -> currentPhase == Running
&& fold (s ^. CM.contractState . contributions) `Value.geq` fold (s ^. CM.contractState . targets)
Pay _ v -> currentPhase == Running
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOut
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOutEstimated
Refund w -> currentPhase == Refunding -- NEW!!!
&& w `Map.member` (s ^. CM.contractState . contributions)
where currentPhase = s ^. CM.contractState . phase
Expand Down
6 changes: 3 additions & 3 deletions doc/plutus/tutorials/Escrow5.hs
Expand Up @@ -24,7 +24,7 @@ import Data.Foldable (fold)
import Data.Map (Map)
import Data.Map qualified as Map

import Ledger (POSIXTime (POSIXTime), Slot (Slot, getSlot), minAdaTxOut)
import Ledger (POSIXTime (POSIXTime), Slot (Slot, getSlot), minAdaTxOutEstimated)
import Ledger.Ada qualified as Ada
import Ledger.Value qualified as Value
import Plutus.Contract (Contract, selectList)
Expand Down Expand Up @@ -115,11 +115,11 @@ instance CM.ContractModel EscrowModel where
precondition s a = case a of
Init s tgts -> currentPhase == Initial
&& s > 1
&& and [Ada.adaValueOf (fromInteger n) `Value.geq` Ada.toValue minAdaTxOut | (_,n) <- tgts]
&& and [Ada.adaValueOf (fromInteger n) `Value.geq` Ada.toValue minAdaTxOutEstimated | (_,n) <- tgts]
Redeem _ -> currentPhase == Running
&& fold (s ^. CM.contractState . contributions) `Value.geq` fold (s ^. CM.contractState . targets)
Pay _ v -> currentPhase == Running
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOut
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOutEstimated
Refund w -> currentPhase == Refunding
&& w `Map.member` (s ^. CM.contractState . contributions)
where currentPhase = s ^. CM.contractState . phase
Expand Down
6 changes: 3 additions & 3 deletions doc/plutus/tutorials/Escrow6.hs
Expand Up @@ -32,7 +32,7 @@ import Data.Foldable (fold)
import Data.Map (Map)
import Data.Map qualified as Map

import Ledger (POSIXTime (POSIXTime), Slot (Slot, getSlot), minAdaTxOut)
import Ledger (POSIXTime (POSIXTime), Slot (Slot, getSlot), minAdaTxOutEstimated)
import Ledger.Ada qualified as Ada
import Ledger.Value qualified as Value
import Plutus.Contract (Contract, selectList)
Expand Down Expand Up @@ -127,11 +127,11 @@ instance CM.ContractModel EscrowModel where
precondition s a = case a of
Init s tgts -> currentPhase == Initial
&& s > 1
&& and [Ada.adaValueOf (fromInteger n) `Value.geq` Ada.toValue minAdaTxOut | (_,n) <- tgts]
&& and [Ada.adaValueOf (fromInteger n) `Value.geq` Ada.toValue minAdaTxOutEstimated | (_,n) <- tgts]
Redeem _ -> currentPhase == Running
&& fold (s ^. CM.contractState . contributions) `Value.geq` fold (s ^. CM.contractState . targets)
Pay _ v -> currentPhase == Running
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOut
&& Ada.adaValueOf (fromInteger v) `Value.geq` Ada.toValue minAdaTxOutEstimated
Refund w -> currentPhase == Refunding
&& w `Map.member` (s ^. CM.contractState . contributions)
where currentPhase = s ^. CM.contractState . phase
Expand Down
Expand Up @@ -307,7 +307,7 @@ handleAdjustUnbalancedTx =
RequestHandler $ \utx ->
surroundDebug @Text "handleAdjustUnbalancedTx" $ do
params <- Wallet.Effects.getClientParams
forM (adjustUnbalancedTx params utx) $ \(missingAdaCosts, adjusted) -> do
forM (adjustUnbalancedTx (emulatorPParams params) utx) $ \(missingAdaCosts, adjusted) -> do
logDebug $ AdjustingUnbalancedTx missingAdaCosts
pure adjusted

Expand Down
3 changes: 2 additions & 1 deletion plutus-contract/src/Wallet/API.hs
Expand Up @@ -132,7 +132,8 @@ payToAddress params range v addr = do
utx <- either (throwError . PaymentMkTxError)
pure
(Constraints.mkTxWithParams @Void params mempty constraints)
(missingAdaCosts, adjustedUtx) <- either (throwError . ToCardanoError) pure (adjustUnbalancedTx params utx)
(missingAdaCosts, adjustedUtx) <- either (throwError . ToCardanoError) pure
(adjustUnbalancedTx (emulatorPParams params) utx)
logDebug $ AdjustingUnbalancedTx missingAdaCosts
unless (utx == adjustedUtx) $
logWarn @Text $ "Wallet.API.payToPublicKeyHash: "
Expand Down
26 changes: 18 additions & 8 deletions plutus-contract/src/Wallet/Emulator/MultiAgent.hs
Expand Up @@ -35,7 +35,6 @@ import Data.Text.Extras (tshow)
import GHC.Generics (Generic)
import Prettyprinter (Pretty (pretty), colon, (<+>))

import Cardano.Api (NetworkId)
import Data.Foldable (fold)
import Ledger hiding (to, value)
import Ledger.Ada qualified as Ada
Expand Down Expand Up @@ -295,9 +294,10 @@ we create 10 Ada-only outputs per wallet here.

-- | Initialise the emulator state with a single pending transaction that
-- creates the initial distribution of funds to public key addresses.
emulatorStateInitialDist :: NetworkId -> Map PaymentPubKeyHash Value -> Either ToCardanoError EmulatorState
emulatorStateInitialDist networkId mp = do
outs <- traverse (toCardanoTxOut networkId) $ Map.toList mp >>= mkOutputs
emulatorStateInitialDist :: Params -> Map PaymentPubKeyHash Value -> Either ToCardanoError EmulatorState
emulatorStateInitialDist params mp = do
minAdaEmptyTxOut <- mMinAdaTxOut
outs <- traverse (toCardanoTxOut $ pNetworkId params) $ Map.toList mp >>= mkOutputs minAdaEmptyTxOut
let tx = mempty
{ txOutputs = TxOut <$> outs
, txMint = fold mp
Expand All @@ -307,13 +307,23 @@ emulatorStateInitialDist networkId mp = do
cTx = Validation.fromPlutusTxSigned def cUtxoIndex tx CW.knownPaymentKeys
pure $ emulatorStatePool [cTx]
where
-- we start with an empty TxOut and we adjust it to be sure that the containted Adas fit the size
-- of the TxOut
mMinAdaTxOut = do
let k = fst $ head $ Map.toList mp
emptyTxOut <- toCardanoTxOut (pNetworkId params) $ mkOutput k mempty
pure $ minAdaTxOut (emulatorPParams params) (TxOut emptyTxOut)
-- See [Creating wallets with multiple outputs]
mkOutputs (key, vl) = mkOutput key <$> splitInto10 vl
splitInto10 vl = if count <= 1 then [vl] else replicate (fromIntegral count) (Ada.toValue (ada `div` count)) ++ remainder
mkOutputs minAda (key, vl) = mkOutput key <$> splitInto10 vl minAda
splitInto10 vl minAda = if count <= 1
then [vl]
else replicate (fromIntegral count) (Ada.toValue (ada `div` count)) ++ remainder
where
ada = if Value.isAdaOnlyValue vl then Ada.fromValue vl else Ada.fromValue vl - minAdaTxOut
ada = if Value.isAdaOnlyValue vl
then Ada.fromValue vl
else Ada.fromValue vl - minAda
-- Make sure we don't make the outputs too small
count = min 10 $ ada `div` minAdaTxOut
count = min 10 $ ada `div` minAda
remainder = [ vl <> Ada.toValue (-ada) | not (Value.isAdaOnlyValue vl) ]
mkOutput key vl = V2.pubKeyHashTxOut vl (unPaymentPubKeyHash key)

Expand Down
5 changes: 2 additions & 3 deletions plutus-contract/src/Wallet/Emulator/Stream.hs
Expand Up @@ -54,7 +54,7 @@ import Streaming (Stream)
import Streaming qualified as S
import Streaming.Prelude (Of)
import Streaming.Prelude qualified as S
import Wallet.API (Params (pNetworkId), WalletAPIError)
import Wallet.API (Params, WalletAPIError)
import Wallet.Emulator (EmulatorEvent, EmulatorEvent')
import Wallet.Emulator qualified as EM
import Wallet.Emulator.Chain (ChainControlEffect, ChainEffect, _SlotAdd)
Expand Down Expand Up @@ -166,11 +166,10 @@ instance Default EmulatorConfig where

initialState :: EmulatorConfig -> EM.EmulatorState
initialState EmulatorConfig{..} = let
networkId = pNetworkId _params
withInitialWalletValues = either
(error . ("Cannot build the initial state: " <>) . show)
id
. EM.emulatorStateInitialDist networkId . Map.mapKeys EM.mockWalletPaymentPubKeyHash
. EM.emulatorStateInitialDist _params . Map.mapKeys EM.mockWalletPaymentPubKeyHash
signTx = onCardanoTx
(\t -> Validation.fromPlutusTxSigned _params cUtxoIndex t CW.knownPaymentKeys)
CardanoApiTx
Expand Down
4 changes: 2 additions & 2 deletions plutus-contract/src/Wallet/Emulator/Wallet.hs
Expand Up @@ -507,7 +507,7 @@ calculateTxChanges addr utxos (neg, pos) = do
$ toCardanoTxOut (pNetworkId params) $ PV2.TxOut addr pos PV2.NoOutputDatum Nothing
(missing, extraTxOut) <-
either (throwError . WAPI.ToCardanoError) pure
$ U.adjustTxOut params txOut
$ U.adjustTxOut (emulatorPParams params) txOut
let missingValue = Ada.toValue (fold missing)
-- Add the missing ada to both sides to keep the balance.
pure (neg <> missingValue, pos <> missingValue, Just extraTxOut)
Expand All @@ -525,7 +525,7 @@ calculateTxChanges addr utxos (neg, pos) = do
-- We have change so we need an extra output, if we didn't have that yet,
-- first make one with an estimated minimal amount of ada
-- which then will calculate a more exact set of inputs
then calculateTxChanges addr utxos (neg <> Ada.toValue Ledger.minAdaTxOut, Ada.toValue Ledger.minAdaTxOut)
then calculateTxChanges addr utxos (neg <> Ada.toValue Ledger.minAdaTxOutEstimated, Ada.toValue Ledger.minAdaTxOutEstimated)
-- Else recalculate with the change added to both sides
-- Ideally this creates the same inputs and outputs and then the change will be zero
-- But possibly the minimal Ada increases and then we also want to compute a new set of inputs
Expand Down

0 comments on commit 4aa6273

Please sign in to comment.