web3-polkadot-1.0.1.0: src/Network/Polkadot/Storage/Key.hs
{-# LANGUAGE GADTs #-}
{-# LANGUAGE RecordWildCards #-}
-- |
-- Module : Network.Polkadot.Storage.Key
-- Copyright : Aleksandr Krupenkin 2016-2024
-- License : Apache-2.0
--
-- Maintainer : mail@akru.me
-- Stability : experimental
-- Portability : portable
--
-- When you use the Substrate RPC to access a storage item,
-- you only need to provide the key associated with that item.
--
module Network.Polkadot.Storage.Key where
import Codec.Scale (encode)
import Codec.Scale.Class (Encode (..))
import Control.Arrow ((&&&))
import Data.ByteString (ByteString)
import Data.Digest.Blake2 (blake2_128, blake2_256)
import Data.Digest.XXHash (xxhash)
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8)
import Network.Polkadot.Metadata.V11 (DoubleMapType (..),
MapType (..),
StorageHasher (..))
import Network.Polkadot.Metadata.V13 (NMapType (..),
StorageEntryMetadata (..),
StorageEntryType (..))
-- | General type wrapper for SCALE encodable storage index argument.
data Argument where
Argument :: Encode a => a -> Argument
-- ^ Wrapped type should be encodable.
instance Encode Argument where
put arg = case arg of Argument a -> put a
-- | Hasher is a function that hash given argument.
type Hasher = Argument -> ByteString
-- | Entry type describe storage prefix for different storage entity types.
data StorageEntry
= PlainEntry ByteString
-- ^ Simple storage type without arguments.
| MapEntry (Argument -> ByteString)
-- ^ Mapping with hashing for arguments.
| DoubleMapEntry (Argument -> Argument -> ByteString)
-- ^ Double map with two different hashers.
| NMapEntry ([Argument] -> ByteString)
-- ^ Map with array of hashers.
instance Show StorageEntry where
show (PlainEntry _) = "PlainEntry"
show (MapEntry _) = "MapEntry"
show (DoubleMapEntry _) = "DoubleMapEntry"
show (NMapEntry _) = "NMapEntry"
-- | Create storage key generator from metadata description.
newEntry :: Text
-- ^ Storage prefix (module name).
-> StorageEntryMetadata
-- ^ Storage key metadata, includes entry type, name, etc.
-> StorageEntry
-- ^ Storage key generator.
newEntry prefix meta = case entryType meta of
Plain _ -> PlainEntry plainKey
Map MapType{..} -> MapEntry (mapCodec mapHasher)
DoubleMap DoubleMapType{..} -> DoubleMapEntry (dMapCodec doubleMapHasher doubleMapKey2Hasher)
NMap NMapType{..} -> NMapEntry undefined -- TODO
where
method = entryName meta
-- To calculate the key for a simple Storage Value,
-- take the TwoX 128 hash of the name of the module that contains the Storage Value
-- and append to it the TwoX 128 hash of the name of the Storage Value itself.
plainKey = xxhash 128 (encodeUtf8 prefix) <> xxhash 128 (encodeUtf8 method)
-- Like Storage Values, the keys for Storage Maps are equal to the TwoX 128 hash
-- of the name of the module that contains the map prepended to the TwoX 128 hash
-- of the name of the Storage Map itself.
mapCodec h1 arg1 = plainKey <> getHasher h1 arg1
dMapCodec h1 h2 arg1 arg2 = mapCodec h1 arg1 <> getHasher h2 arg2
getHasher :: StorageHasher -> Hasher
getHasher Blake2_128 = blake2_128 . encode
getHasher Blake2_256 = blake2_256 . encode
getHasher Blake2_128Concat = uncurry (<>) . (blake2_128 &&& id) . encode
getHasher Twox128 = xxhash 128 . encode
getHasher Twox256 = xxhash 256 . encode
getHasher Twox64Concat = uncurry (<>) . (xxhash 64 &&& id) . encode
getHasher Identity = encode