Skip to content

Commit

Permalink
Add an initial data-backed ScriptContext (IntersectMBO#6171)
Browse files Browse the repository at this point in the history
* Add an initial data-backed ScriptContext and ledger API
  • Loading branch information
ana-pantilie authored Aug 9, 2024
1 parent ab9cc56 commit c32b03a
Show file tree
Hide file tree
Showing 38 changed files with 5,662 additions and 38 deletions.
5 changes: 4 additions & 1 deletion plutus-benchmark/plutus-benchmark.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,10 @@ benchmark bls12-381-benchmarks
library script-contexts-internal
import: lang, ghc-version-support
hs-source-dirs: script-contexts/src
exposed-modules: PlutusBenchmark.ScriptContexts
exposed-modules:
PlutusBenchmark.Data.ScriptContexts
PlutusBenchmark.ScriptContexts

build-depends:
, base >=4.9 && <5
, plutus-ledger-api ^>=1.32
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}

module PlutusBenchmark.Data.ScriptContexts where

import PlutusLedgerApi.Data.V3 (OutputDatum (NoOutputDatum), PubKeyHash (..), Redeemer (..),
ScriptContext (..), ScriptInfo (SpendingScript), TxId (..),
TxInfo (..), TxOut (..), TxOutRef (..), always)
import PlutusLedgerApi.V1.Address
import PlutusLedgerApi.V1.Data.Value
import PlutusTx qualified
import PlutusTx.Builtins qualified as PlutusTx
import PlutusTx.Data.AssocMap qualified as Map
import PlutusTx.Plugin ()
import PlutusTx.Prelude qualified as PlutusTx

-- | A very crude deterministic generator for 'ScriptContext's with size
-- approximately proportional to the input integer.
mkScriptContext :: Int -> ScriptContext
mkScriptContext i =
ScriptContext
(mkTxInfo i)
(Redeemer (PlutusTx.toBuiltinData (1 :: Integer)))
(SpendingScript (TxOutRef (TxId "") 0) Nothing)


mkTxInfo :: Int -> TxInfo
mkTxInfo i = TxInfo {
txInfoInputs=mempty,
txInfoReferenceInputs=mempty,
txInfoOutputs=fmap mkTxOut [1..i],
txInfoFee=10000,
txInfoMint=mempty,
txInfoTxCerts=mempty,
txInfoWdrl=Map.empty,
txInfoValidRange=always,
txInfoSignatories=mempty,
txInfoRedeemers=Map.empty,
txInfoData=Map.empty,
txInfoId=TxId "",
txInfoVotes=Map.empty,
txInfoProposalProcedures=mempty,
txInfoCurrentTreasuryAmount=Nothing,
txInfoTreasuryDonation=Nothing
}

mkTxOut :: Int -> TxOut
mkTxOut i = TxOut {
txOutAddress=pubKeyHashAddress (PubKeyHash ""),
txOutValue=mkValue i,
txOutDatum=NoOutputDatum,
txOutReferenceScript=Nothing
}

mkValue :: Int -> Value
mkValue i = assetClassValue (assetClass adaSymbol adaToken) (fromIntegral i)

-- This example decodes the script context (which is O(size-of-context) work), and then also
-- does some work that's roughly proportional to the size of the script context (counting the
-- outputs). This should be a somewhat realistic example where a reasonable chunk of work is
-- done in addition to decoding.
{-# INLINABLE checkScriptContext1 #-}
checkScriptContext1 :: PlutusTx.BuiltinData -> ()
checkScriptContext1 d =
-- Bang pattern to ensure this is forced, probably not necesssary
-- since we do use it later
let !sc = PlutusTx.unsafeFromBuiltinData d
ScriptContext txi _ _ = sc
in
if PlutusTx.length (txInfoOutputs txi) `PlutusTx.modInteger` 2 PlutusTx.== 0
then ()
else PlutusTx.traceError "Odd number of outputs"

mkCheckScriptContext1Code :: ScriptContext -> PlutusTx.CompiledCode ()
mkCheckScriptContext1Code sc =
let d = PlutusTx.toBuiltinData sc
in
$$(PlutusTx.compile [|| checkScriptContext1 ||])
`PlutusTx.unsafeApplyCode`
PlutusTx.liftCodeDef d

-- This example aims to *force* the decoding of the script context and then ignore it entirely.
-- This corresponds to the unfortunate case where the decoding "wrapper" around a script forces
-- all the decoding work to be done even if it isn't used.
{-# INLINABLE checkScriptContext2 #-}
checkScriptContext2 :: PlutusTx.BuiltinData -> ()
checkScriptContext2 d =
let (sc :: ScriptContext) = PlutusTx.unsafeFromBuiltinData d
-- Just using a bang pattern was not enough to stop GHC from getting
-- rid of the dead binding before we even hit the plugin, this works
-- for now!
in case sc of
!_ ->
if 48 PlutusTx.* 9900 PlutusTx.== (475200 :: Integer)
then ()
else PlutusTx.traceError "Got my sums wrong"

mkCheckScriptContext2Code :: ScriptContext -> PlutusTx.CompiledCode ()
mkCheckScriptContext2Code sc =
let d = PlutusTx.toBuiltinData sc
in
$$(PlutusTx.compile [|| checkScriptContext2 ||])
`PlutusTx.unsafeApplyCode`
PlutusTx.liftCodeDef d

{- Note [Redundant arguments to equality benchmarks]
The arguments for the benchmarks are passed as terms created with `liftCodeDef`.
But the benchmark still needs to _evaluate_ these terms, which adds overhead that
distracts from the main point.
We can't easily remove the overhead, but we can at least include it in both cases to
make things fairer. Hence we include redundant arguments in the two cases to ensure
the same work is done in both cases. There is a third case that is just this overhead
for comparison.
-}

-- This example checks the script context for equality (with itself) when encoded as `Data`.
-- That means it just takes a single builtin call, which is fast (so long as the builtin is
-- costed cheaply).
{-# INLINABLE scriptContextEqualityData #-}
scriptContextEqualityData :: ScriptContext -> PlutusTx.BuiltinData -> ()
-- See Note [Redundant arguments to equality benchmarks]
scriptContextEqualityData _ d =
if PlutusTx.equalsData d d
then ()
else PlutusTx.traceError "The argument is not equal to itself"

mkScriptContextEqualityDataCode :: ScriptContext -> PlutusTx.CompiledCode ()
mkScriptContextEqualityDataCode sc =
let d = PlutusTx.toBuiltinData sc
in $$(PlutusTx.compile [|| scriptContextEqualityData ||])
`PlutusTx.unsafeApplyCode` PlutusTx.liftCodeDef sc
`PlutusTx.unsafeApplyCode` PlutusTx.liftCodeDef d

-- This example is just the overhead from the previous two
-- See Note [Redundant arguments to equality benchmarks]
{-# INLINABLE scriptContextEqualityOverhead #-}
scriptContextEqualityOverhead :: ScriptContext -> PlutusTx.BuiltinData -> ()
scriptContextEqualityOverhead _ _ = ()

mkScriptContextEqualityOverheadCode :: ScriptContext -> PlutusTx.CompiledCode ()
mkScriptContextEqualityOverheadCode sc =
let d = PlutusTx.toBuiltinData sc
in $$(PlutusTx.compile [|| scriptContextEqualityOverhead ||])
`PlutusTx.unsafeApplyCode` PlutusTx.liftCodeDef sc
`PlutusTx.unsafeApplyCode` PlutusTx.liftCodeDef d
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 215441185
| mem: 798229})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(constr 0)
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
({cpu: 68663201
| mem: 260597})
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
(constr 0)
Loading

0 comments on commit c32b03a

Please sign in to comment.