Skip to content

Commit

Permalink
Simplify the VRF mkSeed
Browse files Browse the repository at this point in the history
This makes two actual:
1. change the hash function from SHA256
3. serialise the slot number using raw serialisation, not CBOR

On 1, SHA256 was just the default suggestion, not a requirement. It's
simpler to simply use the same 256bit hash as we use everywhere else.

On 2, we should avoid CBOR serialisation for internal hashing, signing
etc. For one thing it's not quick.
  • Loading branch information
dcoutts committed Jul 14, 2020
1 parent 6611345 commit 488cc7c
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,6 @@ import Data.Aeson (FromJSON (..), ToJSON (..))
import qualified Data.Binary.Put as B
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Data.Coerce (coerce)
import qualified Data.Fixed as FP (Fixed, HasResolution, resolution)
import Data.Functor.Identity
import Data.Ratio (Ratio, denominator, numerator, (%))
Expand Down Expand Up @@ -156,7 +155,7 @@ interval1 = UnsafeUnitInterval 1

-- | Evolving nonce type.
data Nonce
= Nonce !(Hash SHA256 Nonce)
= Nonce !(Hash Blake2b_256 Nonce)
| -- | Identity element
NeutralNonce
deriving (Eq, Generic, Ord, Show, NFData)
Expand Down Expand Up @@ -188,29 +187,30 @@ deriving anyclass instance FromJSON Nonce

-- | Evolve the nonce
(⭒) :: Nonce -> Nonce -> Nonce
(Nonce a) (Nonce b) = Nonce . coerce $ hashRaw @SHA256 id (getHash a <> getHash b)
Nonce a Nonce b = Nonce . castHash $
hashRaw id (getHash a <> getHash b)
x NeutralNonce = x
NeutralNonce x = x

-- | Make a nonce from the VRF output bytes
mkNonceFromOutputVRF :: VRF.OutputVRF v -> Nonce
mkNonceFromOutputVRF =
Nonce
. (castHash :: Hash SHA256 (VRF.OutputVRF v) -> Hash SHA256 Nonce)
. (castHash :: Hash Blake2b_256 (VRF.OutputVRF v) -> Hash Blake2b_256 Nonce)
. hashRaw VRF.getOutputVRFBytes

-- | Make a nonce from a number.
mkNonceFromNumber :: Word64 -> Nonce
mkNonceFromNumber =
Nonce
. (castHash :: Hash SHA256 Word64 -> Hash SHA256 Nonce)
. (castHash :: Hash Blake2b_256 Word64 -> Hash Blake2b_256 Nonce)
. hashRaw (BSL.toStrict . B.runPut . B.putWord64be)

-- | Seed to the verifiable random function.
--
-- We do not expose the constructor to `Seed`. Instead, a `Seed` should be
-- created using `mkSeed` for a VRF calculation.
newtype Seed = Seed (Hash SHA256 Seed)
newtype Seed = Seed (Hash Blake2b_256 Seed)
deriving (Eq, Ord, Show, Generic)
deriving newtype (NoUnexpectedThunks, ToCBOR)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ import Cardano.Binary
withSlice,
withWordSize,
)
import Cardano.Crypto.Hash (SHA256)
import qualified Cardano.Crypto.Hash.Class as Hash
import qualified Cardano.Crypto.KES as KES
import qualified Cardano.Crypto.VRF as VRF
Expand All @@ -85,6 +84,8 @@ import Cardano.Prelude
import Cardano.Slotting.Slot (WithOrigin (..))
import Control.Monad (unless)
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Builder as BS
import qualified Data.ByteString.Builder.Extra as BS
import qualified Data.ByteString.Lazy as BSL
import Data.Coerce (coerce)
import Data.Map.Strict (Map)
Expand Down Expand Up @@ -133,6 +134,7 @@ import Shelley.Spec.Ledger.Serialization
decodeSeq,
encodeFoldableEncoder,
encodeFoldableMapEncoder,
runByteBuilder,
)
import Shelley.Spec.Ledger.Slot (BlockNo (..), SlotNo (..))
import Shelley.Spec.Ledger.Tx (Tx (..), decodeWits, segwitTx, txWitsBytes)
Expand Down Expand Up @@ -596,21 +598,26 @@ hsig ::
hsig (BHeader _ s) = s

-- | Construct a seed to use in the VRF computation.
--
mkSeed ::
-- | Universal constant
Nonce ->
SlotNo ->
-- | Epoch nonce
Nonce ->
Seed
mkSeed uc slot nonce = Seed . coerce $ case uc of
NeutralNonce -> seed
Nonce ucNonce -> ucNonce `Hash.xor` seed
where
seed = coerce $ Hash.hashRaw @SHA256 id (serialize' slot <> nonceBytes)
nonceBytes = case nonce of
NeutralNonce -> mempty
Nonce n -> Hash.getHash n
mkSeed ucNonce (SlotNo slot) eNonce =
Seed
. (case ucNonce of
NeutralNonce -> id
Nonce h -> Hash.xor (Hash.castHash h))
. Hash.castHash
. Hash.hashRaw id
. runByteBuilder (8+32) $
BS.word64BE slot
<> (case eNonce of
NeutralNonce -> mempty
Nonce h -> BS.byteStringCopy (Hash.getHash h))

-- | Check that the certified input natural is valid for being slot leader. This
-- means we check that
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ module Shelley.Spec.Ledger.Serialization
ipv6FromBytes,
ipv6ToCBOR,
ipv6FromCBOR,
-- Raw
runByteBuilder,
)
where

Expand Down Expand Up @@ -74,6 +76,8 @@ import Data.Binary.Get (Get, getWord32le, runGetOrFail)
import Data.Binary.Put (putWord32le, runPut)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import qualified Data.ByteString.Builder as BS
import qualified Data.ByteString.Builder.Extra as BS
import Data.Foldable (foldl')
import Data.Functor.Compose (Compose (..))
import Data.IP
Expand Down Expand Up @@ -333,3 +337,22 @@ ipv6ToCBOR = toCBOR . ipv6ToBytes

ipv6FromCBOR :: Decoder s IPv6
ipv6FromCBOR = byteDecoderToDecoder "IPv6" ipv6FromBytes

--
-- Raw serialisation
--

-- | Run a ByteString 'BS.Builder' using a strategy aimed at making smaller
-- things efficiently.
--
-- It takes a size hint and produces a strict 'ByteString'. This will be fast
-- when the size hint is the same or slightly bigger than the true size.
--
runByteBuilder :: Int -> BS.Builder -> BS.ByteString
runByteBuilder !sizeHint =
BSL.toStrict
. BS.toLazyByteStringWith
(BS.safeStrategy sizeHint (2 * sizeHint))
mempty
{-# NOINLINE runByteBuilder #-}

0 comments on commit 488cc7c

Please sign in to comment.