Skip to content

Commit 8a26ca1

Browse files
authored
Merge pull request #807 from zkFold/allow-null-txs
feat: allow for null transactions
2 parents bee1ca1 + d1152f0 commit 8a26ca1

File tree

12 files changed

+129
-18
lines changed

12 files changed

+129
-18
lines changed

symbolic-ledger/bench/CircuitSize.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,5 @@ main =
4949
[ goldenCircuit "Ledger.1.1.2.1.1.1.1" (ledgerCircuit @1 @1 @2 @1 @1 @1 @1 @RollupBFInterpreter)
5050
, goldenCircuit "Ledger.1.1.2.1.1.1.2" (ledgerCircuit @1 @1 @2 @1 @1 @1 @2 @RollupBFInterpreter)
5151
, goldenCircuit "Ledger.1.1.4.1.1.1.2" (ledgerCircuit @1 @1 @4 @1 @1 @1 @2 @RollupBFInterpreter)
52+
, goldenCircuit "Ledger.1.1.2.2.2.2.2" (ledgerCircuit @1 @1 @2 @2 @2 @2 @2 @RollupBFInterpreter)
5253
]
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
{-# OPTIONS_GHC -Wno-missing-signatures #-}
2+
3+
-- | Example demonstrating that null transactions can be used as batch padding.
4+
-- A "null transaction" has all null inputs and all null outputs. It is a pure
5+
-- no-op on the UTxO state and passes validation.
6+
module ZkFold.Symbolic.Ledger.Examples.Four (
7+
prevState,
8+
batch,
9+
witness,
10+
newState,
11+
utxoPreimage2,
12+
tx1,
13+
nullTx,
14+
sigs,
15+
bridgedIn,
16+
I,
17+
Bi,
18+
Bo,
19+
Ud,
20+
A,
21+
Ixs,
22+
Oxs,
23+
TxCount,
24+
) where
25+
26+
import Control.Applicative (pure)
27+
import GHC.Generics ((:*:) (..), (:.:) (..))
28+
import GHC.IsList (IsList (..))
29+
import ZkFold.Symbolic.Data.Bool (false)
30+
31+
import ZkFold.Symbolic.Ledger.Examples.One (
32+
A,
33+
Bi,
34+
Bo,
35+
I,
36+
Ixs,
37+
Oxs,
38+
Ud,
39+
bridgedIn,
40+
prevState,
41+
privateKey,
42+
publicKey,
43+
tx,
44+
)
45+
import ZkFold.Symbolic.Ledger.Offchain.State.Update (updateLedgerState)
46+
import ZkFold.Symbolic.Ledger.Types
47+
import ZkFold.Symbolic.Ledger.Utils (unsafeToVector')
48+
49+
-- | Batch of 2: one real transaction followed by one null transaction used as padding.
50+
type TxCount = 2
51+
52+
-- | The real transaction (same as 'tx' from Examples.One).
53+
tx1 :: Transaction Ixs Oxs A I
54+
tx1 = tx
55+
56+
-- | A null transaction: all null inputs and all null outputs.
57+
-- Intended as a padding transaction to fill a fixed-size batch.
58+
nullTx :: Transaction Ixs Oxs A I
59+
nullTx =
60+
Transaction
61+
{ inputs = Comp1 (fromList [nullOutputRef])
62+
, outputs = Comp1 (fromList [nullOutput @A @I :*: false])
63+
}
64+
65+
batch :: TransactionBatch Ixs Oxs A TxCount I
66+
batch = TransactionBatch {tbTransactions = unsafeToVector' [tx1, nullTx]}
67+
68+
sigs =
69+
let rPoint :*: s = signTransaction tx1 privateKey
70+
-- Any signature works for the null transaction since its null inputs
71+
-- skip signature verification (isNullUTxO = true).
72+
dummyRPoint :*: dummyS = rPoint :*: s
73+
in Comp1
74+
( unsafeToVector'
75+
[ Comp1 (fromList [rPoint :*: s :*: publicKey])
76+
, Comp1 (fromList [dummyRPoint :*: dummyS :*: publicKey])
77+
]
78+
)
79+
80+
newState :*: witness :*: utxoPreimage2 =
81+
updateLedgerState prevState (pure (nullUTxO @A @I)) bridgedIn batch sigs

symbolic-ledger/src/ZkFold/Symbolic/Ledger/Examples/One.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ module ZkFold.Symbolic.Ledger.Examples.One (
1717
tx2,
1818
sigs2,
1919
bridgedIn2,
20+
privateKey,
21+
publicKey,
2022
I,
2123
Bi,
2224
Bo,

symbolic-ledger/src/ZkFold/Symbolic/Ledger/Validation/State.hs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ For validating transactions, we should check:
4444
\* Bridged out outputs are checked to be same as in bridge out list.
4545
\* Outputs (bridged out or not) must have at least one ada and all assets are non-negative.
4646
\* Transaction is balanced.
47-
\* Transaction must have at least one input.
47+
\* Transaction must have at least one input OR the entire transaction is null (no inputs & no outputs)
4848
\* Bridged out output is not a null output.
4949
5050
For validating batch, we simply apply transaction validation check iteratively.

symbolic-ledger/src/ZkFold/Symbolic/Ledger/Validation/Transaction.hs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import ZkFold.Algebra.Class (
2626
import ZkFold.Control.Conditional (ifThenElse)
2727
import ZkFold.Data.Eq
2828
import ZkFold.Data.HFunctor.Classes (HShow)
29-
import ZkFold.Data.Ord ((>=))
29+
import ZkFold.Data.Ord ((>), (>=))
3030
import ZkFold.Data.Vector (Vector, Zip (..))
3131
import ZkFold.Data.Vector qualified as Vector
3232
import ZkFold.Prelude (foldl')
@@ -251,9 +251,9 @@ validateTransaction utxoTree bridgedOutOutputs tx txw =
251251
true
252252
outputsAssets
253253
inputsWithWitness = zipWith (:*:) (unComp1 tx.inputs) (unComp1 txw.twInputs)
254-
(isInsValid :*: consumedAtleastOneInput :*: updatedUTxOTreeForInputs) =
254+
(isInsValid :*: updatedUTxOTreeForInputs) =
255255
foldl'
256-
( \(isInsValidAcc :*: consumedAtleastOneAcc :*: acc) (inputRef :*: (merkleEntry :*: utxo :*: rPoint :*: s :*: publicKey)) ->
256+
( \(isInsValidAcc :*: acc) (inputRef :*: (merkleEntry :*: utxo :*: rPoint :*: s :*: publicKey)) ->
257257
let
258258
nullUTxOHash' = nullUTxOHash @a @context
259259
utxoHash :: HashSimple context = hash utxo & Base.hHash
@@ -280,12 +280,9 @@ validateTransaction utxoTree bridgedOutOutputs tx txw =
280280
(rPoint :*: s)
281281
)
282282
in
283-
( isValid'
284-
:*: (consumedAtleastOneAcc || not isNullUTxO)
285-
:*: updatedTree
286-
)
283+
(isValid' :*: updatedTree)
287284
)
288-
((true :: Bool context) :*: (false :: Bool context) :*: utxoTree)
285+
((true :: Bool context) :*: utxoTree)
289286
inputsWithWitness
290287
outputsWithWitness = zipWith (:*:) (unComp1 tx.outputs) (unComp1 txw.twOutputs)
291288
(bouts :*: _ :*: outsValid :*: updatedUTxOTreeForOutputs) =
@@ -332,13 +329,13 @@ validateTransaction utxoTree bridgedOutOutputs tx txw =
332329
outputsWithWitness
333330
in
334331
( bouts
335-
:*: (outsValid && isInsValid && consumedAtleastOneInput && isBalanced)
332+
:*: (outsValid && isInsValid && isBalanced) -- Note that we don't need to check if transaction consumes at least one input or is null entirely as our transaction currently only has two fields, namely, inputs & outputs and if thus inputs are null, outputs are null too.
336333
:*: updatedUTxOTreeForOutputs
337334
)
338335

339336
-- | Check if output has sane value.
340337
--
341-
-- We check if the output has at least one ada and all assets are non-negative.
338+
-- We check if the output has at least one ada and all non-null assets have strictly positive quantity.
342339
outputHasValueSanity
343340
:: forall a context
344341
. (KnownRegistersAssetQuantity context, Symbolic context)
@@ -357,7 +354,7 @@ outputHasValueSanity output =
357354
>= fromConstant @Haskell.Integer 1_000_000
358355
)
359356
)
360-
:*: (allNonNegAcc && (asset.assetQuantity >= zero))
357+
:*: (allNonNegAcc && (asset.assetQuantity > zero || (asset.assetPolicy == zero && asset.assetName == zero)))
361358
)
362359
(false :*: true)
363360
(unComp1 (oAssets output))
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Ledger.1.1.2.1.1.1.1
2-
Number of polynomial constraints: 48735
3-
Number of variables: 50041
2+
Number of polynomial constraints: 48740
3+
Number of variables: 50050
44
Number of lookup constraints: 1271
55
Number of lookup tables: 9
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Ledger.1.1.2.1.1.1.2
2-
Number of polynomial constraints: 81497
3-
Number of variables: 84064
2+
Number of polynomial constraints: 81504
3+
Number of variables: 84077
44
Number of lookup constraints: 2500
55
Number of lookup tables: 9
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Ledger.1.1.2.2.2.2.2
2+
Number of polynomial constraints: 190858
3+
Number of variables: 196576
4+
Number of lookup constraints: 5736
5+
Number of lookup tables: 9
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Ledger.1.1.4.1.1.1.2
2-
Number of polynomial constraints: 125747
3-
Number of variables: 128392
2+
Number of polynomial constraints: 125754
3+
Number of variables: 128405
44
Number of lookup constraints: 2500
55
Number of lookup tables: 9

symbolic-ledger/symbolic-ledger.cabal

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ library
9696
import: options
9797
exposed-modules:
9898
ZkFold.Symbolic.Ledger.Circuit.Compile
99+
ZkFold.Symbolic.Ledger.Examples.Four
99100
ZkFold.Symbolic.Ledger.Examples.One
100101
ZkFold.Symbolic.Ledger.Examples.Three
101102
ZkFold.Symbolic.Ledger.Examples.Two
@@ -140,6 +141,7 @@ test-suite symbolic-ledger-test
140141
Tests.Symbolic.Ledger.E2E.Compile.One
141142
Tests.Symbolic.Ledger.E2E.Compile.Three
142143
Tests.Symbolic.Ledger.E2E.Compile.Two
144+
Tests.Symbolic.Ledger.E2E.Four
143145
Tests.Symbolic.Ledger.E2E.One
144146
Tests.Symbolic.Ledger.E2E.Three
145147
Tests.Symbolic.Ledger.E2E.Two

0 commit comments

Comments
 (0)