Skip to content

Commit 472e62f

Browse files
authored
Merge pull request #26 from IntersectMBO/yura/issue-24-define-2-party-escrow
feat: define Two-Party Escrow benchmark scenario
2 parents 29a5669 + afe461d commit 472e62f

File tree

29 files changed

+2361
-365
lines changed

29 files changed

+2361
-365
lines changed

README.md

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,14 @@ cape benchmark list
8383

8484
# View a specific benchmark
8585
cape benchmark fibonacci
86+
cape benchmark two-party-escrow
87+
88+
# Generate JSON statistics for all benchmarks
89+
cape benchmark stats
8690

8791
# Create a submission for your compiler
8892
cape submission new fibonacci MyCompiler 1.0.0 myhandle
93+
cape submission new two-party-escrow MyCompiler 1.0.0 myhandle
8994
```
9095

9196
---
@@ -102,7 +107,7 @@ Latest benchmark reports: [UPLC-CAPE Reports](https://intersectmbo.github.io/UPL
102107
| --- | --- | --- | --- |
103108
| [Fibonacci](scenarios/fibonacci.md) | Synthetic | Recursive algorithm performance | Ready |
104109
| [Factorial](scenarios/factorial.md) | Synthetic | Recursive algorithm performance | Ready |
105-
| Two-Party Escrow | Real-world | Smart contract scenario | Planned |
110+
| [Two-Party Escrow](scenarios/two-party-escrow.md) | Real-world | Smart contract escrow validator | Ready |
106111
| Streaming Payments | Real-world | Payment channel implementation | Planned |
107112
| Simple DAO Voting | Real-world | Governance mechanism | Planned |
108113
| Time-locked Staking | Real-world | Staking protocol | Planned |
@@ -119,6 +124,7 @@ For the full and up-to-date command reference, see [USAGE.md](USAGE.md).
119124
# Benchmarks
120125
cape benchmark list # List all benchmarks
121126
cape benchmark <name> # Show benchmark details
127+
cape benchmark stats # Generate JSON statistics for all benchmarks
122128
cape benchmark new <name> # Create a new benchmark from template
123129

124130
# Submissions
@@ -132,6 +138,23 @@ cape submission report <name> # Generate HTML report for a benchmark
132138
cape submission report --all # Generate HTML reports for all benchmarks
133139
```
134140

141+
### JSON Statistics
142+
143+
The `cape benchmark stats` command generates comprehensive JSON data for all benchmarks:
144+
145+
```zsh
146+
# Output JSON statistics to console
147+
cape benchmark stats
148+
149+
# Save to file
150+
cape benchmark stats > stats.json
151+
152+
# Use with jq for filtering
153+
cape benchmark stats | jq '.benchmarks[] | select(.submission_count > 0)'
154+
```
155+
156+
The output includes formatted metrics, best value indicators, and submission metadata, making it ideal for generating custom reports or integrating with external tools.
157+
135158
### Interactive prompts
136159

137160
```zsh
@@ -275,14 +298,18 @@ cape submission new fibonacci # Prompts for compiler, version, handle
275298
UPLC-CAPE/
276299
├── scenarios/ # Benchmark specifications
277300
│ ├── TEMPLATE/ # Template for new scenarios
278-
│ └── fibonacci.md
301+
│ ├── fibonacci.md
302+
│ ├── factorial.md
303+
│ └── two-party-escrow.md
279304
├── submissions/ # Compiler submissions (per scenario)
280305
│ ├── TEMPLATE/ # Templates and schemas
281306
│ │ ├── metadata.schema.json
282307
│ │ ├── metadata-template.json
283308
│ │ ├── metrics.schema.json
284309
│ │ └── metrics-template.json
285-
│ └── fibonacci/
310+
│ ├── fibonacci/
311+
│ │ └── MyCompiler_1.0.0_handle/
312+
│ └── two-party-escrow/
286313
│ └── MyCompiler_1.0.0_handle/
287314
├── scripts/ # Project CLI tooling
288315
│ ├── cape.sh # Main CLI

flake.lock

Lines changed: 12 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

measure/app/App/Cli.hs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
module App.Cli (
2+
Options (..),
3+
parseOptions,
4+
) where
5+
6+
import Options.Applicative
7+
import Prelude
8+
9+
-- | CLI options
10+
-- -i: input UPLC file
11+
-- -o: output metrics.json file
12+
-- -v: optional verifier UPLC file (applied as (verifier submissionResult))
13+
-- -d: optional driver UPLC file for validator testing with overhead calculation
14+
data Options = Options
15+
{ optInput :: FilePath
16+
, optOutput :: FilePath
17+
, optVerifier :: Maybe FilePath
18+
, optDriver :: Maybe FilePath
19+
}
20+
21+
optionsParser :: Parser Options
22+
optionsParser =
23+
Options
24+
<$> strOption
25+
(long "input" <> short 'i' <> metavar "FILE" <> help "UPLC input file")
26+
<*> strOption
27+
( long "output" <> short 'o' <> metavar "FILE" <> help "metrics.json output file"
28+
)
29+
<*> optional
30+
( strOption
31+
( long "verifier"
32+
<> short 'v'
33+
<> metavar "VERIFIER.uplc"
34+
<> help
35+
"Verifier UPLC file applied as (verifier submissionResult) for correctness verification"
36+
)
37+
)
38+
<*> optional
39+
( strOption
40+
( long "driver"
41+
<> short 'd'
42+
<> metavar "DRIVER.uplc"
43+
<> help
44+
"Driver UPLC file for validator testing with overhead calculation"
45+
)
46+
)
47+
48+
optsInfo :: ParserInfo Options
49+
optsInfo =
50+
info
51+
(optionsParser <**> helper)
52+
( fullDesc
53+
<> progDesc
54+
"Measure a UPLC program and optionally verify correctness using a provided verifier UPLC file or test validators using a driver UPLC file"
55+
<> header "uplc-measure"
56+
)
57+
58+
-- | Parse command-line options
59+
parseOptions :: IO Options
60+
parseOptions = execParser optsInfo

measure/app/App/Error.hs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
module App.Error (
2+
MeasureError (..),
3+
renderMeasureError,
4+
exitCodeForError,
5+
) where
6+
7+
import Control.Exception qualified as E
8+
import System.Exit (ExitCode (..))
9+
import Prelude
10+
11+
-- | Custom exception types for business logic errors
12+
data MeasureError
13+
= FileNotFoundError FilePath
14+
| FileDecodeError FilePath
15+
| UPLCParseError FilePath String
16+
| EvaluationError String
17+
| VerificationError String
18+
| DriverError String
19+
deriving stock (Show)
20+
21+
instance E.Exception MeasureError where
22+
displayException = renderMeasureError
23+
24+
-- | Render MeasureError to user-friendly message
25+
renderMeasureError :: MeasureError -> String
26+
renderMeasureError err = case err of
27+
FileNotFoundError path ->
28+
"Error: File not found: " ++ path
29+
FileDecodeError path ->
30+
"Error: Failed to decode UTF-8 text: " ++ path
31+
UPLCParseError path details ->
32+
"Error: Malformed/invalid UPLC file: " ++ path ++ "\n" ++ details
33+
EvaluationError details ->
34+
"Error: Evaluation failed: " ++ details
35+
VerificationError details ->
36+
"Error: Verification failed: " ++ details
37+
DriverError details ->
38+
"Error: Driver operation failed: " ++ details
39+
40+
-- | Get exit code for MeasureError
41+
exitCodeForError :: MeasureError -> ExitCode
42+
exitCodeForError err = case err of
43+
FileNotFoundError _ -> ExitFailure 2
44+
FileDecodeError _ -> ExitFailure 4
45+
UPLCParseError _ _ -> ExitFailure 4
46+
EvaluationError _ -> ExitFailure 3
47+
VerificationError _ -> ExitFailure 2
48+
DriverError _ -> ExitFailure 3

measure/app/App/Fixture.hs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module App.Fixture (dummyValidator) where
2+
3+
import PlutusCore qualified as PLC
4+
import PlutusCore.MkPlc (mkConstant)
5+
import UntypedPlutusCore qualified as UPLC
6+
import UntypedPlutusCore.Core.Type (Term (..))
7+
8+
-- | Dummy validator UPLC program that ignores input and returns unit
9+
dummyValidator ::
10+
UPLC.Program UPLC.Name PLC.DefaultUni PLC.DefaultFun PLC.SrcSpan
11+
dummyValidator =
12+
let ann = PLC.SrcSpan "" 1 1 1 1
13+
-- Create a lambda that ignores its input and returns unit constant
14+
-- \(x : Data) -> ()
15+
inputName = UPLC.Name "x" (PLC.Unique 0)
16+
unitConstant = mkConstant ann ()
17+
lambdaTerm = LamAbs ann inputName unitConstant
18+
version = PLC.Version 1 1 0
19+
in UPLC.Program ann version lambdaTerm

measure/app/GenerateVerifiers.hs

Lines changed: 46 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,4 @@
1-
{-# LANGUAGE BangPatterns #-}
2-
{-# LANGUAGE BlockArguments #-}
3-
{-# LANGUAGE DataKinds #-}
4-
{-# LANGUAGE LambdaCase #-}
5-
{-# LANGUAGE MultiParamTypeClasses #-}
6-
{-# LANGUAGE MultiWayIf #-}
7-
{-# LANGUAGE NamedFieldPuns #-}
8-
{-# LANGUAGE OverloadedStrings #-}
9-
{-# LANGUAGE PatternSynonyms #-}
101
{-# LANGUAGE Strict #-}
11-
{-# LANGUAGE TemplateHaskell #-}
12-
{-# LANGUAGE ViewPatterns #-}
13-
{-# LANGUAGE NoImplicitPrelude #-}
142
--
153
{-# OPTIONS_GHC -fno-full-laziness #-}
164
{-# OPTIONS_GHC -fno-ignore-interface-pragmas #-}
@@ -26,14 +14,15 @@
2614
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:remove-trace #-}
2715
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:target-version=1.1.0 #-}
2816

29-
module Main where
17+
module Main (main) where
3018

3119
import Data.Text (Text)
3220
import Data.Text qualified as Text
3321
import Data.Text.IO qualified as Text
3422
import PlutusCore.Pretty qualified as PP
3523
import PlutusCore.Quote (runQuoteT)
3624
import PlutusTx qualified as Tx
25+
import PlutusTx.Builtins.Internal (unitval)
3726
import PlutusTx.Prelude
3827
import System.Environment (getArgs)
3928
import System.Exit (exitFailure)
@@ -50,11 +39,36 @@ verifyEquals42 n = check (n == 42)
5039
verifyEquals42Compiled :: Tx.CompiledCode (Integer -> BuiltinUnit)
5140
verifyEquals42Compiled = $$(Tx.compile [||verifyEquals42||])
5241

53-
-- | Generate UPLC text for the verifier
54-
generateVerifierUPLC :: IO Text
55-
generateVerifierUPLC = do
56-
let compiled = verifyEquals42Compiled
57-
uplcProgramWithDeBruijn = Tx.getPlcNoAnn compiled
42+
-- | Dummy validator that always returns unit regardless of input
43+
dummyValidator :: BuiltinData -> BuiltinUnit
44+
dummyValidator _ = unitval
45+
46+
-- | Compile the dummy validator to UPLC
47+
dummyValidatorCompiled :: Tx.CompiledCode (BuiltinData -> BuiltinUnit)
48+
dummyValidatorCompiled = $$(Tx.compile [||dummyValidator||])
49+
50+
-- | Test validator for integration testing (accepts integer 42 as BuiltinData)
51+
testValidator :: BuiltinData -> BuiltinUnit
52+
testValidator input =
53+
case Tx.fromBuiltinData input of
54+
Just (42 :: Integer) -> unitval
55+
_ -> traceError "Invalid input: expected 42"
56+
57+
-- | Compile the test validator to UPLC
58+
testValidatorCompiled :: Tx.CompiledCode (BuiltinData -> BuiltinUnit)
59+
testValidatorCompiled = $$(Tx.compile [||testValidator||])
60+
61+
-- | Generate UPLC text for the specified subject
62+
generateUPLC :: Text -> IO Text
63+
generateUPLC subject = do
64+
uplcProgramWithDeBruijn <- case subject of
65+
"equals42" -> return $ Tx.getPlcNoAnn verifyEquals42Compiled
66+
"dummy-validator" -> return $ Tx.getPlcNoAnn dummyValidatorCompiled
67+
"test-validator" -> return $ Tx.getPlcNoAnn testValidatorCompiled
68+
_ -> do
69+
putStrLn $ "Error: Unknown subject '" ++ Text.unpack subject ++ "'"
70+
putStrLn "Available subjects: equals42, dummy-validator, test-validator"
71+
exitFailure
5872

5973
-- Convert DeBruijn names to regular names to be able to parse it
6074
-- using stock parser from the UntypedPlutusCore library.
@@ -74,23 +88,27 @@ generateVerifierUPLC = do
7488
P.Right prettyUplc ->
7589
P.return P.$ Text.pack P.$ show prettyUplc
7690

77-
-- | Write the verifier UPLC to a file
78-
writeVerifierToFile :: FilePath -> IO ()
79-
writeVerifierToFile filepath = do
80-
uplcText <- generateVerifierUPLC
91+
-- | Write the UPLC program to a file
92+
writeUPLCToFile :: Text -> FilePath -> IO ()
93+
writeUPLCToFile subject filepath = do
94+
uplcText <- generateUPLC subject
8195
Text.writeFile filepath uplcText
82-
putStrLn P.$ "Verifier UPLC written to: " ++ filepath
96+
putStrLn $ "UPLC program ("
97+
++ Text.unpack subject
98+
++ ") written to: "
99+
++ filepath
83100

84101
main :: IO ()
85102
main = do
86103
args <- getArgs
87104
case args of
88-
[filepath] -> writeVerifierToFile filepath
89-
[] -> do
90-
uplcText <- generateVerifierUPLC
105+
[subject, filepath] -> writeUPLCToFile (Text.pack subject) filepath
106+
[subject] -> do
107+
uplcText <- generateUPLC (Text.pack subject)
91108
Text.putStrLn uplcText
92109
_ -> do
93-
putStrLn "Usage: generate-verifiers [output-file]"
110+
putStrLn "Usage: generate-verifiers <subject> [output-file]"
111+
putStrLn " subject: equals42, dummy-validator, test-validator"
94112
putStrLn " Without output-file: prints UPLC to stdout"
95-
putStrLn " With output-file: writes UPLC to file"
113+
putStrLn " Subject parameter is required"
96114
exitFailure

0 commit comments

Comments
 (0)