|
| 1 | +{-# LANGUAGE DuplicateRecordFields #-} |
| 2 | + |
| 3 | +-- | Benchmark suite for the BLS accumulator implementation. |
| 4 | +-- |
| 5 | +-- This suite measures the performance of accumulator operations with realistic |
| 6 | +-- UTxO sets to understand the performance implications of using accumulators |
| 7 | +-- for snapshot signing and partial fanout. |
| 8 | +module Main where |
| 9 | + |
| 10 | +import Hydra.Prelude |
| 11 | + |
| 12 | +import Cardano.Api.UTxO qualified as UTxO |
| 13 | +import Codec.Serialise (serialise) |
| 14 | +import Criterion.Main (bench, bgroup, defaultMain, nf, nfIO, whnf, whnfIO) |
| 15 | +import Hydra.Cardano.Api (Tx, UTxO) |
| 16 | +import Hydra.Tx.Accumulator ( |
| 17 | + buildFromUTxO, |
| 18 | + createMembershipProof, |
| 19 | + createMembershipProofFromUTxO, |
| 20 | + defaultCRS, |
| 21 | + generateCRS, |
| 22 | + getAccumulatorHash, |
| 23 | + unHydraAccumulator, |
| 24 | + ) |
| 25 | +import Hydra.Tx.IsTx (IsTx (..)) |
| 26 | +import Test.Hydra.Tx.Gen (genUTxOAdaOnlyOfSize) |
| 27 | +import Test.QuickCheck (generate) |
| 28 | + |
| 29 | +main :: IO () |
| 30 | +main = do |
| 31 | + putTextLn "=== Accumulator Benchmark Suite ===" |
| 32 | + putTextLn "Generating test data..." |
| 33 | + |
| 34 | + -- Generate UTxO sets of various sizes |
| 35 | + utxo10 <- generateUTxO 10 |
| 36 | + utxo50 <- generateUTxO 50 |
| 37 | + utxo100 <- generateUTxO 100 |
| 38 | + utxo500 <- generateUTxO 500 |
| 39 | + utxo1000 <- generateUTxO 1000 |
| 40 | + utxo5000 <- generateUTxO 5000 |
| 41 | + utxo10000 <- generateUTxO 10000 |
| 42 | + |
| 43 | + putTextLn $ "Generated UTxO sets: 10, 50, 100, 500, 1000, 5000, 10000" |
| 44 | + |
| 45 | + -- Pre-build accumulators for membership proof tests |
| 46 | + let acc10 = buildFromUTxO @Tx utxo10 |
| 47 | + acc50 = buildFromUTxO @Tx utxo50 |
| 48 | + acc100 = buildFromUTxO @Tx utxo100 |
| 49 | + acc500 = buildFromUTxO @Tx utxo500 |
| 50 | + acc1000 = buildFromUTxO @Tx utxo1000 |
| 51 | + acc5000 = buildFromUTxO @Tx utxo5000 |
| 52 | + acc10000 = buildFromUTxO @Tx utxo10000 |
| 53 | + |
| 54 | + putTextLn "Pre-built accumulators" |
| 55 | + |
| 56 | + -- Generate subsets for membership proofs |
| 57 | + -- Testing realistic scenarios: proving 10-20% of UTxOs |
| 58 | + subset5_from50 <- generateSubset utxo50 5 |
| 59 | + subset10_from100 <- generateSubset utxo100 10 |
| 60 | + subset50_from500 <- generateSubset utxo500 50 |
| 61 | + subset100_from1000 <- generateSubset utxo1000 100 |
| 62 | + subset500_from5000 <- generateSubset utxo5000 500 |
| 63 | + subset1000_from10000 <- generateSubset utxo10000 1000 |
| 64 | + |
| 65 | + putTextLn "Generated subsets for membership proofs" |
| 66 | + |
| 67 | + -- Extract individual elements for low-level proof testing |
| 68 | + let elements10 = toPairList @Tx utxo10 |
| 69 | + elements100 = toPairList @Tx utxo100 |
| 70 | + serialized10 = utxoToElement @Tx <$> elements10 |
| 71 | + serialized100 = utxoToElement @Tx <$> elements100 |
| 72 | + |
| 73 | + putTextLn "Starting benchmarks..." |
| 74 | + putTextLn "" |
| 75 | + |
| 76 | + defaultMain |
| 77 | + [ bgroup |
| 78 | + "1. Build Accumulator from UTxO" |
| 79 | + [ bench "10 UTxOs" $ whnf (buildFromUTxO @Tx) utxo10 |
| 80 | + , bench "50 UTxOs" $ whnf (buildFromUTxO @Tx) utxo50 |
| 81 | + , bench "100 UTxOs" $ whnf (buildFromUTxO @Tx) utxo100 |
| 82 | + , bench "500 UTxOs" $ whnf (buildFromUTxO @Tx) utxo500 |
| 83 | + , bench "1000 UTxOs" $ whnf (buildFromUTxO @Tx) utxo1000 |
| 84 | + , bench "5000 UTxOs" $ whnf (buildFromUTxO @Tx) utxo5000 |
| 85 | + , bench "10000 UTxOs" $ whnf (buildFromUTxO @Tx) utxo10000 |
| 86 | + ] |
| 87 | + , bgroup |
| 88 | + "2. UTxO to Elements Conversion" |
| 89 | + [ bench "Extract 10 TxOuts" $ whnf (toPairList @Tx) utxo10 |
| 90 | + , bench "Extract 100 TxOuts" $ whnf (toPairList @Tx) utxo100 |
| 91 | + , bench "Extract 1000 TxOuts" $ whnf (toPairList @Tx) utxo1000 |
| 92 | + , bench "Serialize 10 TxOuts" $ whnf (fmap (utxoToElement @Tx)) elements10 |
| 93 | + , bench "Serialize 100 TxOuts" $ whnf (fmap (utxoToElement @Tx)) elements100 |
| 94 | + ] |
| 95 | + , bgroup |
| 96 | + "3. Create Membership Proofs" |
| 97 | + [ bench "5 from 50" $ whnfIO $ createMembershipProofFromUTxO @Tx subset5_from50 acc50 defaultCRS |
| 98 | + , bench "10 from 100" $ whnfIO $ createMembershipProofFromUTxO @Tx subset10_from100 acc100 defaultCRS |
| 99 | + , bench "50 from 500" $ whnfIO $ createMembershipProofFromUTxO @Tx subset50_from500 acc500 defaultCRS |
| 100 | + , bench "100 from 1000" $ whnfIO $ createMembershipProofFromUTxO @Tx subset100_from1000 acc1000 defaultCRS |
| 101 | + , bench "500 from 5000" $ whnfIO $ createMembershipProofFromUTxO @Tx subset500_from5000 acc5000 defaultCRS |
| 102 | + , bench "1000 from 10000" $ whnfIO $ createMembershipProofFromUTxO @Tx subset1000_from10000 acc10000 defaultCRS |
| 103 | + ] |
| 104 | + , bgroup |
| 105 | + "4. Create Membership Proofs (Low-level)" |
| 106 | + [ bench "5 elements from 10" $ whnfIO $ createMembershipProof (take 5 serialized10) acc10 defaultCRS |
| 107 | + , bench "10 elements from 100" $ whnfIO $ createMembershipProof (take 10 serialized100) acc100 defaultCRS |
| 108 | + , bench "50 elements from 100" $ whnfIO $ createMembershipProof (take 50 serialized100) acc100 defaultCRS |
| 109 | + ] |
| 110 | + , bgroup |
| 111 | + "5. Accumulator Hashing" |
| 112 | + [ bench "Hash 10 UTxOs" $ nf getAccumulatorHash acc10 |
| 113 | + , bench "Hash 100 UTxOs" $ nf getAccumulatorHash acc100 |
| 114 | + , bench "Hash 1000 UTxOs" $ nf getAccumulatorHash acc1000 |
| 115 | + , bench "Hash 10000 UTxOs" $ nf getAccumulatorHash acc10000 |
| 116 | + ] |
| 117 | + , bgroup |
| 118 | + "6. Accumulator Serialization" |
| 119 | + [ bench "Serialize accumulator (10)" $ nf (serialise . unHydraAccumulator) acc10 |
| 120 | + , bench "Serialize accumulator (100)" $ nf (serialise . unHydraAccumulator) acc100 |
| 121 | + , bench "Serialize accumulator (1000)" $ nf (serialise . unHydraAccumulator) acc1000 |
| 122 | + , bench "Serialize accumulator (10000)" $ nf (serialise . unHydraAccumulator) acc10000 |
| 123 | + ] |
| 124 | + , bgroup |
| 125 | + "7. CRS Generation" |
| 126 | + [ bench "CRS size 10" $ whnf generateCRS 10 |
| 127 | + , bench "CRS size 100" $ whnf generateCRS 100 |
| 128 | + , bench "CRS size 1000" $ whnf generateCRS 1000 |
| 129 | + , bench "CRS size 5000" $ whnf generateCRS 5000 |
| 130 | + , bench "CRS size 10000" $ whnf generateCRS 10000 |
| 131 | + ] |
| 132 | + , bgroup |
| 133 | + "8. End-to-End Snapshot Simulation" |
| 134 | + [ bench "Full cycle: 100 UTxOs" $ nfIO (fullSnapshotCycle utxo100) |
| 135 | + , bench "Full cycle: 1000 UTxOs" $ nfIO (fullSnapshotCycle utxo1000) |
| 136 | + , bench "Partial fanout: 100 from 1000" $ nfIO (partialFanoutCycle utxo1000 subset100_from1000) |
| 137 | + ] |
| 138 | + ] |
| 139 | + |
| 140 | +-- | Generate a UTxO set of specified size with realistic transaction outputs. |
| 141 | +generateUTxO :: Int -> IO UTxO |
| 142 | +generateUTxO n = generate $ genUTxOAdaOnlyOfSize n |
| 143 | + |
| 144 | +-- | Generate a subset of a given UTxO. |
| 145 | +-- This simulates selecting UTxOs for partial fanout. |
| 146 | +generateSubset :: UTxO -> Int -> IO UTxO |
| 147 | +generateSubset utxo n = do |
| 148 | + let allPairs = UTxO.toList utxo |
| 149 | + if n >= length allPairs |
| 150 | + then pure utxo |
| 151 | + else do |
| 152 | + let subsetPairs = take n allPairs |
| 153 | + pure $ UTxO.fromList subsetPairs |
| 154 | + |
| 155 | +-- | Simulate the full snapshot creation cycle: |
| 156 | +-- 1. Build accumulator from UTxO |
| 157 | +-- 2. Hash the accumulator |
| 158 | +-- 3. Serialize for signing |
| 159 | +fullSnapshotCycle :: UTxO -> IO ByteString |
| 160 | +fullSnapshotCycle utxo = do |
| 161 | + let accumulator = buildFromUTxO @Tx utxo |
| 162 | + hash = getAccumulatorHash accumulator |
| 163 | + pure hash |
| 164 | + |
| 165 | +-- | Simulate a partial fanout operation: |
| 166 | +-- 1. Build accumulator from full UTxO |
| 167 | +-- 2. Create membership proof for subset |
| 168 | +-- 3. Return the proof |
| 169 | +partialFanoutCycle :: UTxO -> UTxO -> IO Text |
| 170 | +partialFanoutCycle fullUtxo subsetUtxo = do |
| 171 | + let accumulator = buildFromUTxO @Tx fullUtxo |
| 172 | + createMembershipProofFromUTxO @Tx subsetUtxo accumulator defaultCRS |
| 173 | + |
0 commit comments