Skip to content

Commit b47fc69

Browse files
committed
[WIP] new V3 and PV11 profiles
1 parent e6fd9df commit b47fc69

10 files changed

Lines changed: 237 additions & 18 deletions

File tree

bench/cardano-profile/src/Cardano/Benchmarking/Profile/Builtin/Plutuscall.hs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@ profilesPlutuscall =
4444
. P.analysisStandard
4545
. P.desc "Small dataset, honest 15 epochs duration"
4646

47-
loop = plutusCall & V.plutusTypeLoop . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
48-
ecdsa = plutusCall & V.plutusTypeECDSA . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
49-
schnorr = plutusCall & V.plutusTypeSchnorr . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
47+
loop = plutusCall & V.plutusTypeLoop . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
48+
ecdsa = plutusCall & V.plutusTypeECDSA . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
49+
schnorr = plutusCall & V.plutusTypeSchnorr . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
50+
schnorrV3 = plutusCall & V.plutusTypeSchnorrV3 . V.plutusDoubleSaturation . P.analysisSizeModerate2 . P.analysisEpoch3Plus
51+
mscalmul = plutusCall & V.plutusTypeMultScalarMultG2 . V.plutusDoubleSaturation . P.analysisSizeModerate . P.analysisEpoch3Plus
5052

5153
loopVolt = plutusCall & V.plutusTypeLoop . V.plutusDoublePlusSaturation . P.analysisSizeSmall
5254
blstVolt = plutusCall & V.plutusTypeBLST . V.plutusDoublePlusSaturation . P.analysisSizeModerate2
@@ -65,5 +67,8 @@ profilesPlutuscall =
6567
, blstVolt & P.name "plutuscall-volt-blst" . postPlomin
6668
, ripemdVolt & P.name "plutuscall-volt-ripemd" . postPlomin
6769

70+
-- PlutusV3 benchmarks, targeting PV11
6871
, ripemdVolt & P.name "plutuscall-voltv11-ripemd" . postPlomin . P.v11Preview
72+
, schnorrV3 & P.name "plutuscall-voltv11-schnorrv3" . postPlomin . P.v11Preview
73+
, mscalmul & P.name "plutuscall-voltv11-mscalmul" . postPlomin . P.v11Preview
6974
]

bench/cardano-profile/src/Cardano/Benchmarking/Profile/Vocabulary.hs

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ module Cardano.Benchmarking.Profile.Vocabulary (
2020
, plutusSaturation, plutusDoubleSaturation, plutusDoublePlusSaturation
2121

2222
, plutusTypeLoop, plutusTypeLoopV3, plutusTypeLoop2024, plutusTypeECDSA, plutusTypeSchnorr
23-
, plutusTypeBLST, plutusTypeRIPEMD
23+
, plutusTypeBLST, plutusTypeRIPEMD, plutusTypeMultScalarMultG2
24+
, plutusTypeSchnorrV3
2425
) where
2526

2627
--------------------------------------------------------------------------------
@@ -250,3 +251,37 @@ plutusTypeRIPEMD =
250251
, KeyMap.fromList [("bytes", Aeson.String "5a56da88e6fd8419181dec4d3dd6997bab953d2f")]
251252
]
252253
. P.txFee 940000
254+
255+
-- the input data to the benchmark follows the rationale described in the script's source module:
256+
-- cf. bench/plutus-scripts-bench/src/Cardano/Benchmarking/PlutusScripts/MultiScalarMulG2.hs
257+
plutusTypeMultScalarMultG2 :: Types.Profile -> Types.Profile
258+
plutusTypeMultScalarMultG2 =
259+
P.plutusType "LimitTxPerBlock_4" . P.plutusScript "MultiScalarMulG2"
260+
. P.redeemerFields [
261+
KeyMap.fromList [("int", Aeson.Number 1000000.0)]
262+
, KeyMap.fromList [("list", Aeson.Array $ Vector.fromList [
263+
Aeson.Object $ KeyMap.fromList [("int", Aeson.Number 42148542872313659974763123524802260613429458928903366458240005849867442446806.0)]
264+
, Aeson.Object $ KeyMap.fromList [("int", Aeson.Number 43175885667968324076475204474312361068172079008185553631823030366527704308590.0)]
265+
, Aeson.Object $ KeyMap.fromList [("int", Aeson.Number 30296922462898274839287732105045363550642484192066614321073140284814517593443.0)]
266+
])
267+
]
268+
, KeyMap.fromList [("list", Aeson.Array $ Vector.fromList [
269+
Aeson.Object $ KeyMap.fromList [("bytes", Aeson.String "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8")]
270+
, Aeson.Object $ KeyMap.fromList [("bytes", Aeson.String "aa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053")]
271+
, Aeson.Object $ KeyMap.fromList [("bytes", Aeson.String "89380275bbc8e5dcea7dc4dd7e0550ff2ac480905396eda55062650f8d251c96eb480673937cc6d9d6a44aaa56ca66dc122915c824a0857e2ee414a3dccb23ae691ae54329781315a0c75df1c04d6d7a50a030fc866f09d516020ef82324afae")]
272+
, Aeson.Object $ KeyMap.fromList [("bytes", Aeson.String "870227d3f13684fdb7ce31b8065ba3acb35f7bde6fe2ddfefa359f8b35d08a9ab9537b43e24f4ffb720b5a0bda2a82f20e7a30979a8853a077454eb63b8dcee75f106221b262886bb8e01b0abb043368da82f60899cc1412e33e4120195fc557")]
273+
])
274+
]
275+
]
276+
. P.txFee 568200
277+
278+
plutusTypeSchnorrV3 :: Types.Profile -> Types.Profile
279+
plutusTypeSchnorrV3 =
280+
P.plutusType "LimitTxPerBlock_4" . P.plutusScript "SchnorrSecp256k1LoopV3"
281+
. P.redeemerFields [
282+
KeyMap.fromList [("int", Aeson.Number 1000000.0)]
283+
, KeyMap.fromList [("bytes", Aeson.String "599de3e582e2a3779208a210dfeae8f330b9af00a47a7fb22e9bb8ef596f301b")]
284+
, KeyMap.fromList [("bytes", Aeson.String "30303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030303030")]
285+
, KeyMap.fromList [("bytes", Aeson.String "5a56da88e6fd8419181dec4d3dd6997bab953d2fc71ab65e23cfc9e7e3d1a310613454a60f6703819a39fdac2a410a094442afd1fc083354443e8d8bb4461a9b")]
286+
]
287+
. P.txFee 586000

bench/plutus-scripts-bench/plutus-scripts-bench.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 3.0
22
name: plutus-scripts-bench
3-
version: 1.0.4.3
3+
version: 1.0.5.0
44
synopsis: Plutus scripts used for benchmarking
55
description: Plutus scripts used for benchmarking.
66
category: Cardano,
@@ -75,6 +75,7 @@ library
7575
Cardano.Benchmarking.PlutusScripts.HashOntoG2AndAdd
7676
Cardano.Benchmarking.PlutusScripts.Loop2024
7777
Cardano.Benchmarking.PlutusScripts.LoopV3
78+
Cardano.Benchmarking.PlutusScripts.MultiScalarMulG2
7879
Cardano.Benchmarking.PlutusScripts.Ripemd160
7980
Cardano.Benchmarking.PlutusScripts.SchnorrSecp256k1Loop
8081
Cardano.Benchmarking.PlutusScripts.SchnorrSecp256k1LoopV3

bench/plutus-scripts-bench/src/Cardano/Benchmarking/PlutusScripts.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import qualified Cardano.Benchmarking.PlutusScripts.EcdsaSecp256k1LoopV3 as ECDS
2929
import qualified Cardano.Benchmarking.PlutusScripts.HashOntoG2AndAdd as HashG2Add
3030
import qualified Cardano.Benchmarking.PlutusScripts.Loop2024 as Loop2024
3131
import qualified Cardano.Benchmarking.PlutusScripts.LoopV3 as LoopV3
32+
import qualified Cardano.Benchmarking.PlutusScripts.MultiScalarMulG2 as MultiScalarMulG2
3233
import qualified Cardano.Benchmarking.PlutusScripts.Ripemd160 as Ripemd160
3334
import qualified Cardano.Benchmarking.PlutusScripts.SchnorrSecp256k1Loop as Schnorr
3435
import qualified Cardano.Benchmarking.PlutusScripts.SchnorrSecp256k1LoopV3 as SchnorrV3
@@ -45,6 +46,7 @@ getAllScripts =
4546
, HashG2Add.script
4647
, Loop2024.script
4748
, LoopV3.script
49+
, MultiScalarMulG2.script
4850
, Ripemd160.script
4951
, Schnorr.script
5052
, SchnorrV3.script
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
{-# LANGUAGE BangPatterns #-}
2+
{-# LANGUAGE DataKinds #-}
3+
{-# LANGUAGE NoImplicitPrelude #-}
4+
{-# LANGUAGE TemplateHaskell #-}
5+
6+
module Cardano.Benchmarking.PlutusScripts.MultiScalarMulG2 (script) where
7+
8+
import Cardano.Api (PlutusScriptVersion (PlutusScriptV3))
9+
import Cardano.Benchmarking.ScriptAPI (PlutusBenchScript, mkPlutusBenchScript)
10+
import Language.Haskell.TH.Syntax (Exp (LitE), Lit (StringL), Loc (loc_module), qLocation)
11+
import PlutusLedgerApi.Common (serialiseCompiledCode)
12+
import qualified PlutusLedgerApi.V3 as PlutusV3
13+
import qualified PlutusTx (compile)
14+
import qualified PlutusTx.Builtins.Internal as BI (BuiltinList, head, snd, tail, unitval,
15+
unsafeDataAsConstr)
16+
import PlutusTx.Builtins as BI (bls12_381_G2_multiScalarMul)
17+
import PlutusTx.Prelude as Tx hiding (Semigroup (..), (.), (<$>))
18+
import Prelude as Haskell ((.), (<$>))
19+
20+
21+
script :: PlutusBenchScript
22+
script = mkPlutusBenchScript
23+
$(LitE . StringL . loc_module <$> qLocation)
24+
PlutusScriptV3
25+
(serialiseCompiledCode $$(PlutusTx.compile [|| mkValidator ||]))
26+
27+
{-# INLINABLE mkValidator #-}
28+
mkValidator :: BuiltinData -> BuiltinUnit
29+
mkValidator arg =
30+
if red_n < 1000000 -- large number ensures same bitsize for all counter values
31+
then traceError "redeemer is < 1000000"
32+
else loop (fmap Tx.bls12_381_G2_uncompress red_bss) red_is red_n
33+
where
34+
-- lazily decode script context up to redeemer, which is less expensive and results in much smaller tx size
35+
constrArgs :: BuiltinData -> BI.BuiltinList BuiltinData
36+
constrArgs = BI.snd . BI.unsafeDataAsConstr
37+
38+
redeemerFollowedByScriptInfo :: BI.BuiltinList BuiltinData
39+
redeemerFollowedByScriptInfo = BI.tail (constrArgs arg)
40+
41+
redeemer :: BuiltinData
42+
redeemer = BI.head redeemerFollowedByScriptInfo
43+
44+
red_n :: Integer
45+
red_is :: [Integer]
46+
red_bss :: [BuiltinByteString]
47+
(red_n, red_is, red_bss) = PlutusV3.unsafeFromBuiltinData redeemer
48+
49+
-- see Note[1]
50+
loop points scalars n
51+
| n == 1000000 = BI.unitval
52+
| otherwise = let !_ = BI.bls12_381_G2_multiScalarMul (n : scalars) points in loop points scalars (pred n)
53+
54+
55+
{-
56+
57+
Note[1]:
58+
59+
The benchmarking loop's counter will always be used as a nonce, prepended to the list of scalars.
60+
Hence, make sure that in the redeemer args,
61+
>> THE LIST OF SCALARS IS ALWAYS 1 ELEMENT SHORTER THAN THE LIST OF POINTS <<
62+
63+
== Reason for Nonce-as-Head ('n : scalars'):
64+
1. Defeats Pippenger Bucket-Caching: Mutating a single scalar
65+
head element breaks the windowed bit-partitioning configuration. This forces
66+
the 'blst' library to perform full, un-cached linear combination logic from
67+
scratch rather than reusing pre-computed bucket structures.
68+
2. Minmize execution units to achieve 1.: Prepending a head nonce element
69+
guarantees a predictable O(1) overhead, focusing execution cost purely on the underlying
70+
curve arithmetic. Also, this guarantees a stable memory footprint.
71+
72+
CIP-0133 Optimal 256-bit Scalars (Bounded by Curve Order 'r'):
73+
To prevent early-termination short-circuits in the underlying 'blst' library,
74+
we use high bit-density, non-trivial scalars strictly reduced modulo
75+
'r' (= 73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001) - see example below.
76+
77+
CIP-0133 Valid G2 Test Points (96-Byte Compressed Hex Literals):
78+
The points have been chosen to ensure maximum Montgomery multiplication stress in
79+
the F_p^2 field and prevent 'blst' from short-circuiting. They correspond to
80+
1. Official 1*G2 Base Generator, 2. 2*G2 Point, 3. 3*G2 Point and 4. 4*G2 Point - see example below.
81+
82+
Assuming you want to run a benchmark with input length 4, this would be the resulting redeemer:
83+
84+
{
85+
"constructor": 0,
86+
"fields": [
87+
{
88+
"int": 1000000
89+
},
90+
{
91+
"list": [
92+
{
93+
"int": 42148542872313659974763123524802260613429458928903366458240005849867442446806
94+
},
95+
{
96+
"int": 43175885667968324076475204474312361068172079008185553631823030366527704308590
97+
},
98+
{
99+
"int": 30296922462898274839287732105045363550642484192066614321073140284814517593443
100+
}
101+
]
102+
},
103+
{
104+
"list": [
105+
{
106+
"bytes": "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8"
107+
},
108+
{
109+
"bytes": "aa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053"
110+
},
111+
{
112+
"bytes": "89380275bbc8e5dcea7dc4dd7e0550ff2ac480905396eda55062650f8d251c96eb480673937cc6d9d6a44aaa56ca66dc122915c824a0857e2ee414a3dccb23ae691ae54329781315a0c75df1c04d6d7a50a030fc866f09d516020ef82324afae"
113+
},
114+
{
115+
"bytes": "870227d3f13684fdb7ce31b8065ba3acb35f7bde6fe2ddfefa359f8b35d08a9ab9537b43e24f4ffb720b5a0bda2a82f20e7a30979a8853a077454eb63b8dcee75f106221b262886bb8e01b0abb043368da82f60899cc1412e33e4120195fc557"
116+
}
117+
]
118+
}
119+
]
120+
}
121+
122+
>> BEWARE <<
123+
124+
The scalars are way out of range for safe representation in most JSON implementations: IEEE 754 double-precision (what most JSON parsers use for numbers) has a safe integer limit of roughly 16 digits.
125+
The scalars used here are 77 digits.
126+
127+
Consequences in practice:
128+
- JavaScript JSON.parse(), Python json.parse() silently lose precision; you get a wrong number with no error.
129+
- Make sure your tooling like nix, jq, dyff et al. does not exhibit the same issue, in case you process the redeemer as a JSON file.
130+
- Haskell's aeson parses it as Scientific or Integer (arbitrary precision); the scalars survive correctly.
131+
- For a Plutus redeemer value this doesn't matter at the JSON level because it is CBOR-encoded, and CBOR has native arbitrary-precision integer support.
132+
-}

bench/tx-generator/app/calibrate-script.hs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,10 @@ runScaling ::
206206
-> IO [(PlutusBudgetSummary, ScriptRedeemer)]
207207
runScaling script budgetType basePParams baseline baseBudget (scaleSanitize -> scalesArg)
208208
| null scalesArg = go1 scaleBaseLine scaleAutoFactors
209-
| otherwise = mapM go0 scalesArg
209+
| otherwise = catMaybes <$> mapM go0 scalesArg
210210
where
211211
-- specific scaling requested
212-
go0 :: Scale -> IO (PlutusBudgetSummary, ScriptRedeemer)
212+
go0 :: Scale -> IO (Maybe (PlutusBudgetSummary, ScriptRedeemer))
213213
go0 scale =
214214
let
215215
fields@[txm, txs, bm, bs] = scaleFields scale
@@ -218,22 +218,22 @@ runScaling script budgetType basePParams baseline baseBudget (scaleSanitize -> s
218218
budget = withHint (maximum fields) baseBudget
219219
in do
220220
putStrLn $ "--> run: " ++ show scope
221-
evaluate $
222-
summaryAndRedeermerOrDie scope $
223-
plutusAutoScaleBlockfit pparams (scriptNameExt, scope) script budget strategy 1
221+
summaryAndRedeermerSafe scope $
222+
plutusAutoScaleBlockfit pparams (scriptNameExt, scope) script budget strategy 1
224223

225224
-- auto-scale until conditions are met (last list entry), but include intermediate results
226225
go1 :: Scale -> [Double] -> IO [(PlutusBudgetSummary, ScriptRedeemer)]
227226
go1 _ [] = pure []
228227
go1 scale_ factors@(factor:fs) =
229228
case bumpLimit strategy budgetType factor scale_ of
230229
Nothing -> go1 scaleBaseLine fs -- no further bump possible: give up and auto-scale for next factor
231-
Just scale -> do
232-
run@(summary, _) <- go0 scale
233-
(run :) <$>
234-
if all (\cond -> cond summary baseline) happilyCalibrated
235-
then go1 scaleBaseLine fs -- auto-scale next factor
236-
else go1 scale factors -- apply next bump to current scaling, re-run
230+
Just scale -> go0 scale >>= \case
231+
Nothing -> pure [] -- if there was an error, skip all remaining scaling runs
232+
Just run@(summary, _) ->
233+
(run :) <$>
234+
if all (\cond -> cond summary baseline) happilyCalibrated
235+
then go1 scaleBaseLine fs -- auto-scale next factor
236+
else go1 scale factors -- apply next bump to current scaling, re-run
237237

238238
-- conditions for a succesful calibration: same limiting factors, same txn count per block
239239
-- NEXT RELEASE: does that need to be strategy dependent with TargetBlockExpenditure?
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"constructor": 0,
3+
"fields": [
4+
{
5+
"int": 1000000
6+
},
7+
{
8+
"list": [
9+
{
10+
"int": 42148542872313659974763123524802260613429458928903366458240005849867442446806
11+
},
12+
{
13+
"int": 43175885667968324076475204474312361068172079008185553631823030366527704308590
14+
},
15+
{
16+
"int": 30296922462898274839287732105045363550642484192066614321073140284814517593443
17+
}
18+
]
19+
},
20+
{
21+
"list": [
22+
{
23+
"bytes": "93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8"
24+
},
25+
{
26+
"bytes": "aa4edef9c1ed7f729f520e47730a124fd70662a904ba1074728114d1031e1572c6c886f6b57ec72a6178288c47c335771638533957d540a9d2370f17cc7ed5863bc0b995b8825e0ee1ea1e1e4d00dbae81f14b0bf3611b78c952aacab827a053"
27+
},
28+
{
29+
"bytes": "89380275bbc8e5dcea7dc4dd7e0550ff2ac480905396eda55062650f8d251c96eb480673937cc6d9d6a44aaa56ca66dc122915c824a0857e2ee414a3dccb23ae691ae54329781315a0c75df1c04d6d7a50a030fc866f09d516020ef82324afae"
30+
},
31+
{
32+
"bytes": "870227d3f13684fdb7ce31b8065ba3acb35f7bde6fe2ddfefa359f8b35d08a9ab9537b43e24f4ffb720b5a0bda2a82f20e7a30979a8853a077454eb63b8dcee75f106221b262886bb8e01b0abb043368da82f60899cc1412e33e4120195fc557"
33+
}
34+
]
35+
}
36+
]
37+
}

bench/tx-generator/src-calibrate/Cardano/TxGenerator/Calibrate/Utils.hs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,13 @@ summaryAndRedeermerOrDie scope =
4141
(\(summary, budget, _) -> (summary, autoBudgetRedeemer budget))
4242
where withScope s = concat [scope, ": ", s]
4343

44+
summaryAndRedeermerSafe :: String -> Either TxGenError (PlutusBudgetSummary, PlutusAutoBudget, b) -> IO (Maybe (PlutusBudgetSummary, ScriptRedeemer))
45+
summaryAndRedeermerSafe scope =
46+
either
47+
(\err -> putStrLn (withScope $ show err) >> pure Nothing)
48+
(\(summary, budget, _) -> pure (Just (summary, autoBudgetRedeemer budget)))
49+
where withScope s = concat [scope, ": ", s]
50+
4451
resolveRedeemer :: Either ScriptData TxGenPlutusParams -> IO (Either TxGenError HashableScriptData)
4552
resolveRedeemer = resolveRedeemerQuiet False
4653

bench/tx-generator/tx-generator.cabal

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ common with-library
3030
-- It makes sure, we only depend on that package if it is buildable.
3131
-- The tx-generator will fall back to pre-serialized Plutus scripts if this package is not present.
3232
if flag(withplutuslib) && !(impl(ghc <9.6) || impl(ghc >=9.7))
33-
build-depends: plutus-scripts-bench ^>= 1.0.4
33+
build-depends: plutus-scripts-bench ^>= 1.0.5
3434
cpp-options: -DWITH_LIBRARY
3535

3636
common maybe-unix

0 commit comments

Comments
 (0)