Skip to content

Commit f622a23

Browse files
authored
Merge pull request #132 from zkFold/131-incorporate-bridging
feat(#131): briding support to rollup validator
2 parents 6cf0228 + ba06738 commit f622a23

File tree

7 files changed

+456
-81
lines changed

7 files changed

+456
-81
lines changed

zkfold-cardano-scripts-common/data/compiled-scripts/rollup-simple.blueprint

Lines changed: 134 additions & 21 deletions
Large diffs are not rendered by default.

zkfold-cardano-scripts-common/src/ZkFold/Cardano/UPLC/RollupSimple/Types.hs

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,20 @@
33

44
module ZkFold.Cardano.UPLC.RollupSimple.Types (
55
RollupState (..),
6-
RollupSimpleRed,
6+
RollupSimpleRed (..),
7+
BridgeUtxoStatus (..),
8+
BridgeUtxoInfo (..),
9+
RollupConfiguration (..),
710
) where
811

912
import GHC.Generics (Generic)
13+
import PlutusLedgerApi.V3 (Address, CurrencySymbol, TokenName, TxOutRef)
1014
import PlutusTx.Blueprint
1115
import qualified PlutusTx.Blueprint.TH
1216
import PlutusTx.Prelude hiding (toList, (*), (+))
1317
import Prelude (Show)
1418

15-
import ZkFold.Cardano.OnChain.Plonkup.Data (ProofBytes)
19+
import ZkFold.Cardano.OnChain.Plonkup.Data (ProofBytes, SetupBytes)
1620

1721
data RollupState = RollupState
1822
{ previousStateHash :: Integer
@@ -26,4 +30,55 @@ data RollupState = RollupState
2630

2731
PlutusTx.Blueprint.TH.makeIsDataSchemaIndexed ''RollupState [('RollupState, 0)]
2832

29-
type RollupSimpleRed = ProofBytes
33+
data RollupSimpleRed = RollupSimpleRed
34+
{ rsrProofBytes :: ProofBytes
35+
-- ^ Proof for state update.
36+
, rsrAddress :: Address
37+
-- ^ Address of the spending validator.
38+
}
39+
deriving stock (Show, Generic)
40+
deriving anyclass HasBlueprintDefinition
41+
42+
PlutusTx.Blueprint.TH.makeIsDataSchemaIndexed ''RollupSimpleRed [('RollupSimpleRed, 0)]
43+
44+
data BridgeUtxoStatus
45+
= -- | New UTxO being bridged in, also giving it's layer-2 address.
46+
BridgeIn Integer
47+
| -- | UTxO being bridged out.
48+
BridgeOut
49+
| -- | Already bridged in UTxO is getting updated, usually for satisfying bridge-out requirement.
50+
BridgeBalance
51+
deriving stock (Show, Generic)
52+
deriving anyclass HasBlueprintDefinition
53+
54+
PlutusTx.Blueprint.TH.makeIsDataSchemaIndexed ''BridgeUtxoStatus [('BridgeIn, 0), ('BridgeOut, 1), ('BridgeBalance, 2)]
55+
56+
data BridgeUtxoInfo = BridgeUtxoInfo
57+
{ buiORef :: TxOutRef
58+
-- ^ Reference to the state UTxO being updated.
59+
, buiStatus :: BridgeUtxoStatus
60+
-- ^ Status of the UTxO.
61+
}
62+
deriving stock (Show, Generic)
63+
deriving anyclass HasBlueprintDefinition
64+
65+
PlutusTx.Blueprint.TH.makeIsDataSchemaIndexed ''BridgeUtxoInfo [('BridgeUtxoInfo, 0)]
66+
67+
data RollupConfiguration = RollupConfiguration
68+
{ rcNftCurrencySymbol :: CurrencySymbol
69+
-- ^ NFT Currency Symbol.
70+
, rcNftTokenName :: TokenName
71+
-- ^ NFT Token Name.
72+
, rcSetupBytes :: SetupBytes
73+
-- ^ Setup bytes.
74+
, rcMaxBridgeIn :: Integer
75+
-- ^ Maximum number of UTxOs that can be bridged in.
76+
, rcMaxBridgeOut :: Integer
77+
-- ^ Maximum number of UTxOs that can be bridged out.
78+
, rcMaxOutputAssets :: Integer
79+
-- ^ Maximum number of assets that can be present in the layer-2 output.
80+
}
81+
deriving stock (Show, Generic)
82+
deriving anyclass HasBlueprintDefinition
83+
84+
PlutusTx.Blueprint.TH.makeIsDataSchemaIndexed ''RollupConfiguration [('RollupConfiguration, 0)]

zkfold-cardano-scripts/src/ZkFold/Cardano/UPLC/RollupSimple.hs

Lines changed: 123 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,51 +9,145 @@
99

1010
module ZkFold.Cardano.UPLC.RollupSimple (
1111
rollupSimple,
12+
rollupSimpleStake,
1213
module ZkFold.Cardano.UPLC.RollupSimple.Types,
1314
) where
15+
1416
import Data.Function ((&))
1517
import PlutusLedgerApi.V1 (valueOf)
1618
import PlutusLedgerApi.V3
19+
import qualified PlutusTx.AssocMap as AssocMap
1720
import qualified PlutusTx.Builtins.Internal as BI
18-
import PlutusTx.Prelude hiding (toList, (*), (+))
21+
import PlutusTx.Prelude hiding (toList)
1922

2023
import ZkFold.Cardano.OnChain.BLS12_381 (toF)
2124
import ZkFold.Cardano.OnChain.Plonkup (PlonkupPlutus)
22-
import ZkFold.Cardano.OnChain.Plonkup.Data (SetupBytes)
23-
import ZkFold.Cardano.OnChain.Utils (findOwnInput')
24-
import ZkFold.Cardano.UPLC.RollupSimple.Types (RollupSimpleRed, RollupState (..))
25+
import ZkFold.Cardano.UPLC.RollupSimple.Types (BridgeUtxoInfo (..), BridgeUtxoStatus (..),
26+
RollupConfiguration (..), RollupSimpleRed (..),
27+
RollupState (..))
28+
import ZkFold.Cardano.UPLC.RollupSimple.Utils
2529
import ZkFold.Protocol.NonInteractiveProof (NonInteractiveProof (..))
2630

2731
{-# INLINEABLE rollupSimple #-}
2832
rollupSimple ::
29-
-- | Setup bytes.
33+
-- | Script hash of the stake validator.
3034
BuiltinData ->
31-
-- | NFT Currency Symbol.
35+
-- | Script context.
3236
BuiltinData ->
33-
-- | NFT Token Name.
37+
BuiltinUnit
38+
rollupSimple (unsafeFromBuiltinData -> sh :: ScriptHash) scData =
39+
check
40+
$ AssocMap.member (toBuiltinData $ ScriptCredential sh) txInfoWrdl
41+
&& trySpend
42+
== 1 -- Disallowing any other use-case for now to be on safe-side.
43+
where
44+
txInfoL = BI.unsafeDataAsConstr scData & BI.snd
45+
txInfo = txInfoL & BI.head & BI.unsafeDataAsConstr & BI.snd
46+
txInfoWrdl :: Map BuiltinData BuiltinData =
47+
txInfo
48+
& BI.tail
49+
& BI.tail
50+
& BI.tail
51+
& BI.tail
52+
& BI.tail
53+
& BI.tail
54+
& BI.head
55+
& unsafeFromBuiltinData
56+
redL = txInfoL & BI.tail
57+
scriptInfo = redL & BI.tail & BI.head & BI.unsafeDataAsConstr
58+
trySpend = BI.fst scriptInfo
59+
60+
{-# INLINEABLE rollupSimpleStake #-}
61+
rollupSimpleStake ::
62+
-- | Rollup configuration.
3463
BuiltinData ->
3564
-- | Script context.
3665
BuiltinData ->
3766
BuiltinUnit
38-
rollupSimple (unsafeFromBuiltinData -> (setupBytes :: SetupBytes)) (unsafeFromBuiltinData -> (nftCurrencySymbol :: CurrencySymbol)) (unsafeFromBuiltinData -> (nftTokenName :: TokenName)) scData = check $
39-
trySpend == 1
40-
&& valueOf (txOutValue ownInputOutput) nftCurrencySymbol nftTokenName == 1
41-
&& verify @PlonkupPlutus setupBytes (toF <$> [previousStateHash oldState, utxoTreeRoot oldState, chainLength oldState, bridgeInCommitment oldState, bridgeOutCommitment oldState, previousStateHash newState, utxoTreeRoot newState, chainLength newState, bridgeInCommitment newState, bridgeOutCommitment newState, 1]) proofBytes
42-
where
43-
44-
txInfoL = BI.unsafeDataAsConstr scData & BI.snd
45-
txInfo = txInfoL & BI.head & BI.unsafeDataAsConstr & BI.snd
46-
txInfoInputs = BI.head txInfo & unsafeFromBuiltinData @[TxInInfo]
47-
txInfoOutputs = txInfo & BI.tail & BI.tail & BI.head & unsafeFromBuiltinData @[TxOut]
48-
redL = txInfoL & BI.tail
49-
proofBytes = redL & BI.head & unsafeFromBuiltinData
50-
-- Extracting ScriptInfo
51-
scriptInfo = redL & BI.tail & BI.head & BI.unsafeDataAsConstr
52-
trySpend = BI.fst scriptInfo
53-
spendFields = scriptInfo & BI.snd
54-
spendRef = spendFields & BI.head & unsafeFromBuiltinData @TxOutRef
55-
Just (unsafeFromBuiltinData . getDatum -> (oldState :: RollupState)) = spendFields & BI.tail & BI.head & unsafeFromBuiltinData @(Maybe Datum)
56-
Just ownInput = findOwnInput' txInfoInputs spendRef
57-
ownInputOutput = txInInfoResolved ownInput
58-
Just continuingOutput = find (\txOut -> txOutAddress txOut == txOutAddress ownInputOutput && valueOf (txOutValue txOut) nftCurrencySymbol nftTokenName == 1) txInfoOutputs
59-
OutputDatum (unsafeFromBuiltinData . getDatum -> (newState :: RollupState)) = txOutDatum continuingOutput
67+
rollupSimpleStake (unsafeFromBuiltinData -> RollupConfiguration {..}) scData =
68+
check
69+
$ if scriptInfoIx == 2
70+
then
71+
-- Remaining funds are securely returned to the validator.
72+
traceIfFalse
73+
"rollupSimpleStake: availableBridgeVal mismatch"
74+
( availableBridgeVal
75+
== (bridgeOutReqVal <> bridgeLeftoverVal)
76+
)
77+
&& traceIfFalse
78+
"rollupSimpleStake: proof verification failed"
79+
( verify @PlonkupPlutus
80+
rcSetupBytes
81+
( toF
82+
<$> [previousStateHash oldState, utxoTreeRoot oldState, chainLength oldState, bridgeInCommitment oldState, bridgeOutCommitment oldState, previousStateHash newState, utxoTreeRoot newState, chainLength newState, bridgeInCommitment newState, bridgeOutCommitment newState, 1]
83+
<> (bridgeInList <> fillWithZeros3WithAdd (rcMaxBridgeIn - quot (length bridgeInList)) rcMaxOutputAssets 3 [])
84+
<> (bridgeOutList <> fillWithZeros3WithAdd (rcMaxBridgeOut - quot (length bridgeOutList)) rcMaxOutputAssets 3 [])
85+
)
86+
rsrProofBytes
87+
)
88+
else
89+
scriptInfoIx
90+
== 3
91+
&& txCertIx
92+
== 0 -- Allow for registering of stake validator.
93+
where
94+
quot = (`quotient` (1 + 3 * rcMaxOutputAssets))
95+
txInfoL = BI.unsafeDataAsConstr scData & BI.snd
96+
txInfo = txInfoL & BI.head & BI.unsafeDataAsConstr & BI.snd
97+
txInfoInputs = BI.head txInfo & unsafeFromBuiltinData @[TxInInfo]
98+
txInfoOutputs = txInfo & BI.tail & BI.tail & BI.head & unsafeFromBuiltinData @[TxOut]
99+
redL = txInfoL & BI.tail
100+
RollupSimpleRed {..} = redL & BI.head & unsafeFromBuiltinData
101+
scriptInfo = redL & BI.tail & BI.head & BI.unsafeDataAsConstr
102+
scriptInfoIx = BI.fst scriptInfo
103+
txCertIx = BI.snd scriptInfo & BI.tail & BI.head & BI.unsafeDataAsConstr & BI.fst
104+
toSymbolicValue' = toSymbolicValue rcMaxOutputAssets
105+
goInputs remInputs availableBridgeValAcc mownInput =
106+
case remInputs of
107+
[] -> (availableBridgeValAcc, mownInput)
108+
(i' : is) ->
109+
let i = txInInfoResolved i'
110+
in -- Input is relevant.
111+
if txOutAddress i == rsrAddress
112+
then
113+
-- Whether it is state input or an input given to satisfy bridge-out requirement.
114+
if valueOf (txOutValue i) rcNftCurrencySymbol rcNftTokenName == 1
115+
then
116+
goInputs is availableBridgeValAcc (Just i')
117+
else goInputs is (availableBridgeValAcc <> txOutValue i) mownInput
118+
else goInputs is availableBridgeValAcc mownInput
119+
(availableBridgeVal, Just ownInputInfo) = goInputs txInfoInputs mempty Nothing
120+
ownInputOutput = txInInfoResolved ownInputInfo
121+
OutputDatum (unsafeFromBuiltinData . getDatum -> (oldState :: RollupState)) = txOutDatum ownInputOutput
122+
ownInputRef = txInInfoOutRef ownInputInfo
123+
goOutputs remOutputs bridgeOutReqValAcc bridgeLeftoverValAcc bridgeOutListAcc bridgeInListAcc mcontinuingOutput =
124+
case remOutputs of
125+
[] -> (bridgeOutReqValAcc, bridgeLeftoverValAcc, bridgeOutListAcc, bridgeInListAcc, mcontinuingOutput)
126+
(o : os) ->
127+
case txOutDatum o of
128+
NoOutputDatum -> goOutputs os bridgeOutReqValAcc bridgeLeftoverValAcc bridgeOutListAcc bridgeInListAcc mcontinuingOutput
129+
OutputDatumHash _ -> goOutputs os bridgeOutReqValAcc bridgeLeftoverValAcc bridgeOutListAcc bridgeInListAcc mcontinuingOutput
130+
OutputDatum (getDatum -> odatum) ->
131+
if txOutAddress o == rsrAddress
132+
then
133+
if valueOf (txOutValue o) rcNftCurrencySymbol rcNftTokenName == 1
134+
then
135+
goOutputs os bridgeOutReqValAcc bridgeLeftoverValAcc bridgeOutListAcc bridgeInListAcc (Just o)
136+
else
137+
let odatum' :: BridgeUtxoInfo = unsafeFromBuiltinData odatum
138+
in if buiORef odatum' == ownInputRef
139+
then case buiStatus odatum' of
140+
BridgeIn layer2Address ->
141+
goOutputs os bridgeOutReqValAcc bridgeLeftoverValAcc bridgeOutListAcc ((layer2Address : toSymbolicValue' (txOutValue o)) <> bridgeInListAcc) mcontinuingOutput
142+
BridgeBalance ->
143+
goOutputs os bridgeOutReqValAcc (txOutValue o <> bridgeLeftoverValAcc) bridgeOutListAcc bridgeInListAcc mcontinuingOutput
144+
BridgeOut -> traceError "rollupSimpleStake: bridge-out output cannot be to the rollup validator"
145+
else
146+
traceError "rollupSimpleStake: output to rollup validator must be either a bridge-in, bridge-balance or state UTxO"
147+
else
148+
if odatum == toBuiltinData (BridgeUtxoInfo ownInputRef BridgeOut)
149+
then
150+
goOutputs os (bridgeOutReqValAcc <> txOutValue o) bridgeLeftoverValAcc ((byteStringToInteger' (addressToBS (txOutAddress o)) : toSymbolicValue' (txOutValue o)) <> bridgeOutListAcc) bridgeInListAcc mcontinuingOutput
151+
else goOutputs os bridgeOutReqValAcc bridgeLeftoverValAcc bridgeOutListAcc bridgeInListAcc mcontinuingOutput
152+
(bridgeOutReqVal, bridgeLeftoverVal, bridgeOutList, bridgeInList, Just continuingOutput) = goOutputs txInfoOutputs mempty mempty mempty mempty Nothing
153+
OutputDatum (unsafeFromBuiltinData . getDatum -> (newState :: RollupState)) = txOutDatum continuingOutput
Lines changed: 42 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,20 @@
1-
21
module ZkFold.Cardano.UPLC.RollupSimple.Compile (
32
writeRollupSimpleBP,
43
rollupSimpleSerialisedScript,
54
rollupSimpleCompiledCode,
65
) where
76

8-
import Data.ByteString (ByteString)
9-
import Data.ByteString.Short (fromShort)
10-
import Data.Function ((&))
11-
import Data.Maybe (Maybe (..))
12-
import qualified Data.Set as Set
7+
import Data.ByteString (ByteString)
8+
import Data.ByteString.Short (fromShort)
9+
import Data.Function ((&))
10+
import Data.Maybe (Maybe (..))
11+
import qualified Data.Set as Set
1312
import PlutusLedgerApi.V3
1413
import qualified PlutusTx
1514
import PlutusTx.Blueprint
16-
import qualified PlutusTx.Prelude as PlutusTx
17-
import Prelude (FilePath, IO, ($))
15+
import qualified PlutusTx.Prelude as PlutusTx
16+
import Prelude (FilePath, IO, ($))
1817

19-
import ZkFold.Cardano.OnChain.Plonkup.Data (SetupBytes)
2018
import ZkFold.Cardano.UPLC.RollupSimple
2119

2220
rollupSimpleBP :: ContractBlueprint
@@ -37,37 +35,47 @@ rollupSimpleBP =
3735
{ validatorTitle = "rollupSimple"
3836
, validatorRedeemer =
3937
MkArgumentBlueprint
40-
{ argumentTitle = Just "RollupSimpleRed"
41-
, argumentSchema = definitionRef @RollupSimpleRed
38+
{ argumentTitle = Just "BuiltinData"
39+
, argumentSchema = definitionRef @BuiltinData
4240
, argumentPurpose = Set.singleton Spend
43-
, argumentDescription = Nothing
41+
, argumentDescription = Just "No redeemer is required."
4442
}
4543
, validatorParameters =
4644
[ MkParameterBlueprint
47-
{ parameterTitle = Just "SetupBytes"
48-
, parameterSchema = definitionRef @SetupBytes
45+
{ parameterTitle = Just "ScriptHash"
46+
, parameterSchema = definitionRef @ScriptHash
4947
, parameterPurpose = Set.singleton Spend
5048
, parameterDescription = Nothing
5149
}
52-
, MkParameterBlueprint
53-
{ parameterTitle = Just "CurrencySymbol"
54-
, parameterSchema = definitionRef @CurrencySymbol
55-
, parameterPurpose = Set.singleton Spend
56-
, parameterDescription = Nothing
57-
}
58-
, MkParameterBlueprint
59-
{ parameterTitle = Just "TokenName"
60-
, parameterSchema = definitionRef @TokenName
61-
, parameterPurpose = Set.singleton Spend
50+
]
51+
, validatorDescription = Just "Rollup Simple spend validator"
52+
, validatorDatum = Nothing -- Omitting information about datum as it is not straightforward.
53+
, validatorCompiled = Just $ compiledValidator commonPlutusVersion rollupSimpleSerialisedScript
54+
}
55+
, MkValidatorBlueprint
56+
{ validatorTitle = "rollupSimpleStake"
57+
, validatorRedeemer =
58+
MkArgumentBlueprint
59+
{ argumentTitle = Just "RollupSimpleRed"
60+
, argumentSchema = definitionRef @RollupSimpleRed
61+
, argumentPurpose = Set.singleton Withdraw
62+
, argumentDescription = Nothing
63+
}
64+
, validatorParameters =
65+
[ MkParameterBlueprint
66+
{ parameterTitle = Just "RollupConfiguration"
67+
, parameterSchema = definitionRef @RollupConfiguration
68+
, parameterPurpose = Set.singleton Withdraw
6269
, parameterDescription = Nothing
6370
}
6471
]
65-
, validatorDescription = Just "Rollup Simple validator"
72+
, validatorDescription = Just "Rollup Simple stake validator"
6673
, validatorDatum = Nothing
67-
, validatorCompiled = Just $ compiledValidator commonPlutusVersion rollupSimpleSerialisedScript
74+
, validatorCompiled = Just $ compiledValidator commonPlutusVersion rollupSimpleStakeSerialisedScript
6875
}
6976
]
70-
, contractDefinitions = deriveDefinitions @'[SetupBytes, CurrencySymbol, TokenName, RollupState, RollupSimpleRed] }
77+
, contractDefinitions = deriveDefinitions @'[BuiltinData, ScriptHash, RollupSimpleRed, RollupConfiguration]
78+
}
7179
where
7280
commonPlutusVersion = PlutusV3
7381

@@ -77,5 +85,11 @@ writeRollupSimpleBP fp = writeBlueprint fp rollupSimpleBP
7785
rollupSimpleSerialisedScript :: ByteString
7886
rollupSimpleSerialisedScript = serialiseCompiledCode rollupSimpleCompiledCode & fromShort
7987

80-
rollupSimpleCompiledCode :: PlutusTx.CompiledCode (PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinUnit)
88+
rollupSimpleCompiledCode :: PlutusTx.CompiledCode (PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinUnit)
8189
rollupSimpleCompiledCode = $$(PlutusTx.compile [||rollupSimple||])
90+
91+
rollupSimpleStakeSerialisedScript :: ByteString
92+
rollupSimpleStakeSerialisedScript = serialiseCompiledCode rollupSimpleStakeCompiledCode & fromShort
93+
94+
rollupSimpleStakeCompiledCode :: PlutusTx.CompiledCode (PlutusTx.BuiltinData -> PlutusTx.BuiltinData -> PlutusTx.BuiltinUnit)
95+
rollupSimpleStakeCompiledCode = $$(PlutusTx.compile [||rollupSimpleStake||])

0 commit comments

Comments
 (0)