Binary Protocols

Sizes of all fields are represented in bytes. Big-Endian is used everywhere. Composite types are serialized in the order of definition with no delimiters.

For example, (Word32, Word8) is serialized with 5 bytes: 4 for Word32, and 1 for Word8.

For variable-length structures, dependent on object of type T, we use size(T) notation.

Word32 is unsigned integer of 32 bits (uint32).

To test serialization of object myObject in ghci, one should use the following commands in cardano-sl root project directory:

$ stack repl
...
<Press Enter>
...
ghci> import Data.ByteString.Builder
ghci> let hexEncode myObject = toLazyByteString $ lazyByteStringHex $ Pos.Binary.encode $ myObject
ghci> hexEncode (mkCoin 1000)
"0064"

Common Haskell Data Types

Maybe

data Maybe a = Nothing | Just a

Maybe a is either value of type a or empty (aka null, None). To distinguish between two values we add 1 byte tag before data.

Tag size Tag Type Tag Value Description Field size Description
1 Word8 0x00 Tag for Nothing    
    0x01 Tag for Just    
        size(a) Value of type a

Example:

ghci> hexEncode (Nothing :: Maybe Word32)
"00"
ghci> hexEncode (Just 4  :: Maybe Word32)
"0100000004"

Either

data Either a b = Left a | Right b

Either a b is either value of type a or value of type b. To distinguish between two values we add 1 byte tag before data.

Tag size Tag Type Tag Value Description Field size Description
1 Word8 0x00 Tag for Left    
        size(a) Value of type a
    0x01 Tag for Right    
        size(b) Value of type b

Example:

ghci> hexEncode (Left 3  :: Either Word16 Word32)
"000003"
ghci> hexEncode (Right 4 :: Either Word16 Word32)
"0100000004"

Big Integer

-- Fixed-size type for a subset of Integer
type SmallInt = Int32

Integers are encoded in two ways: if they fit inside a SmallInt, they’re written as a byte tag, and that value. If the Integer value is too large to fit in a SmallInt, it is written as a byte array, along with a sign and length field.

For reference, see implementation.

Example:

ghci> hexEncode $ (15 :: Integer)
"000000000f"
ghci> hexEncode $ (  (2 :: Integer) ^ (128 :: Integer))
"010100000000000000110000000000000000000000000000000001"
ghci> hexEncode $ (- (2 :: Integer) ^ (128 :: Integer))
"01ff00000000000000110000000000000000000000000000000001"

Unsigned Variable Length Integer

This type will be referenced to later as UVarInt Word16 or UVarInt Word64 to describe maximum available value.

newtype UnsignedVarInt a = UnsignedVarInt {getUnsignedVarInt :: a}
    deriving (Eq, Ord, Show, Generic, NFData)

Values are encoded 7 bits at a time, with the most significant one being a continuation bit. Thus, the numbers from 0 to 127 require only a single byte to encode, those from 128 to 16383 require two bytes, etc.

This format is taken from Google’s Protocol Buffers, which provides a bit more verbiage on the encoding.

Example:

ghci> hexEncode (UnsignedVarInt (3 :: Word32))
"03"
ghci> hexEncode (UnsignedVarInt (126 :: Word32))
"7e"
ghci> hexEncode (UnsignedVarInt (127 :: Word32))
"7f"
ghci> hexEncode (UnsignedVarInt (128 :: Word32))
"8001"

Tiny Variable Length Integer

-- | A newtype wrapper for non-negative integers less than @2^14@. Use it if
-- you want to be extra careful. Compared to 'SignedVarInt' and
-- 'UnsignedVarInt', it provides two benefits:
--
-- * It is guaranteed to take either 1 or 2 bytes (the standard decoder for
--   variants can consume an unlimited amount of bytes).
--
-- * It is unambiguous (e.g. @0@ can be encoded in only one way instead of
--   two).
newtype TinyVarInt = TinyVarInt {getTinyVarInt :: Word16}
    deriving (Eq, Ord, Show, Generic, NFData)
Field size Type Description
1-2 UVarInt Word16 Variable length integer up to 2^14 - 1

Example:

ghci> hexEncode $ TinyVarInt 0
"00"
ghci> hexEncode $ TinyVarInt (2^14 -1)
"ff7f"

Lists, NonEmpty and Vectors

Sometimes we store a list of some objects inside our datatypes. You will see references to them as Vector a or [a]. You should read this as array of objects of types a. Both of these standard Haskell data types are serialized in the same way. If you see NonEmpty a in type you should read it as [a] but the size of that list is guaranteed to be at least 1.

Field size Type Value Description
1-9 UVarInt Int n Size of array
n * size(a) a[n]   Array with length n of objects of type a

Example:

ghci> hexEncode ([1, 31] :: [Word16])
"020001001f"
ghci> hexEncode ([0..135] :: [Word8])  -- 136 bytes from 0 to 135 including
"8801000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252
62728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4
f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f70717273747576777
8797a7b7c7d7e7f8081828384858687"

HashMap

HashMap key value is mapping from keys to values. In serialization, HashMap is represented as list of pairs from key and value and thus is serialized as [(key, value)].

Field size Type Value Description
1-9 UVarInt Int n Size of HashMap
n * (size(key) + size(value)) <key, value>[n]   Array with length n of objects of type (key, value)

Example:

ghci> hexEncode $ Data.HashMap.Strict.fromList [(1 :: Word8, 127 :: Word64), (2, 255)]
"0201000000000000007f0200000000000000ff"

Networking

MessageName

newtype MessageName = MessageName BS.ByteString

Generally, we use MessageName represented by one or two encoded UnsignedVarInts. At serialization state it is encoded as binary string, thus gets prefixed by length of the string.

Example:

ghci> hexEncode $ messageName (Proxy :: Proxy SendProxySK)
"0102"
ghci> hexEncode $ messageName (Proxy :: Proxy (DataMsg GtMsgContents))
"020a03"

Basic Cardano SL Data Types

Coin

-- | Coin is the least possible unit of currency.
newtype Coin = Coin
    { getCoin :: Word64
    } deriving (Show, Ord, Eq, Bounded, Generic, Hashable, Data, NFData)

Number of total coins is 45*10^9 * 10^6.

45*10^15 needs 56 bits to represent
45*10^9  (integral mega coins) needs 36 bits to represent
999999   (floating mega coins) needs 20 bits to represent
Decimal Needed Bits
0-9 4 bits
0-99 7 bits
0-999 10 bits
0-9999 14 bits
0-99999 17 bits
0-999999 20 bits

Coin is splitted in mega coin (10^6) and the remaining coin for serialization.

1000999 coin = 1.000999 mega coin

Simple variant encoding with Word64 limit. The total length of the sequence is encoded in the first byte with a variable mask.

header mask spare bits extra byte total bits as value serialized size
0 xxxxxxx 0x7f 7 bits 0 7 bits 1 byte
10 xxxxxx 0x3f 6 bits 1 14 bits 2 bytes
110 xxxxx 0x1f 5 bits 2 21 bits 3 bytes
1110 xxxx 0x0f 4 bits 3 27 bits 4 bytes
11110 xxx 0x07 3 bits 4 35 bits 5 bytes
111110 xx 0x03 2 bits 5 42 bits 6 bytes
1111110 x 0x01 1 bit 6 49 bits 7 bytes
11111110 0x00 0 bit 7 56 bits 8 bytes
11111111 0x00 0 bit 8 64 bits 9 bytes

Specialized to the integral part which only needs 36 bits maximum:

header mask spare bits extra byte total bits as value serialized size
0 xxxxxxx 0x7f 7 bits 0 7 bits 1 byte
10 xxxxxx 0x3f 6 bits 1 14 bits 2 bytes
110 xxxxx 0x1f 5 bits 2 21 bits 3 bytes
1110 xxxx 0x0f 4 bits 3 27 bits 4 bytes
1111 xxxx 0x0f 4 bits 4 36 bits 5 bytes

And the floating part, needs 20 bits to represent, encoding value from 0 to 999999:

header mask spare bits extra byte total bits as value serialized size
0 xxxxxx 0x7f 7 bits 0 7 bits 1 byte
10 xxxxxx 0x3f 6 bits 1 14 bits 2 bytes
110 xxxxx 0x3f 5 bits 2 21 bits 3 bytes

Note: we could save one bit in the 3 bytes scheme here by considering the end of encoding but we don’t need it, so by not changing the scheme we can re-use the previous scheme for integral as is.

For details of implementations look at this module.

Examples:

ghci> hexEncode (mkCoin 0)
"0000"
ghci> hexEncode (mkCoin 1)
"00c186a0"
ghci> hexEncode (mkCoin 2)
"00c30d40"
ghci> hexEncode (mkCoin 31)
"00c1fbd0"
ghci> hexEncode (mkCoin 128)
"00cc8708"
ghci> hexEncode (mkCoin 129)
"00ce0da8"
ghci> hexEncode (mkCoin 1000)
"0064"
ghci> hexEncode (mkCoin 10000)
"000a"
ghci> hexEncode (mkCoin 1000000)
"0100"
ghci> hexEncode (mkCoin 1000999)
"01cf3e58"

Hash

-- | Hash wrapper with phantom type for more type-safety.
-- Made abstract in order to support different algorithms in
-- different situations
newtype AbstractHash algo a = AbstractHash (Digest algo)
    deriving (Show, Eq, Ord, ByteArray.ByteArrayAccess, Generic, NFData)

-- | Type alias for commonly used hash
type Hash = AbstractHash Blake2b_256
Field size Type Description
32 Word8[32] 256 bits of hash digest

So whenever you see Hash SomeType in the code, this field will occupy 32 bytes. An additional type parameter after Hash is used only in code for type-safety and has no impact on serialization.

Example:

ghci> hash $ mkCoin 3
AbstractHash 29bcdcff253cd2864a8b5e25992a6db86a7a41dc5e69c0599730f2c5716d9362
ghci> hexEncode $ hash $ mkCoin 3
"29bcdcff253cd2864a8b5e25992a6db86a7a41dc5e69c0599730f2c5716d9362"

Public Key

-- | Wrapper around 'Ed25519.PublicKey'.
newtype PublicKey = PublicKey Ed25519.PublicKey
    deriving (Eq, Ord, Show, Generic, NFData, Hashable, Typeable)
Field size Type Description
32 Word8[32] 32 bytes of public key

Signature

-- | Wrapper around 'Ed25519.Signature'.
newtype Signature a = Signature Ed25519.Signature
    deriving (Eq, Ord, Show, Generic, NFData, Hashable, Typeable)
Field size Type Description
64 Word8[64] 64 bytes of signature string

Epoch Index

-- | Index of epoch.
newtype EpochIndex = EpochIndex
    { getEpochIndex :: Word64
    } deriving (Show, Eq, Ord, Num, Enum, Integral, Real, Generic, Hashable, Bounded, Typeable)
Field size Type Description
1-10 UVarInt Word64 epoch index

Example:

ghci> hexEncode (EpochIndex 128)
"8001"

Local Slot Index

-- | Index of slot inside a concrete epoch.
newtype LocalSlotIndex = LocalSlotIndex
    { getSlotIndex :: Word16
    } deriving (Show, Eq, Ord, Num, Enum, Ix, Integral, Real, Generic, Hashable, Buildable, Typeable)
Field size Type Description
1-3 UVarInt Word16 index of local slot

Example:

ghci> hexEncode (LocalSlotIndex 15)
"0f"

SlotId

-- | Slot is identified by index of epoch and local index of slot in
-- this epoch. This is a global index
data SlotId = SlotId
    { siEpoch :: !EpochIndex
    , siSlot  :: !LocalSlotIndex
    } deriving (Show, Eq, Ord, Generic, Typeable)
Field size Type Description
1-10 UVarInt Word64 Epoch index
1-3 UVarInt Word16 Slot index inside a concrete epoch

Example:

ghci> hexEncode (SlotId 128 15)
"80010f"

Attributes

-- | Convenient wrapper for the datatype to represent it (in binary
-- format) as k-v map.
data Attributes h = Attributes
    { -- | Data, containing known keys (deserialized)
      attrData   :: h
      -- | Unparsed ByteString
    , attrRemain :: ByteString
    }
  deriving (Eq, Ord, Generic, Typeable)

General Case Serialization

Stored as totalLen + (k, v) pairs + some remaining part. attrData is stored as list of pairs (Word8, v) where key has type Word8 and you should specify how to encode h in that way.

Field size Type Value Description
1-9 UVarInt Int64 m + n Size of attributes in bytes
m = t * (1 + size(v)) <Word8,v>[t]   Array of pairs. Given without length.
n ByteString   Remaining byte array

Example:

ghci> toLazyByteString
      $ lazyByteStringHex
      $ runPut
      $ putAttributes (\h -> [(1, put h), (0, put h)])
      $ Attributes (9 :: Word32) "abc"
"0d0000000009010000000961626"

Here h = 9 :: Word32 and we encode h as two key-value pairs: value is 4-byte 9 and keys are 0 and 1.

Attributes ()

In this special case no (key, value) pairs are stored — only arbitrary length byte array.

Field size Type Value Description
1-4 UVarInt Int64 n Size of attributes in bytes. Should be < 2^28
n Word8[n]   n bytes of data

Example:

ghci> hexEncode $ Attributes () (BSS.pack [])
"00"
ghci> hexEncode $ Attributes () (BSS.pack [1,31])
"02011f"
ghci> hexEncode $ Attributes () "abc"
"03616263"

Script

-- | Version of script
type ScriptVersion = Word16

-- | A script for inclusion into a transaction.
data Script = Script {
    scrVersion :: ScriptVersion,    -- ^ Version
    scrScript  :: LByteString}      -- ^ Serialized script
  deriving (Eq, Show, Generic, Typeable)
Field size Type Value Description
1-3 UVarInt Word16   Script version
1-9 UVarInt Int64 n Size of byte array
n Word8[n]   n bytes of script

Example:

ghci> hexEncode $ Script 0 "a"
"000161"

Address Attributes

data AddrPkAttrs = AddrPkAttrs
    { addrPkDerivationPath :: Maybe [Word32]
    } deriving (Eq, Ord, Generic, Typeable, Show)

type PubKeyAddressAttributes = Attributes AddrPkAttrs
Size Type Value Description
1-2 TinyVarInt n + m sise of PubKeyAddress content
n Maybe (Word8, [Word32])   Empty for nothing and list of Word32 preceded with zero
m ByteString   Remaining bytes

See examples in next section.

Address

type AddressHash = AbstractHash Blake2b_224

-- | Stakeholder identifier (stakeholders are identified by their public keys)
type StakeholderId = AddressHash PublicKey

-- | Address is where you can send coins.
data Address
    = PubKeyAddress
          { addrKeyHash      :: !(AddressHash PublicKey)
          , addrPkAttributes :: !PubKeyAddressAttributes }
    | ScriptAddress
          { addrScriptHash :: !(AddressHash Script) }
    | UnknownAddressType !Word8 !ByteString
    deriving (Eq, Ord, Generic, Typeable, Show)

Public Key Address

addrPkAttributes field is required for HD-wallets.

Size Type Value Description
1 Word8 0x00 PubKeyAddress tag
1-2 TinyVarInt 28 + m Size of PubKeyAddress content
28 Word8[28]   addKeyHash: 28 bytes of Blake2b_224 hash
m PubKeyAddressAttributes   addrPkAttributes
4 Word32   CRC32 of all previous data

Example:

ghci> abstractHash somPk :: AddressHash PublicKey
AbstractHash 380dea393a631ad563154a13bc5ee49fa4b62a60218358b5dcb875e0
ghci> hexEncode $ PubKeyAddress (abstractHash somePk) (Attributes (AddrPkAttrs Nothing) "a")
"001e380dea393a631ad563154a13bc5ee49fa4b62a60218358b5dcb875e00161cf52c5ec"
ghci> hexEncode $ PubKeyAddress (abstractHash somePk) (Attributes (AddrPkAttrs $ Just [3,9]) "a")
"0028380dea393a631ad563154a13bc5ee49fa4b62a60218358b5dcb875e00b0002000000030000000961f1d810f7"

You can notice in first example, that 0xCF52C5EC is CRC32 of 001e380dea393a631ad563154a13bc5ee49fa4b62a60218358b5dcb875e00161.

Script Address

Size Type Value Description
1 Word8 0x01 ScriptAddress tag
1 Word8 0x1C Size of addrScriptHash: always 28
28 Word8[28]   28 bytes of Blake2b_224 hash
4 Word32   CRC32 of all previous data

Example:

ghci> hexEncode $ ScriptAddress (abstractHash $ Script 0 "a")
"011c7ec20301993e369571c6225e1e563812198433801820a2d7328756dc61c5be8e"

As you can notice, 4-byte suffix 0x61c5be8e is CRC32 of prefix: 011c7ec20301993e369571c6225e1e563812198433801820a2d7328756dc.

Unknown Address

Size Type Value Description
1   t UnknownAddress tag
1-2 TinyVarInt n Size of unknown address
n Word8[n]   Unknown address itself
4 Word32   CRC32 of all previous data

Example:

Let’s encode UnknownAddressType 3 "a". Without CRC32 this will be encoded as 0x030161. CRC32 of 0x030161 is 0xDEA907C4. Thus we have:

ghci> hexEncode $ UnknownAddressType 3 "a"
"030161dea907c4"

MerkleRoot

-- | Data type for root of merkle tree.
newtype MerkleRoot a = MerkleRoot
    { getMerkleRoot :: Hash Raw  -- ^ returns root 'Hash' of Merkle Tree
    } deriving (Show, Eq, Ord, Generic, ByteArrayAccess, Typeable)
Field size Type Description
32 Hash Raw Root hash of Merkle tree

Chain Difficulty

-- | Chain difficulty represents necessary effort to generate a
-- chain. In the simplest case it can be the number of blocks in the chain.
newtype ChainDifficulty = ChainDifficulty
    { getChainDifficulty :: Word64
    } deriving (Show, Eq, Ord, Num, Enum, Real, Integral, Generic, Buildable, Typeable)
Size Type Description
1-10 UVarInt Word64 Chain difficulty

SlotLeaders

-- | 'NonEmpty' list of slot leaders.
type SlotLeaders = NonEmpty StakeholderId
Field size Type Value Description
1-9 UVarInt Int n Size of slot leaders list
n * size(StakeholderId) StakeholderId[n]   List of slot leaders of size n

GodTossing

Crypto.Pvss

Types prefixed with Pvss are defined in Crypto.Pvss module. This section describes binary format of these types.

Point

newtype Point = Point { unPoint :: SSL.EcPoint }
    deriving (Generic)
Field size Type Description
33 ByteString Binary represented elliptic curve point (see ecPointFromOct function)

Secret

-- | Secret
newtype Secret = Secret Point
    deriving (Show,Eq,NFData,Binary)
Field size Type Description
size(Point) Point Secret

Proof

newtype Scalar = Scalar { unScalar :: Integer }
    deriving (Show,Eq,Generic,NFData)

newtype Challenge = Challenge ByteString
    deriving (Show,Eq,NFData)

-- | The generated proof
data Proof = Proof
    { proof_c :: !Challenge
    , proof_z :: !Scalar
    } deriving (Show,Eq,Generic)
Field size Type Description
32 ByteString 32-byte string challenge
32 Integer 32-byte integer scalar

DecryptedShare

-- | An decrypted share decrypted by a party's key and
data DecryptedShare = DecryptedShare
    { decryptedShareID    :: !ShareId
    , shareDecryptedVal   :: !Point -- ^ decrypted share
    , decryptedValidProof :: !Proof -- ^ proof the decryption is valid
    } deriving (Show,Eq,Generic)
Field size Type Description
32 Integer Share id
size(Point) Point Decrypted share
size(Proof) Proof Proof the description is valid

ExtraGen

-- | Extra generator
newtype ExtraGen = ExtraGen Point
    deriving (Show,Eq,NFData,Binary)
Field size Type Description
size(Point) Point Extra generator

Commitment

newtype Commitment = Commitment { unCommitment :: Point }
    deriving (Show,Eq,NFData,Binary)
Field size Type Description
size(Point) Point Commitment

PublicKey

-- | Public Key
newtype PublicKey = PublicKey Point
    deriving (Show,Eq,NFData,Binary)
Field size Type Description
size(Point) Point Public key

Secret Sharing

Most of following types are just aliases for Pvss types, so they are serialized in the same way.

-- | Secret can be generated by `genSharedSecret` function along with shares.
newtype Secret = Secret
    { getSecret :: Pvss.Secret
    } deriving (Show, Eq)

-- | Shares can be used to reconstruct Secret.
newtype Share = Share
    { getShare :: Pvss.DecryptedShare
    } deriving (Show, Eq)

-- | Encrypted share which needs to be decrypted using VssKeyPair first.
newtype EncShare = EncShare
    { getEncShare :: Pvss.EncryptedShare
    } deriving (Show, Eq)

-- | This extra data may be used to verify encrypted share.
data SecretSharingExtra =
    SecretSharingExtra !Pvss.ExtraGen
                       ![Pvss.Commitment]
    deriving (Show, Eq, Generic)

-- | SecretProof may be used to commit Secret without revealing it.
newtype SecretProof =
    SecretProof Pvss.Proof
    deriving (Show, Eq, Generic)

-- | This key is used as public key in VSS.
newtype VssPublicKey = VssPublicKey
    { getVssPublicKey :: Pvss.PublicKey
    } deriving (Show, Eq)

Format of SecretSharingExtra:

Field size Type Value Description
size(ExtraGen) Pvss.ExtraGen   Extra generator
1-9 UVarInt Int n Length of commitments list
n * size(Pvss.Commitment) [Pvss.Commitment]   Commitments

Commitments, Openings and Shares

Commitment

-- | Commitment is a message generated during the first stage of
-- GodTossing. It contains encrypted shares and proof of secret.
-- Invariant which must be ensured: commShares is not empty.
data Commitment = Commitment
    { commExtra  :: !(AsBinary SecretSharingExtra)
    , commProof  :: !(AsBinary SecretProof)
    , commShares :: !(HashMap (AsBinary VssPublicKey) (AsBinary EncShare))
    } deriving (Show, Eq, Generic)
Field size Type Value Description
1-9 UVarInt Int n  
n * (sizeof(VssPublicKey) + sizeof(EncShare)) HashMap (AsBinary VssPublicKey) (AsBinary EncShare)   commShares
sizeof(SecretSharingExtra) AsBinary SecretSharingExtra   commExtra
sizeof(SecretProof) AsBinary SecretProof   commProof

CommitmentSignature

-- | Signature which ensures that commitment was generated by node
-- with given public key for given epoch.
type CommitmentSignature = Signature (EpochIndex, Commitment)
Field size Type Description
size(Signature) Signature (EpochIndex, Commitment) Signature which ensures that commitment was generated by node with given public key for given epoch

SignedCommitment

type SignedCommitment = (PublicKey, Commitment, CommitmentSignature)
Field size Type Description
size(PublicKey) PublicKey Public key of node that generated this commitment
size(Commitment) Commitment Commitment
size(CommitmentSignature) CommitmentSignature Commitment signature

CommitmentsMap

-- | 'CommitmentsMap' is a wrapper for 'HashMap StakeholderId SignedCommitment'
-- which ensures that keys are consistent with values, i. e. 'PublicKey'
-- from 'SignedCommitment' corresponds to key which is 'StakeholderId'.
newtype CommitmentsMap = CommitmentsMap
    { getCommitmentsMap :: HashMap StakeholderId SignedCommitment
    } deriving (Semigroup, Monoid, Show, Eq, Container)
Field size Type Value Description
1-9 UVarInt Int n Number of commitments in map
n * (size(StakeholderId) + size(SignedCommitment)) HashMap StakeholderId SignedCommitment   Commitments map as list of pairs

Opening

-- | Opening reveals secret.
newtype Opening = Opening
    { getOpening :: (AsBinary Secret)
    } deriving (Show, Eq, Generic, Buildable)
Field size Type Description
size(Secret) AsBinary Secret Revealed secret

OpeningsMap

type OpeningsMap = HashMap StakeholderId Opening
Field size Type Value Description
1-9 UVarInt Int n Number of openings in map
n * (size(StakeholderId) + size(Opening)) HashMap StakeholderId Opening   Openings map as list of pairs

VssCertificate

-- | VssCertificate allows VssPublicKey to participate in MPC.
-- Each stakeholder should create a Vss keypair, sign VSS public key with signing
-- key and send it into blockchain.
--
-- A public key of node is included in certificate in order to
-- enable validation of it using only node's P2PKH address.
-- Expiry epoch is last epoch when certificate is valid, expiry epoch is included
-- in certificate and signature.
--
-- Other nodes accept this certificate if it is valid and if node has
-- enough stake.
--
-- Invariant: 'checkSig vcSigningKey (vcVssKey, vcExpiryEpoch) vcSignature'.
data VssCertificate = VssCertificate
    { vcVssKey      :: !(AsBinary VssPublicKey)
    , vcExpiryEpoch :: !EpochIndex
    -- ^ Epoch up to which certificates is valid.
    , vcSignature   :: !(Signature (AsBinary VssPublicKey, EpochIndex))
    , vcSigningKey  :: !PublicKey
    } deriving (Show, Eq, Generic)
Field size Type Description
size(VssPublicKey) AsBinary VssPublicKey Public key of stakeholder which is allowed to participate in MPC
size(EpochIndex) EpochIndex Last epoch when certificate is valid
size(Signature) Signature (AsBinary VssPublicKey, EpochIndex) Signature
size(PublicKey) PublicKey Signing key

VssCertificatesMap

-- | VssCertificatesMap contains all valid certificates collected
-- during some period of time.
type VssCertificatesMap = HashMap StakeholderId VssCertificate
Field size Type Value Description
1-9 UVarInt Int n Number of certificates in map
n * (size(StakeholderId) + size(VssCertificate)) HashMap StakeholderId VssCertificate   Vss certificates map as list of pairs

GtProof

-- | Proof of MpcData.
-- We can use ADS for commitments, openings, shares as well,
-- if we find it necessary.
data GtProof
    = CommitmentsProof !(Hash CommitmentsMap) !(Hash VssCertificatesMap)
    | OpeningsProof !(Hash OpeningsMap) !(Hash VssCertificatesMap)
    | SharesProof !(Hash SharesMap) !(Hash VssCertificatesMap)
    | CertificatesProof !(Hash VssCertificatesMap)
    deriving (Show, Eq, Generic)
Tag size Tag Type Tag Value Description Field size Field Type
1 Word8 0x00 Tag for CommitmentsProof    
        32 Hash CommitmentsMap
        32 Hash VssCertificatesMap
    0x01 Tag for OpeningsProof    
        32 Hash OpeningsMap
        32 Hash VssCertificatesMap
    0x02 Tag for SharesProof    
        32 Hash SharesMap
        32 Hash VssCertificatesMap
    0x03 Tag for CertificatesProof    
        32 Hash VssCertificatesMap

Block Headers

BlockVersion

-- | Communication protocol version.
data BlockVersion = BlockVersion
    { bvMajor :: !Word16
    , bvMinor :: !Word16
    , bvAlt   :: !Word8
    } deriving (Eq, Generic, Ord, Typeable)
Field size Type Description
2 Word16 Major version
2 Word16 Minor version
1 Word8 Alt version from initial US spec

SoftwareVersion

newtype ApplicationName = ApplicationName
    { getApplicationName :: Text
    } deriving (Eq, Ord, Show, Generic, Typeable, ToString, Hashable, Buildable, ToJSON, FromJSON)

-- | Numeric software version associated with ApplicationName.
type NumSoftwareVersion = Word32

-- | Software version.
data SoftwareVersion = SoftwareVersion
    { svAppName :: !ApplicationName
    , svNumber  :: !NumSoftwareVersion
    } deriving (Eq, Generic, Ord, Typeable)
Field size Type Value Description
1 UVarInt Int n Length of application name (should be <= 10)
n Word8[n]   svAppName: UTF8 encoded application name
4 Word32   svNumber

MainBlockHeader

Field size Type Description
4 Word32 Protocol magic
32 HeaderHash Previous block hash
size(MainProof) MainProof Body proof
size(MainConsensusData) MainConsensusData Consensus data
size(MainExtraHeaderData) MainExtraHeaderData MainExtraHeaderData

MainProof

-- | Proof of transactions list and MPC data.
    data BodyProof (MainBlockchain ssc) = MainProof
        { mpNumber        :: !Word32
        , mpRoot          :: !(MerkleRoot Tx)
        , mpWitnessesHash :: !(Hash [TxWitness])
        , mpMpcProof      :: !(SscProof ssc)
        , mpProxySKsProof :: !(Hash [ProxySKSimple])
        , mpUpdateProof   :: !UpdateProof
        } deriving (Generic)
Field size Type Description
1-5 UVarInt Word32 mpNumber
size(MerkleRoot) MerkleRoot Tx mpRoot
32 Hash [TxWitness] mpWitnessesHash
size(GtProof) GtProof mpMpcProof
32 Hash [ProxySKSimple] mpProxySKsProof
32 UpdateProof mpUpdateProof

MainConsensusData

data ConsensusData (MainBlockchain ssc) = MainConsensusData
        { -- | Id of the slot for which this block was generated.
        _mcdSlot       :: !SlotId
        , -- | Public key of slot leader. Maybe later we'll see it is redundant.
        _mcdLeaderKey  :: !PublicKey
        , -- | Difficulty of chain ending in this block.
        _mcdDifficulty :: !ChainDifficulty
        , -- | Signature given by slot leader.
        _mcdSignature  :: !(BlockSignature ssc)
        } deriving (Generic, Show, Eq)
Field size Type Description
size(SlotId) SlotId mcdSlot
32 PublicKey mcdLeaderKey
1-10 ChainDifficulty mcdDifficulty
64 BlockSignature mcdSignature

MainExtraHeaderData

-- | Represents main block header extra data
data MainExtraHeaderData = MainExtraHeaderData
    { -- | Version of block.
      _mehBlockVersion    :: !BlockVersion
    , -- | Software version.
      _mehSoftwareVersion :: !SoftwareVersion
    , -- | Header attributes
      _mehAttributes      :: !BlockHeaderAttributes
    }
    deriving (Eq, Show, Generic)

-- | Represents main block header attributes: map from 1-byte integer to
-- arbitrary-type value. To be used for extending header with new
-- fields via softfork.
type BlockHeaderAttributes = Attributes ()
Field size Type Description
size(BlockVersion) BlockVersion Version of block
size(SoftwareVersion) SoftwareVersion Software version
size(BlockHeaderAttributes) BlockHeaderAttributes Header attributes (used for extending header with new fields via softfork)

GenesisBlockHeader

Field size Type Description
4 Word32 Protocol magic
32 HeaderHash Previous block hash
size(GenesisProof) GenesisProof Body proof
size(GenesisConsensusData) GenesisConsensusData Consensus data

GenesisProof

-- | Proof of GenesisBody is just a hash of slot leaders list.
    data BodyProof (GenesisBlockchain ssc) = GenesisProof
        !(Hash SlotLeaders)
        deriving (Eq, Generic, Show)
Field size Type Description
32 Hash SlotLeaders Hash of slot leaders list

GenesisConsensusData

data ConsensusData (GenesisBlockchain ssc) = GenesisConsensusData
        { -- | Index of the slot for which this genesis block is relevant.
          _gcdEpoch :: !EpochIndex
        , -- | Difficulty of the chain ending in this genesis block.
          _gcdDifficulty :: !ChainDifficulty
        } deriving (Generic, Show, Eq)
Field Type Description
1-10 EpochIndex Index of epoch for which this genesis block is relevant
1-10 ChainDifficulty Difficulty of the chain ending in this genesis block.

BlockHeader

-- | Either header of ordinary main block or genesis block.
type BlockHeader ssc = Either (GenesisBlockHeader ssc) (MainBlockHeader ssc)
Tag size Tag Type Tag Value Description Field size
1 Word8 0x00 Tag for GenesisBlockHeader  
        size(GenesisBlockHeader)
    0x01 Tag for MainBlockHeader  
        size(MainBlockHeader)

Block Exchange Messages

HeaderHash

-- | 'Hash' of block header. This should be @Hash (BlockHeader ssc)@
-- but there is no @ssc@ in HeaderHash type.
type HeaderHash = Hash BlockHeaderStub
data BlockHeaderStub

GetHeaders

-- | 'GetHeaders' message. Behaviour of the response depends on
-- particular combination of 'mghFrom' and 'mghTo'.
--
-- * 'mghTo' resolves to some header (let's call it @top@ for
-- convenience) -- node's tip if it's @Nothing@, header with hash in
-- @Just@ if it's @Just@.
--
-- * If 'mghFrom' is empty, then semantics is "request to return
-- header of block @top@".
--
-- * Otherwise (if 'mghFrom' isn't empty) it represents the set of
-- checkpoints. Responding node will try to iterate headers from @top@
-- to older until it reaches any checkpoint. If it finds checkpoint
-- @c@, it returns all headers in range @[c.next..top]@. If it doesn't
-- find any checkpoint or depth of searching exceeds
-- 'recoveryHeadersMessage', it will try to find the newest checkpoint
-- @cc@ from 'mghFrom' that's in main chain of responding node and
-- then return at most 'recoveryHeadersMessage' headers starting with
-- @cc@ as the oldest one, returning headers in range @l2 =
-- [cc.next..x]@ where @x@ is either @top@ (in case @length l2 <
-- recoveryHeadersMessage@) or some arbitrary header (and length is
-- precisely 'recoveryHeadersMessage').
data MsgGetHeaders = MsgGetHeaders
    { -- not guaranteed to be in any particular order
      mghFrom :: ![HeaderHash]
    , mghTo   :: !(Maybe HeaderHash)
    } deriving (Generic, Show, Eq)
Field size Type Value Description
1-9 UVarInt Int n Number of checkpoints
n * size(Hash) Hash[n]   List of length n with hashes
1 Word8 tag = 0x00 or 0x01 Tag for optional to hash
tag * size(Hash) Hash   If tag is not 0x00 then hash of to block

GetBlocks

-- | 'GetBlocks' message (see protocol specification).
data MsgGetBlocks = MsgGetBlocks
    { mgbFrom :: !HeaderHash
    , mgbTo   :: !HeaderHash
    } deriving (Generic, Show, Eq)
Field size Type Field
size(Hash) Hash mgbFrom
size(Hash) Hash mgbTo

Headers

-- | 'Headers' message (see protocol specification).
newtype MsgHeaders ssc =
    MsgHeaders (NewestFirst NE (BlockHeader ssc))
    deriving (Generic, Show, Eq)
Field size Type Value Description
1-9 UVarInt Int n Number of block headers
n * size(BlockHeader) BlockHeader[n]   n block headers

Block

-- | 'Block' message (see protocol specification).
newtype MsgBlock ssc =
    MsgBlock (Block ssc)
    deriving (Generic, Show)
Field size Type Value Description
1-9 UVarInt Int64 n Size of Block in bytes
size(Block) Block   Block with size of n bytes

Contains one Block. We encode block size and then the block itself so that we’d be able to reject the block if it’s of the wrong size without consuming the whole block.

Transaction sending

To send transaction you need to create and send TxAux data type to node. All data types required to successfully perform sending are described in this section.

Transaction input

-- | Represents transaction identifier as 'Hash' of 'Tx'.
type TxId = Hash Tx

-- | Transaction input.
data TxIn = TxIn
    { -- | Which transaction's output is used
      txInHash  :: !TxId
      -- | Index of the output in transaction's outputs
    , txInIndex :: !Word32
    } deriving (Eq, Ord, Show, Generic, Typeable)
Field size Type Field name
32 Hash txOutAddress
1-10 UVarInt Word32 txOutValue

Transaction output

-- | Transaction output.
data TxOut = TxOut
    { txOutAddress :: !Address
    , txOutValue   :: !Coin
    } deriving (Eq, Ord, Generic, Show, Typeable)
Field size Type Field name
size(Address) Address txOutAddress
size(Coin) Coin txOutValue

Example:

ghci> let addr = PubKeyAddress (abstractHash somePk) (Attributes (AddrPkAttrs Nothing) "a")
ghci> hexEncode addr
"001e380dea393a631ad563154a13bc5ee49fa4b62a60218358b5dcb875e00161cf52c5ec"
ghci> hexEncode $ TxOut addr (mkCoin 1000)
"001e380dea393a631ad563154a13bc5ee49fa4b62a60218358b5dcb875e00161cf52c5ec0064"

Transaction output auxilary

-- | Transaction output auxilary data
type TxOutAux = (TxOut, [(StakeholderId, Coin)])

Lets define distr_size(n) = n * (size(Hash) + size(Coin)).

Field size Type Value Description
size(TxOut) TxOut   Transaction output
1-9 UVarInt Int n Length of list for output auxilary data
distr_size(n) <Hash,Coin>[n]   Array of pairs for StakeholderId and Coin

Transaction signature data


-- | Data that is being signed when creating a TxSig.
type TxSigData = (TxId, Word32, Hash [TxOut], Hash TxDistribution)
Field size Type Description
32 Hash Signature of transaction
4 Word32 Index of the output in transaction’s outputs (see txInIndex in TxIn)
32 Hash Hash of list of transaction outputs
32 Hash Hash of transaction distribution

Transaction witness

-- | 'Signature' of addrId.
type TxSig = Signature TxSigData

-- | A witness for a single input.
data TxInWitness
    = PkWitness { twKey :: PublicKey
                , twSig :: TxSig}
    | ScriptWitness { twValidator :: Script
                    , twRedeemer  :: Script}
    deriving (Eq, Show, Generic, Typeable)

-- | A witness is a proof that a transaction is allowed to spend the funds it
-- spends (by providing signatures, redeeming scripts, etc). A separate proof
-- is provided for each input.
type TxWitness = Vector TxInWitness

Table for TxInWitness:

Tag size Tag Type Tag Value Description Field size Field Type Field name
1 Word8 0x00 Tag for PkWitness      
        32 PublicKey twKey
        64 TxSig twSig
    0x01 Tag for ScriptWitness      
        size(Script) Script twValidator
        size(Script) Script twRedeemer

Transaction

-- | Transaction.
--
-- NB: transaction witnesses are stored separately.
data Tx = Tx
    { txInputs     :: ![TxIn]   -- ^ Inputs of transaction.
    , txOutputs    :: ![TxOut]  -- ^ Outputs of transaction.
    , txAttributes :: !TxAttributes -- ^ Attributes of transaction
    } deriving (Eq, Ord, Generic, Show, Typeable)
Field size Type Value Description
1-9 UVarInt Int n Number of transaction inputs
n * size(TxIn) TxIn[n]   Array of transaction inputs
1-9 UVarInt Int m Number of transaction outputs
m * size(TxOut) TxOut[m]   Array of transaction outputs
size(TxAttributes) TxAttributes   Attributes for transaction

Transaction distribution

-- | Distribution of “fake” stake that follow-the-satoshi would use for a
-- particular transaction.
newtype TxDistribution = TxDistribution {
    getTxDistribution :: [[(StakeholderId, Coin)]] }
    deriving (Eq, Show, Generic, Typeable)

Though transaction distribution can be stored as list of list using previous serialization strategy it is often happens that we pass list of empty lists. In that case we store such lists more efficiently.

Tag size Tag Type Tag Value Description Field size Field Type Value
1 Word8 0x00 List of empty lists      
        1-9 UVarInt Int  
    0x01 Some lists are not empty      
        1-9 UVarInt Int n
        distr_size(n) <Hash,Coin>[n]  

Transaction auxilary

-- | Transaction + auxiliary data
type TxAux = (Tx, TxWitness, TxDistribution)
Field size Type Description
size(Tx) Tx Transaction itself
size(TxWitness) TxWitness Witness for transaction
size(TxDistribution) TxDistribution Transaction distribution

Delegation

Description of Delegation process can be found in CSL-application chapter. Here you can find description of message formats only.

Proxy Certificate

Similar to Signature.

-- | Proxy certificate, made of ω + public key of delegate.
newtype ProxyCert w = ProxyCert { unProxyCert :: Ed25519.Signature }
    deriving (Eq, Ord, Show, Generic, NFData, Hashable)
Field size Type Description
64 Word8[64] unProxyCert: 64 bytes of signature string

Example:

ghci> (issuerPk, issuerSk) <- keyGen
ghci> hexEncode issuerPk
"0659c8e27599dc4709dab3bb58ce50d0729150fc238010fd3a68dcf07c621bdc"
ghci> (delegatePk, delegateSk) <- keyGen
ghci> hexEncode delegatePk
"5eaf0944733da8386c427656a876b20ae411fa686ea4bb165b53a311c868c287"
ghci> let cert = createProxyCert issuerSk delegatePk (0, 10) :: ProxyCert (EpochIndex, EpochIndex)
ghci> hexEncode cert
"8db543c5fff7dd5dab609d04a834cda77958faf48cabee351def8985a2ec7dae71c7b2f0390caa54c61c9d41f5228e1a0b5da1c08638b99d03a1c02c81cb1607"
ghci> verifyProxyCert issuerPk delegatePk (0, 10) cert
True

Proxy Secret Key

-- | Convenient wrapper for secret key, that's basically ω + certificate.
data ProxySecretKey w = ProxySecretKey
    { pskOmega      :: w
    , pskIssuerPk   :: PublicKey
    , pskDelegatePk :: PublicKey
    , pskCert       :: ProxyCert w
    } deriving (Eq, Ord, Show, Generic)
Field size Type Description
size(w) w pskOmega
32 PublicKey pskIssuerPk
32 PublicKey pskDelegatePk
64 ProxyCert w pskCert

Proxy signature

-- | Delegate signature made with certificate-based permission. @a@
-- stays for message type used in proxy (ω in the implementation
-- notes).
data ProxySignature w a = ProxySignature
    { pdOmega      :: w
    , pdDelegatePk :: PublicKey
    , pdCert       :: ProxyCert w
    , pdSig        :: Ed25519.Signature
    } deriving (Eq, Ord, Show, Generic)
Field size Type Description
size(w) w pdOmega
32 PublicKey pdDelegatePk
64 ProxyCert w pdCert
64 Signature pdSig

Proxy Secret Key and Signature for Lightweight Delegation

Secret Key

-- | Same alias for the proxy secret key (see 'ProxySigLight').
type ProxySKLight = ProxySecretKey (EpochIndex, EpochIndex)
Field size Type Description
1-10 UVarInt Word64 from epoch
1-10 UVarInt Word64 to epoch
32 PublicKey pskIssuerPk
32 PublicKey pskDelegatePk
64 ProxyCert (EpochIndex, EpochIndex) pskCert

Example:

ghci> let proxySk = createProxySecretKey issuerSk delegatePk (0, 10) :: ProxySKLight
ghci> hexEncode proxySk
"000a0659c8e27599dc4709dab3bb58ce50d0729150fc238010fd3a68dcf07c621bdc5eaf0944733da8386
c427656a876b20ae411fa686ea4bb165b53a311c868c2878db543c5fff7dd5dab609d04a834cda77958faf
48cabee351def8985a2ec7dae71c7b2f0390caa54c61c9d41f5228e1a0b5da1c08638b99d03a1c02c81cb1607"
ghci> verifyProxySecretKey proxySk
True

Signature

-- | Proxy signature used in csl -- holds a pair of epoch
-- indices. Block is valid if it's epoch index is inside this range.
type ProxySigLight a = ProxySignature (EpochIndex, EpochIndex) a
Field size Type Description
1-10 UVarInt Word64 from epoch
1-10 UVarInt Word64 to epoch
32 PublicKey pdDelegatePk
64 ProxyCert (EpochIndex, EpochIndex) pdCert
64 Signature pdSig

Example:

ghci> let proxyLightSig = proxySign delegateSk proxySk proxySk :: ProxySigLight ProxySKLight
ghci> hexEncode proxyLightSig
"000a5eaf0944733da8386c427656a876b20ae411fa686ea4bb165b53a311c868c2878db543c5fff7dd5dab609d04a
834cda77958faf48cabee351def8985a2ec7dae71c7b2f0390caa54c61c9d41f5228e1a0b5da1c08638b99d03a1c02
c81cb1607e764468529599312ebe4dd5587383e5ccd3c2755401b22c8ff08827ecabd1afc8c634e17085ec83179193
afad2868e6aabce3e3e46e3170d077ee4e8613aa700"
ghci> proxyVerify issuerPk proxyLightSig (== (0, 10)) proxySk
True

Proxy Secret Key and Signature for Heavyweight Delegation

Secret Key

-- | Correspondent SK for no-ttl proxy signature scheme.
type ProxySKHeavy = ProxySecretKey EpochIndex
Field size Type Description
1-10 UVarInt Word64 epoch
32 PublicKey pskIssuerPk
32 PublicKey pskDelegatePk
64 ProxyCert EpochIndex pskCert

Signature

-- | Simple proxy signature without ttl/epoch index
-- constraints. 'EpochIndex' inside is needed for replay attack
-- prevention.
type ProxySigHeavy a = ProxySignature EpochIndex a
Field size Type Description
1-10 UVarInt Word64 epoch
32 PublicKey pdDelegatePk
64 ProxyCert EpochIndex pdCert
64 Signature pdSig

Proxy Delegation Start Message

-- | Message with delegated proxy secret key. Is used to propagate
-- both epoch-oriented psks (lightweight) and simple (heavyweight).
data SendProxySK
    = SendProxySKLight !ProxySKLight
    | SendProxySKHeavy !ProxySKHeavy
    deriving (Show, Eq, Generic)
Tag size Tag Type Tag Value Description Field size Description
1 Word8 0x00 Tag for SendProxySKLight    
        size(ProxySKLight) lightweight
    0x01 Tag for SendProxySKHeavy    
        size(ProxySKHeavy) heavyweight

Example:

ghci> hexEncode (SendProxySKLight proxySk)
"00000a0659c8e27599dc4709dab3bb58ce50d0729150fc238010fd3a68dcf07c621bdc5eaf0944733da
8386c427656a876b20ae411fa686ea4bb165b53a311c868c2878db543c5fff7dd5dab609d04a834cda77
958faf48cabee351def8985a2ec7dae71c7b2f0390caa54c61c9d41f5228e1a0b5da1c08638b99d03a1c02c81cb1607"

Lightweight Delegation Confirmation

ConfirmProxySK

-- | Confirmation of proxy signature delivery. Delegate should take
-- the proxy signing key he has and sign this key with itself. If the
-- signature is correct, then it was done by delegate (guaranteed by
-- PSK scheme). Checking @w@ can be done with @(const True)@
-- predicate, because certificate may be sent in epoch id that's
-- before lower cert's @EpochIndex@.
data ConfirmProxySK =
    ConfirmProxySK !ProxySKLight !(ProxySigLight ProxySKLight)
    deriving (Show, Eq, Generic)
Field size Description
size(ProxySKLight) certificate
size(ProxySigLight) proof for certificate

Update System

Update Vote

-- | ID of softwaree update proposal
type UpId = Hash UpdateProposal

-- | Vote for update proposal
data UpdateVote = UpdateVote
    { -- | Public key of stakeholder, who votes
      uvKey        :: !PublicKey
    , -- | Proposal to which this vote applies
      uvProposalId :: !UpId
    , -- | Approval/rejection bit
      uvDecision   :: !Bool
    , -- | Signature of (Update proposal, Approval/rejection bit)
      --   by stakeholder
      uvSignature  :: !(Signature (UpId, Bool))
    } deriving (Eq, Show, Generic, Typeable)
Field size Type Field
32 PublicKey uvKey
32 Hash uvProposalId
1 Bool uvDecision
64 Signature uvSignature

Vote Identifier

type VoteId = (UpId, PublicKey, Bool)
Field size Type Description
32 Hash hash of update proposal
32 PublicKey public key
1 Bool vote result

For more description of fields, see UpdateVote message description. VoteId is just (uvProposalId, uvKey, uvDecision).

Block Version Data

-- | Data which is associated with 'BlockVersion'.
data BlockVersionData = BlockVersionData
    { bvdScriptVersion     :: !ScriptVersion
    , bvdSlotDuration      :: !Millisecond
    , bvdMaxBlockSize      :: !Byte
    , bvdMaxTxSize         :: !Byte
    , bvdMpcThd            :: !CoinPortion
    , bvdHeavyDelThd       :: !CoinPortion
    , bvdUpdateVoteThd     :: !CoinPortion
    , bvdUpdateProposalThd :: !CoinPortion
    , bvdUpdateImplicit    :: !FlatSlotId
    , bvdUpdateSoftforkThd :: !CoinPortion
    } deriving (Show, Eq, Generic, Typeable)
Field size Type Field
1-3 UVarInt Word16 bvdScriptVersion
size(Integer) Integer bvdSlotDuration
4 Int32 bvdMaxBlockSize
4 Int32 bvdMaxTxSize
8 Word64 bvdMpcThd
8 Word64 bvdHeavyDelThd
8 Word64 bvdUpdateVoteThd
8 Word64 bvdUpdateProposalThd
8 Word64 bvdUpdateImplicit
8 Word64 bvdUpdateSoftforkThd

Update Data

-- | Data which describes update. It is specific for each system.
data UpdateData = UpdateData
    { udAppDiffHash  :: !(Hash Raw)
    -- ^ Hash of binary diff between two applications. This diff can
    -- be passed to updater to create new application.
    , udPkgHash      :: !(Hash Raw)
    -- ^ Hash of package to install new application. This package can
    -- be used to install new application from scratch instead of
    -- updating existing application.
    , udUpdaterHash  :: !(Hash Raw)
    -- ^ Hash if update application which can be used to install this
    -- update (relevant only when updater is used, not package).
    , udMetadataHash :: !(Hash Raw)
    -- ^ Hash of metadata relevant to this update.  It is raw hash,
    -- because metadata can include image or something
    -- (maybe). Anyway, we can always use `unsafeHash`.
    } deriving (Eq, Show, Generic, Typeable)
Field size Type Field
32 Hash udAppDiffHash
32 Hash udPkgHash
32 Hash udUpdaterHash
32 Hash udMetadataHash

System Tag

-- | Tag of system for which update data is purposed, e.g. win64, mac32
newtype SystemTag = SystemTag { getSystemTag :: Text }
  deriving (Eq, Ord, Show, Generic, Buildable, Hashable, Lift, Typeable)

SystemTag is encoded as ByteString in UTF-8 encoding.

Field size Type Value Field
1-9 UVarInt Int64 n Size of text in bytes
n Word8[n]   n bytes of UTF-8 encoded text

Update Proposal

type UpAttributes = Attributes ()

-- | Proposal for software update
data UpdateProposal = UpdateProposal
    { upBlockVersion     :: !BlockVersion
    , upBlockVersionData :: !BlockVersionData
    , upSoftwareVersion  :: !SoftwareVersion
    , upData             :: !(HM.HashMap SystemTag UpdateData)
    -- ^ UpdateData for each system which this update affects.
    -- It must be non-empty.
    , upAttributes       :: !UpAttributes
    -- ^ Attributes which are currently empty, but provide
    -- extensibility.
    } deriving (Eq, Show, Generic, Typeable)
Field size Type Value Field
5 BlockVersion   upBlockVersion
size(BlockVersionData) BlockVersionData   upBlockVersionData
size(SoftwareVersion) SoftwareVersion   upSoftwareVersion
1-9 UVarInt Int n  
n * (size(SystemTag) + size(UpdateData)) <SystemTag, UpdateData>[n]   upData
size(Attributes ()) Attributes ()   upAttributes