Skip to content

Commit 86fd753

Browse files
committed
refactor(testing-interface): move test scripts from coin-selection to testing-interface
- Move PingPong, Bounty, Sample validators and specs to testing-interface - Keep MatchingIndex in coin-selection (tests coin-selection specific functionality) - Add test suite configuration to testing-interface cabal file - Clean up unused imports and fix compilation warnings - Remove convex-testing-interface dependency from coin-selection tests
1 parent d80f84e commit 86fd753

File tree

16 files changed

+1282
-1083
lines changed

16 files changed

+1282
-1083
lines changed

.gitignore

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,3 @@ download-tx-test.sh
3535
tx.json
3636
graph.dot
3737
.vim
38-
CLAUDE.md
39-
AGENTS.md
40-
.opencode/
41-
todo.md

src/coin-selection/convex-coin-selection.cabal

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,7 @@ test-suite convex-coin-selection-test
8484
main-is: Spec.hs
8585
other-modules:
8686
Scripts
87-
Scripts.Bounty
88-
Scripts.Bounty.Vulnerable.DoubleSatisfaction
8987
Scripts.MatchingIndex
90-
Scripts.PingPong
91-
Scripts.PingPong.Vulnerable.UnprotectedScriptOutput
92-
Scripts.Sample
9388

9489
build-depends:
9590
, base >=4.14.0
@@ -103,16 +98,13 @@ test-suite convex-coin-selection-test
10398
, convex-coin-selection
10499
, convex-mockchain
105100
, convex-optics
106-
, convex-testing-interface
107101
, convex-wallet
108102
, lens
109103
, mtl
110104
, plutus-ledger-api
111105
, plutus-ledger-api:plutus-ledger-api-testlib
112106
, plutus-tx
113107
, plutus-tx-plugin
114-
, pretty-show
115-
, prettyprinter
116108
, QuickCheck
117109
, tasty
118110
, tasty-hunit

src/coin-selection/test/Scripts.hs

Lines changed: 4 additions & 229 deletions
Original file line numberDiff line numberDiff line change
@@ -5,74 +5,34 @@
55
-- 1.1.0.0 will be enabled in conway
66
{-# OPTIONS_GHC -fobject-code -fno-ignore-interface-pragmas -fno-omit-interface-pragmas -fplugin-opt PlutusTx.Plugin:target-version=1.1.0.0 #-}
77
{-# OPTIONS_GHC -fplugin-opt PlutusTx.Plugin:defer-errors #-}
8-
{-# OPTIONS_GHC -g -fplugin-opt PlutusTx.Plugin:coverage-all #-}
98

10-
-- | Scripts used for testing
9+
-- | Scripts used for testing coin-selection
1110
module Scripts (
12-
v2SpendingScriptSerialised,
1311
v2SpendingScript,
1412
v2StakingScript,
1513
matchingIndexValidatorScript,
1614
matchingIndexMPScript,
1715
spendMatchingIndex,
1816
mintMatchingIndex,
19-
20-
-- * Sample
21-
sampleValidatorScript,
22-
spendSample,
23-
Sample.SampleRedeemer (..),
24-
25-
-- * PingPong (Secure version)
26-
pingPongValidatorScript,
27-
playPingPongRound,
28-
pingPongCovIdx,
29-
PingPong.PingPongRedeemer (..),
30-
PingPong.PingPongState (..),
31-
32-
-- * PingPong Vulnerable (for threat model demonstration)
33-
pingPongVulnerableScript,
34-
playPingPongVulnerableRound,
35-
36-
-- * Bounty (Secure version - resists double satisfaction)
37-
bountyValidatorScript,
38-
claimBounty,
39-
Bounty.BountyDatum (..),
40-
Bounty.BountyRedeemer (..),
41-
42-
-- * Bounty Vulnerable (for double satisfaction threat model demonstration)
43-
bountyVulnerableScript,
44-
claimBountyVulnerable,
4517
) where
4618

47-
import Cardano.Api (NetworkId)
4819
import Cardano.Api qualified as C
4920
import Convex.BuildTx (MonadBuildTx)
5021
import Convex.BuildTx qualified as BuildTx
5122
import Convex.PlutusTx (compiledCodeToScript)
5223
import Convex.Scripts (toHashableScriptData)
53-
import Convex.Utils (inAlonzo, inBabbage)
54-
import PlutusLedgerApi.Common (SerialisedScript)
24+
import Convex.Utils (inAlonzo)
5525
import PlutusLedgerApi.Test.Examples (alwaysSucceedingNAryFunction)
56-
import PlutusLedgerApi.V3 qualified as PV3
5726
import PlutusTx (BuiltinData, CompiledCode)
5827
import PlutusTx qualified
59-
import PlutusTx.Builtins qualified as PlutusTx
60-
import PlutusTx.Code (getCovIdx)
61-
import PlutusTx.Coverage (CoverageIndex)
6228
import PlutusTx.Prelude (BuiltinUnit)
63-
import Scripts.Bounty qualified as Bounty
64-
import Scripts.Bounty.Vulnerable.DoubleSatisfaction qualified as BountyVulnerable
6529
import Scripts.MatchingIndex qualified as MatchingIndex
66-
import Scripts.PingPong qualified as PingPong
67-
import Scripts.PingPong.Vulnerable.UnprotectedScriptOutput qualified as PingPongVulnerable
68-
import Scripts.Sample qualified as Sample
6930

31+
-- | V2 spending script (always succeeds, 3 args: datum, redeemer, context)
7032
v2SpendingScript :: C.PlutusScript C.PlutusScriptV2
7133
v2SpendingScript = C.PlutusScriptSerialised $ alwaysSucceedingNAryFunction 3
7234

73-
v2SpendingScriptSerialised :: SerialisedScript
74-
v2SpendingScriptSerialised = alwaysSucceedingNAryFunction 3
75-
35+
-- | V2 staking script (always succeeds, 2 args: redeemer, context)
7636
v2StakingScript :: C.PlutusScript C.PlutusScriptV2
7737
v2StakingScript = C.PlutusScriptSerialised $ alwaysSucceedingNAryFunction 2
7838

@@ -88,28 +48,6 @@ matchingIndexValidatorScript = compiledCodeToScript matchingIndexValidatorCompil
8848
matchingIndexMPScript :: C.PlutusScript C.PlutusScriptV3
8949
matchingIndexMPScript = compiledCodeToScript matchingIndexMPCompiled
9050

91-
sampleValidatorCompiled :: CompiledCode (BuiltinData -> BuiltinUnit)
92-
sampleValidatorCompiled = $$(PlutusTx.compile [||Sample.validator||])
93-
94-
sampleValidatorScript :: C.PlutusScript C.PlutusScriptV3
95-
sampleValidatorScript = compiledCodeToScript sampleValidatorCompiled
96-
97-
pingPongValidatorCompiled :: CompiledCode (BuiltinData -> BuiltinUnit)
98-
pingPongValidatorCompiled = $$(PlutusTx.compile [||PingPong.validator||])
99-
100-
pingPongValidatorScript :: C.PlutusScript C.PlutusScriptV3
101-
pingPongValidatorScript = compiledCodeToScript pingPongValidatorCompiled
102-
103-
pingPongCovIdx :: CoverageIndex
104-
pingPongCovIdx = getCovIdx $$(PlutusTx.compile [||PingPong.validator||])
105-
106-
-- | Vulnerable PingPong validator (for threat model demonstration)
107-
pingPongVulnerableCompiled :: CompiledCode (BuiltinData -> BuiltinUnit)
108-
pingPongVulnerableCompiled = $$(PlutusTx.compile [||PingPongVulnerable.validator||])
109-
110-
pingPongVulnerableScript :: C.PlutusScript C.PlutusScriptV3
111-
pingPongVulnerableScript = compiledCodeToScript pingPongVulnerableCompiled
112-
11351
{- | Script that passes if the input's index (in the list of transaction inputs)
11452
matches the number passed as the redeemer
11553
-}
@@ -144,166 +82,3 @@ mintMatchingIndex policy assetName quantity =
14482
C.NoScriptDatumForMint
14583
(fromIntegral @Int @Integer $ BuildTx.findIndexMinted policy txBody)
14684
in BuildTx.setScriptsValid >> BuildTx.addMintWithTxBody policy assetName quantity witness
147-
148-
spendSample
149-
:: forall era m
150-
. (C.IsAlonzoBasedEra era, C.HasScriptLanguageInEra C.PlutusScriptV3 era)
151-
=> (MonadBuildTx era m)
152-
=> Sample.SampleRedeemer
153-
-> C.TxIn
154-
-> m ()
155-
spendSample redeemer txi =
156-
let witness _ =
157-
C.ScriptWitness C.ScriptWitnessForSpending $
158-
BuildTx.buildScriptWitness
159-
sampleValidatorScript
160-
(C.ScriptDatumForTxIn $ Just $ toHashableScriptData ())
161-
-- (fromIntegral @Int @Integer $ 9898) -- BuildTx.findIndexSpending txi txBody)
162-
redeemer
163-
in BuildTx.setScriptsValid >> BuildTx.addInputWithTxBody txi witness
164-
165-
plutusScript :: (C.IsPlutusScriptLanguage lang) => C.PlutusScript lang -> C.Script lang
166-
plutusScript = C.PlutusScript C.plutusScriptVersion
167-
168-
-- | Convert a cardano-api TxIn to a Plutus TxOutRef
169-
txInToTxOutRef :: C.TxIn -> PV3.TxOutRef
170-
txInToTxOutRef (C.TxIn txId (C.TxIx ix)) =
171-
PV3.TxOutRef
172-
{ PV3.txOutRefId = PV3.TxId $ PlutusTx.toBuiltin $ C.serialiseToRawBytes txId
173-
, PV3.txOutRefIdx = fromIntegral ix
174-
}
175-
176-
playPingPongRound
177-
:: forall era m
178-
. ( C.IsBabbageBasedEra era
179-
, C.HasScriptLanguageInEra C.PlutusScriptV3 era
180-
)
181-
=> (MonadBuildTx era m)
182-
=> NetworkId
183-
-> C.Lovelace
184-
-> PingPong.PingPongRedeemer
185-
-> C.TxIn
186-
-> m ()
187-
playPingPongRound networkId value redeemer txi = do
188-
let witness _ =
189-
C.ScriptWitness C.ScriptWitnessForSpending $
190-
BuildTx.buildScriptWitness
191-
pingPongValidatorScript
192-
(C.ScriptDatumForTxIn $ Nothing)
193-
redeemer
194-
BuildTx.setScriptsValid >> BuildTx.addInputWithTxBody txi witness
195-
BuildTx.payToScriptInlineDatum
196-
networkId
197-
(C.hashScript (plutusScript pingPongValidatorScript))
198-
( case redeemer of
199-
PingPong.Ping -> PingPong.Pinged
200-
PingPong.Pong -> PingPong.Ponged
201-
PingPong.Stop -> PingPong.Stopped
202-
)
203-
C.NoStakeAddress
204-
(C.lovelaceToValue value)
205-
206-
-- | Play a round using the VULNERABLE PingPong validator (for threat model demo)
207-
playPingPongVulnerableRound
208-
:: forall era m
209-
. ( C.IsBabbageBasedEra era
210-
, C.HasScriptLanguageInEra C.PlutusScriptV3 era
211-
)
212-
=> (MonadBuildTx era m)
213-
=> NetworkId
214-
-> C.Lovelace
215-
-> PingPongVulnerable.PingPongRedeemer
216-
-> C.TxIn
217-
-> m ()
218-
playPingPongVulnerableRound networkId value redeemer txi = do
219-
let witness _ =
220-
C.ScriptWitness C.ScriptWitnessForSpending $
221-
BuildTx.buildScriptWitness
222-
pingPongVulnerableScript
223-
(C.ScriptDatumForTxIn $ Nothing)
224-
redeemer
225-
BuildTx.setScriptsValid >> BuildTx.addInputWithTxBody txi witness
226-
BuildTx.payToScriptInlineDatum
227-
networkId
228-
(C.hashScript (plutusScript pingPongVulnerableScript))
229-
( case redeemer of
230-
PingPongVulnerable.Ping -> PingPongVulnerable.Pinged
231-
PingPongVulnerable.Pong -> PingPongVulnerable.Ponged
232-
PingPongVulnerable.Stop -> PingPongVulnerable.Stopped
233-
)
234-
C.NoStakeAddress
235-
(C.lovelaceToValue value)
236-
237-
-- Bounty validators
238-
239-
bountyValidatorCompiled :: CompiledCode (BuiltinData -> BuiltinUnit)
240-
bountyValidatorCompiled = $$(PlutusTx.compile [||Bounty.validator||])
241-
242-
bountyValidatorScript :: C.PlutusScript C.PlutusScriptV3
243-
bountyValidatorScript = compiledCodeToScript bountyValidatorCompiled
244-
245-
-- | Vulnerable Bounty validator (for double satisfaction threat model demo)
246-
bountyVulnerableCompiled :: CompiledCode (BuiltinData -> BuiltinUnit)
247-
bountyVulnerableCompiled = $$(PlutusTx.compile [||BountyVulnerable.validator||])
248-
249-
bountyVulnerableScript :: C.PlutusScript C.PlutusScriptV3
250-
bountyVulnerableScript = compiledCodeToScript bountyVulnerableCompiled
251-
252-
{- | Claim a bounty using the SECURE validator.
253-
The output to the beneficiary must include the spent TxOutRef as inline datum.
254-
-}
255-
claimBounty
256-
:: forall era m
257-
. ( C.IsBabbageBasedEra era
258-
, C.HasScriptLanguageInEra C.PlutusScriptV3 era
259-
)
260-
=> (MonadBuildTx era m)
261-
=> C.TxIn
262-
-- ^ The bounty UTxO to spend
263-
-> C.AddressInEra era
264-
-- ^ The beneficiary address (must match datum)
265-
-> C.Lovelace
266-
-- ^ Amount to pay to beneficiary
267-
-> m ()
268-
claimBounty txi beneficiaryAddr value = inBabbage @era $ do
269-
let witness _ =
270-
C.ScriptWitness C.ScriptWitnessForSpending $
271-
BuildTx.buildScriptWitness
272-
bountyValidatorScript
273-
(C.ScriptDatumForTxIn Nothing)
274-
Bounty.Claim
275-
BuildTx.setScriptsValid >> BuildTx.addInputWithTxBody txi witness
276-
-- SECURE: Output includes the TxOutRef as inline datum
277-
-- This prevents double satisfaction as each spend needs its own tagged output
278-
let txOutRef = txInToTxOutRef txi
279-
dat = C.TxOutDatumInline C.babbageBasedEra (toHashableScriptData txOutRef)
280-
val = BuildTx.mkTxOutValue (C.lovelaceToValue value)
281-
txo = C.TxOut beneficiaryAddr val dat C.ReferenceScriptNone
282-
BuildTx.prependTxOut txo
283-
284-
{- | Claim a bounty using the VULNERABLE validator.
285-
The output to the beneficiary does NOT include any identifying datum.
286-
-}
287-
claimBountyVulnerable
288-
:: forall era m
289-
. ( C.IsBabbageBasedEra era
290-
, C.HasScriptLanguageInEra C.PlutusScriptV3 era
291-
)
292-
=> (MonadBuildTx era m)
293-
=> C.TxIn
294-
-- ^ The bounty UTxO to spend
295-
-> C.AddressInEra era
296-
-- ^ The beneficiary address (must match datum)
297-
-> C.Lovelace
298-
-- ^ Amount to pay to beneficiary
299-
-> m ()
300-
claimBountyVulnerable txi beneficiaryAddr value = do
301-
let witness _ =
302-
C.ScriptWitness C.ScriptWitnessForSpending $
303-
BuildTx.buildScriptWitness
304-
bountyVulnerableScript
305-
(C.ScriptDatumForTxIn Nothing)
306-
BountyVulnerable.Claim
307-
BuildTx.setScriptsValid >> BuildTx.addInputWithTxBody txi witness
308-
-- VULNERABLE: No datum on the output - any output to beneficiary satisfies multiple spends
309-
BuildTx.payToAddress beneficiaryAddr (C.lovelaceToValue value)

0 commit comments

Comments
 (0)