Skip to content

Commit c76d023

Browse files
committed
Add more tests for reference script size computation in BBODY
1 parent 3969590 commit c76d023

File tree

1 file changed

+179
-4
lines changed
  • eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp

1 file changed

+179
-4
lines changed

eras/conway/impl/testlib/Test/Cardano/Ledger/Conway/Imp/BbodySpec.hs

Lines changed: 179 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,46 +3,56 @@
33
{-# LANGUAGE FlexibleContexts #-}
44
{-# LANGUAGE NumericUnderscores #-}
55
{-# LANGUAGE OverloadedLists #-}
6+
{-# LANGUAGE PatternSynonyms #-}
67
{-# LANGUAGE ScopedTypeVariables #-}
78
{-# LANGUAGE TypeApplications #-}
89

910
module Test.Cardano.Ledger.Conway.Imp.BbodySpec (
1011
spec,
1112
) where
1213

14+
import Cardano.Ledger.Alonzo.Rules (AlonzoUtxosPredFailure)
1315
import Cardano.Ledger.BHeaderView (BHeaderView (..))
1416
import Cardano.Ledger.Babbage.Core
15-
import Cardano.Ledger.BaseTypes (BlocksMade (..), Mismatch (..), ProtVer (..))
17+
import Cardano.Ledger.BaseTypes (BlocksMade (..), Mismatch (..), ProtVer (..), natVersion)
1618
import Cardano.Ledger.Block
1719
import Cardano.Ledger.Coin (Coin (..))
1820
import Cardano.Ledger.Conway.Rules (
1921
ConwayBbodyPredFailure (..),
2022
maxRefScriptSizePerBlock,
2123
maxRefScriptSizePerTx,
24+
totalRefScriptSizeInBlock,
2225
)
2326
import Cardano.Ledger.Conway.State
24-
import Cardano.Ledger.Plutus (SLanguage (..))
27+
import Cardano.Ledger.Plutus (SLanguage (..), hashPlutusScript)
2528
import Cardano.Ledger.Shelley.LedgerState
2629
import Cardano.Ledger.Shelley.Rules (
2730
BbodyEnv (..),
2831
Event,
2932
ShelleyBbodyState (..),
3033
)
34+
import Cardano.Ledger.Shelley.Scripts (
35+
pattern RequireSignature,
36+
)
37+
import Cardano.Ledger.TxIn
3138
import Control.Monad (forM)
39+
import Data.List.NonEmpty (NonEmpty (..))
3240
import qualified Data.List.NonEmpty as NE
3341
import qualified Data.Map as Map
3442
import qualified Data.Sequence.Strict as SSeq
35-
import Lens.Micro ((^.))
43+
import Lens.Micro ((&), (.~), (^.))
3644
import Lens.Micro.Mtl (use)
3745
import Test.Cardano.Ledger.Babbage.ImpTest
46+
import Test.Cardano.Ledger.Core.Utils (txInAt)
3847
import Test.Cardano.Ledger.Imp.Common
39-
import Test.Cardano.Ledger.Plutus.Examples (purposeIsWellformedNoDatum)
48+
import Test.Cardano.Ledger.Plutus.Examples (alwaysFailsNoDatum, purposeIsWellformedNoDatum)
4049

4150
spec ::
4251
forall era.
4352
( AlonzoEraImp era
4453
, BabbageEraTxBody era
4554
, InjectRuleFailure "BBODY" ConwayBbodyPredFailure era
55+
, InjectRuleFailure "LEDGER" AlonzoUtxosPredFailure era
4656
, ToExpr (Event (EraRule "BBODY" era))
4757
) =>
4858
SpecWith (ImpInit (LedgerSpec era))
@@ -87,6 +97,170 @@ spec = do
8797
}
8898
)
8999
]
100+
101+
it "BodyRefScriptsSizeTooBig with reference scripts in the same block" $
102+
whenMajorVersionAtLeast @11 $ do
103+
Just plutusScript <- pure $ mkPlutusScript @era $ purposeIsWellformedNoDatum SPlutusV2
104+
let scriptSize = originalBytesSize plutusScript
105+
106+
txScriptCounts <-
107+
genNumAdditionsExceeding
108+
scriptSize
109+
maxRefScriptSizePerTx
110+
maxRefScriptSizePerBlock
111+
112+
let expectedTotalRefScriptSize = scriptSize * sum txScriptCounts
113+
114+
-- We are creating reference scripts and transaction that depend on them in a "simulation",
115+
-- so the result will be correctly constructed that are not applied to the ledger state
116+
txs :: [Tx era] <- simulateThenRestore $ do
117+
concat
118+
<$> forM
119+
txScriptCounts
120+
( \n -> do
121+
-- produce reference scripts
122+
refScriptTxs <-
123+
replicateM n (produceRefScriptsTx (fromPlutusScript plutusScript :| []))
124+
125+
-- spend using the reference scripts
126+
let txIns = (`mkTxInPartial` 0) . txIdTx <$> refScriptTxs
127+
rootIn <- fst <$> getImpRootTxOut
128+
spendTx <- submitTxWithRefInputs rootIn (NE.fromList txIns)
129+
pure $ refScriptTxs ++ [spendTx]
130+
)
131+
132+
predFailures <- expectLeftExpr =<< tryRunBBODY txs
133+
predFailures
134+
`shouldBe` NE.fromList
135+
[ injectFailure
136+
( BodyRefScriptsSizeTooBig $
137+
Mismatch
138+
{ mismatchSupplied = expectedTotalRefScriptSize
139+
, mismatchExpected = maxRefScriptSizePerBlock
140+
}
141+
)
142+
]
143+
144+
it "totalRefScriptSizeInBlock" $ do
145+
script <- RequireSignature @era <$> freshKeyHash
146+
let scriptSize = originalBytesSize script
147+
scriptSpendIn <- impAddNativeScript script >>= produceScript
148+
scriptSpendIn2 <- impAddNativeScript script >>= produceScript
149+
protVer <- getsPParams ppProtocolVersionL
150+
151+
-- We want to verify that the total size of reference scripts in a list of transactions
152+
-- remains unchanged before and after applying them to the ledger state.
153+
-- To do this, we generate the expected transactions, simulate submitting them to obtain
154+
-- their individual reference script sizes, and then restore the original state -
155+
-- meaning the transactions are not actually applied.
156+
-- Finally, we check that the accumulated sizes from both before and after match.
157+
txsWithRefScriptSizes :: ([(Tx era, Int)], Int) <- simulateThenRestore $ do
158+
let mkTxWithExpectedSize expectedSize txAction = do
159+
tx <- txAction
160+
totalRefScriptSizeInBlock protVer [tx] <$> getUTxO `shouldReturn` expectedSize
161+
pure (tx, expectedSize)
162+
163+
-- submit reference scripts
164+
refScriptTx1 <-
165+
mkTxWithExpectedSize 0 $
166+
produceRefScriptsTx (fromNativeScript script :| [])
167+
let refScriptTx1In = txInAt 0 (fst refScriptTx1)
168+
refScriptTx2 <-
169+
mkTxWithExpectedSize 0 $
170+
produceRefScriptsTx (fromNativeScript script :| [])
171+
-- spend script using the reference script
172+
spendScriptWithRefScriptTx <-
173+
mkTxWithExpectedSize scriptSize $
174+
submitTxWithRefInputs scriptSpendIn [refScriptTx1In]
175+
-- spend using two ref inputs
176+
spendScriptWithTwoRefScriptsTx <-
177+
mkTxWithExpectedSize (2 * scriptSize) $
178+
submitTxWithRefInputs scriptSpendIn2 [refScriptTx1In, txInAt 0 (fst refScriptTx2)]
179+
-- spend the root utxo
180+
rootIn <- fst <$> getImpRootTxOut
181+
spendRootUtxoTx <-
182+
mkTxWithExpectedSize scriptSize $
183+
submitTxWithRefInputs rootIn [refScriptTx1In]
184+
-- spend the reference script itself
185+
-- We must check the size without submitting the transaction,
186+
-- since applying it removes the reference script from the UTxO
187+
spendRefScriptTx <-
188+
mkTxWithExpectedSize scriptSize $
189+
fixupTx $
190+
mkTxWithRefInputs refScriptTx1In (NE.fromList [refScriptTx1In])
191+
192+
let txsWithRefScriptSizes =
193+
[ refScriptTx1
194+
, refScriptTx2
195+
, spendScriptWithRefScriptTx
196+
, spendScriptWithTwoRefScriptsTx
197+
, spendRootUtxoTx
198+
, spendRefScriptTx
199+
]
200+
201+
-- check and return the accumulated reference script size of all transactions,
202+
-- so we can check that the same sum for the unapplied transactions matches
203+
let expectedTotalRefScriptSize = 5 * scriptSize
204+
totalRefScriptSizeInBlock protVer (SSeq.fromList (fst <$> txsWithRefScriptSizes))
205+
<$> getUTxO `shouldReturn` expectedTotalRefScriptSize
206+
pure (txsWithRefScriptSizes, expectedTotalRefScriptSize)
207+
208+
let (txWithSizes, expectedTotalSize) = txsWithRefScriptSizes
209+
210+
-- for each prefix of the list, the accumulated sum should match the sum of the applied transactions
211+
forM_ ([1 .. length txWithSizes] :: [Int]) $ \ix -> do
212+
let slice = take ix txWithSizes
213+
214+
totalRefScriptSizeInBlock protVer (SSeq.fromList (fst <$> slice))
215+
<$> getUTxO
216+
`shouldReturn` (if isPostV10 protVer then sum (snd <$> slice) else 0)
217+
218+
totalRefScriptSizeInBlock protVer (SSeq.fromList (fst <$> txWithSizes))
219+
<$> getUTxO
220+
`shouldReturn` (if isPostV10 protVer then expectedTotalSize else 0)
221+
222+
-- disabled in conformance because submiting phase2-invalid transactions are not supported atm
223+
disableImpInitExpectLedgerRuleConformance $
224+
it "Use a reference script in a collateral output" $ do
225+
protVer <- getsPParams ppProtocolVersionL
226+
227+
-- produce an utxo with a failing script
228+
failingPlutusTxIn <- do
229+
let plutus = alwaysFailsNoDatum SPlutusV3
230+
produceScript $ hashPlutusScript plutus
231+
232+
-- produce a utxo with a succeeding script
233+
script <- RequireSignature @era <$> freshKeyHash
234+
scriptTxIn <- impAddNativeScript script >>= produceScript
235+
let scriptSize = originalBytesSize script
236+
237+
-- prepare a txout with the succeeding script as reference script
238+
collRefScriptTxOut <- do
239+
addr <- freshKeyAddr_
240+
pure $ mkBasicTxOut addr mempty & referenceScriptTxOutL .~ pure (fromNativeScript script)
241+
242+
(txs :: [Tx era]) <- simulateThenRestore $ do
243+
-- submit an invalid transaction which attempts to consume the failing script
244+
-- and specifies as collateral return the txout with reference script
245+
createCollateralTx <-
246+
submitPhase2Invalid $
247+
mkBasicTx
248+
( mkBasicTxBody
249+
& inputsTxBodyL .~ [failingPlutusTxIn]
250+
& collateralReturnTxBodyL .~ pure collRefScriptTxOut
251+
)
252+
totalRefScriptSizeInBlock protVer [createCollateralTx] <$> getUTxO `shouldReturn` 0
253+
254+
-- consume the script, passing the output from the previous collateral as reference input
255+
let refScriptTxIn = txInAt 1 createCollateralTx
256+
useCollateralTx <- submitTxWithRefInputs scriptTxIn [refScriptTxIn]
257+
totalRefScriptSizeInBlock protVer [createCollateralTx, useCollateralTx]
258+
<$> getUTxO `shouldReturn` scriptSize
259+
pure [createCollateralTx, useCollateralTx]
260+
261+
totalRefScriptSizeInBlock protVer (SSeq.fromList txs)
262+
<$> getUTxO
263+
`shouldReturn` (if isPostV10 protVer then scriptSize else 0)
90264
where
91265
tryRunBBODY txs = do
92266
let txSeq = toTxSeq @era $ SSeq.fromList txs
@@ -107,6 +281,7 @@ spec = do
107281
(BbodyEnv pp (nes ^. chainAccountStateL))
108282
(BbodyState ls (BlocksMade Map.empty))
109283
(Block bhView txSeq)
284+
isPostV10 protVer = pvMajor protVer >= natVersion @11
110285

111286
-- Generate a list of integers such that the sum of their multiples by scale is greater than toExceed
112287
-- and each individual value multiplied by the scale is smaller than maxSingle

0 commit comments

Comments
 (0)