mirror of
https://github.com/logos-storage/logos-storage-proofs-circuits.git
synced 2026-01-02 21:43:10 +00:00
131 lines
4.0 KiB
Haskell
131 lines
4.0 KiB
Haskell
|
|
module Slot where
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
import Data.Bits
|
|
import Data.Word
|
|
|
|
import Data.ByteString (ByteString)
|
|
import qualified Data.ByteString as B
|
|
import qualified Data.ByteString.Char8 as C
|
|
|
|
import Control.Monad
|
|
import System.IO
|
|
|
|
import Poseidon2
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
type Seed = Int
|
|
type CellIdx = Int
|
|
type Hash = Fr
|
|
|
|
data DataSource
|
|
= SlotFile FilePath
|
|
| FakeData Seed
|
|
deriving Show
|
|
|
|
data SlotConfig = MkSlotCfg
|
|
{ _cellSize :: Int -- ^ cell size in bytes
|
|
, _nCells :: Int -- ^ number of cells per slot (should be power of two)
|
|
, _nSamples :: Int -- ^ how many cells we sample
|
|
, _dataSrc :: DataSource -- ^ slot data source
|
|
}
|
|
deriving Show
|
|
|
|
-- | Example slot configuration
|
|
exSlotCfg :: SlotConfig
|
|
exSlotCfg = MkSlotCfg
|
|
{ _cellSize = 256
|
|
, _nCells = 1024
|
|
, _nSamples = 20
|
|
, _dataSrc = FakeData 12345
|
|
}
|
|
|
|
fieldElemsPerCell :: SlotConfig -> Int
|
|
fieldElemsPerCell cfg = (_cellSize cfg + 30) `div` 31
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Writes a @circom@ main component with the given parameters
|
|
--
|
|
-- > template SampleAndProveV1( nCells, nFieldElemsPerCell, nSamples ) { ... }
|
|
--
|
|
circomMainComponentV1 :: SlotConfig -> FilePath -> IO ()
|
|
circomMainComponentV1 slotCfg circomFile = do
|
|
|
|
let params = show (_nCells slotCfg)
|
|
++ ", " ++ show (fieldElemsPerCell slotCfg)
|
|
++ ", " ++ show (_nSamples slotCfg)
|
|
|
|
writeFile circomFile $ unlines
|
|
[ "pragma circom 2.0.0;"
|
|
, "include \"sample_cells.circom\";"
|
|
, "component main {public [entropy,slotRoot]} = SampleAndProveV1(" ++ params ++ ");"
|
|
]
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- * load data
|
|
|
|
genFakeCell :: SlotConfig -> Seed -> CellIdx -> ByteString
|
|
genFakeCell cfg seed1 seed2 = B.pack list where
|
|
list = go (_cellSize cfg) 1
|
|
go :: Int -> Int -> [Word8]
|
|
go 0 _ = []
|
|
go cnt state = fromIntegral state' : go (cnt-1) state' where
|
|
state' = state*state + seed1*state + (seed2 + 17)
|
|
|
|
loadCellData :: SlotConfig -> CellIdx -> IO ByteString
|
|
loadCellData cfg idx = case _dataSrc cfg of
|
|
FakeData seed -> return $ genFakeCell cfg seed idx
|
|
SlotFile fname -> do
|
|
h <- openBinaryFile fname ReadMode
|
|
hSeek h AbsoluteSeek (fromIntegral (_cellSize cfg) * fromIntegral idx)
|
|
bs <- B.hGet h (_cellSize cfg)
|
|
hClose h
|
|
return bs
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
calcSlotTree :: SlotConfig -> IO MerkleTree
|
|
calcSlotTree cfg = calcMerkleTree <$> calcCellHashes cfg
|
|
|
|
calcCellHashes :: SlotConfig -> IO [Hash]
|
|
calcCellHashes cfg = do
|
|
forM [0..(_nCells cfg - 1)] $ \idx -> do
|
|
cell <- loadCellData cfg idx
|
|
return (hashCell cell)
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
-- | Split bytestring into smaller pieces
|
|
splitByteString :: Int -> ByteString -> [ByteString]
|
|
splitByteString k = go where
|
|
go bs
|
|
| B.null bs = []
|
|
| otherwise = B.take k bs : go (B.drop k bs)
|
|
|
|
-- | Chunk a ByteString into a sequence of field elements
|
|
cellDataToFieldElements :: ByteString -> [Fr]
|
|
cellDataToFieldElements rawdata = map chunkToField pieces where
|
|
chunkSize = 31
|
|
pieces = splitByteString chunkSize rawdata
|
|
|
|
-- | Hash a cell
|
|
hashCell :: ByteString -> Hash
|
|
hashCell rawdata = sponge2 (cellDataToFieldElements rawdata)
|
|
|
|
chunkToField :: ByteString -> Fr
|
|
chunkToField chunk
|
|
| B.length chunk <= 31 = fromInteger (chunkToIntegerLE chunk)
|
|
| otherwise = error "chunkToField: chunk is too big (expecting at most 31 bytes)"
|
|
|
|
-- | Interpret a ByteString as an integer (little-endian)
|
|
chunkToIntegerLE :: ByteString -> Integer
|
|
chunkToIntegerLE chunk = go (B.unpack chunk) where
|
|
go [] = 0
|
|
go (w:ws) = fromIntegral w + shiftL (go ws) 8
|
|
|
|
--------------------------------------------------------------------------------
|