Binary Protocols

IMPORTANT: BINARY PROTOCOLS WILL BE CHANGED SOON.

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, Functor)

Source code link.

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^[email protected] 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. @[email protected] can be encoded in only one way instead of
--   two).
newtype TinyVarInt = TinyVarInt {getTinyVarInt :: Word16}
    deriving (Eq, Ord, Show, Generic, NFData)

Source code link.

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"

ByteStrings in cardano are encoded as a sequence of bytes preceded by its length, where the length is encoded as a variable-length integer. If length is encoded as TinyVarInt then such ByteString is denoted as TinyBS.

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

Source code link.

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, Generic, Hashable, Data, NFData)

Source code link.

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

Source code link.

Field size Type Description
32 Word8[32] 256 bits of hash digest

Message size limit: 32.

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 'CC.XPub'.
newtype PublicKey = PublicKey CC.XPub
    deriving (Eq, Ord, Show, Generic, NFData, Hashable, Typeable)

data XPub = XPub !Edwards25519.PointCompressed !ChainCode
    deriving (Eq, Ord, Show, Generic)

Source code link.

Field size Type Description
32 ByteString 32-byte PointCompressed
32 ByteString 32-byte ChainCode

Message size limit: 64.

Signature

-- | Wrapper around 'CC.XSignature'.
newtype Signature a = Signature CC.XSignature
    deriving (Eq, Ord, Show, Generic, NFData, Hashable, Typeable)

Source code link.

Field size Type Description
64 Word8[64] 64 bytes of signature string

Message size limit: 64.

Epoch Index

-- | Index of epoch.
newtype EpochIndex = EpochIndex
    { getEpochIndex :: Word64
    } deriving (Show, Eq, Ord, Num, Enum, Ix, Integral, Real, Generic, Hashable, Bounded, Typeable, NFData)

Source code link.

Field size Type Description
1-10 UVarInt Word64 epoch index

Message size limit: 10.

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, Ix, Generic, Hashable, Buildable, Typeable, NFData)

Source code link.

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)

Source code link.

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
      -- | Remaining, unparsed fields
    , attrRemain :: UnparsedFields
    }
  deriving (Eq, Ord, Generic, Typeable)

Source code link

-- | Representation of unparsed fields in Attributes. Newtype wrapper is used
-- for clear backward compatibility between previous representation (which was
-- just a single ByteString) during transition from Store to CBOR.
newtype UnparsedFields = UnparsedFields (Map Word8 ByteString)
    deriving (Eq, Ord, Show, Generic, Typeable, NFData)

Source code link

Message size limit: each type with Attributes defines its own limit for them.

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)

Source code link.

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

-- | Additional information stored along with address. It's intended
-- to be put into 'Attributes' data type to make it extensible with
-- softfork.
data AddrAttributes = AddrAttributes
    { aaPkDerivationPath  :: !(Maybe HDAddressPayload)
    , aaStakeDistribution :: !AddrStakeDistribution
    } deriving (Eq, Ord, Show, Generic, Typeable)

Source code link

Address attributes hold additional information about address:

  • encrypted derivation path from the root of HD wallet tree (aaPkDerivationPath)
  • stake distribution associated with this address (aaStakeDistribution)

Note that address may not contain an encrypted derivation path. Addresses without derivation path included in the attributes are usually derived from level 0 (wallet) public keys and used as IDs for wallets. Such addresses are not meant to contain any money.

Address Payload

data HDAddressPayload = HDAddressPayload !ByteString
    deriving (Eq, Ord, Show, Generic)

Source code link

HDAddressPayload datatype represents a derivation path ([Word32]), symmetrically encrypted via ChaChaPoly1305 algorithm. An encryption key may be any ByteString of 32 bytes, known only to owner of the address.

In current Cardano implementation, encryption key is a 32-byte key derived from root public key of wallet using PBKDF2 derivation algorithm with HMAC-SHA512 as pseudorandom function, 500 iterations and string address-hashing as a salt. Source code link

Address attributes are encrypted with crypto tag, thus allowing to check whether or not attributes are decrypted successfully. Consequently, one can determine whether or not given address belongs to a given wallet by trying to decrypt this address’ attributes using encryption key derived from wallet’s root public key. Source code link to helper functions which implement attributes decryption and encryption.

Address Stake Distribution

-- | Stake distribution associated with an address.
data AddrStakeDistribution
    = BootstrapEraDistr
    -- ^ Stake distribution for bootstrap era.
    | SingleKeyDistr !StakeholderId
    -- ^ Stake distribution stating that all stake should go to the given stakeholder.
    | UnsafeMultiKeyDistr !(Map StakeholderId CoinPortion)
    -- ^ Stake distribution which gives stake to multiple
    -- stakeholders. 'CoinPortion' is a portion of an output (output
    -- has a value, portion of this value is stake). The constructor
    -- is unsafe because there are some predicates which must hold:
    --
    -- • the sum of portions must be @[email protected] (basically 1);
    -- • all portions must be positive;
    -- • there must be at least 2 items, because if there is only one item,
    -- 'SingleKeyDistr' can be used instead (which is smaller).
    deriving (Eq, Ord, Show, Generic, Typeable)

Source code link

In Byron era, all addresses should use BootstrapEraDistr as stake distribution – which means, all the stake is automatically delegated to bootstrap stakeholders.

In Shelley era, addresses will use SingleKeyDistr or UnsafeMultiKeyDistr to delegate stake associated with address’ balance to their own stakeholder IDs.

Address

-- | Hash used to identify address.
type AddressHash = AbstractHash Blake2b_224

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

Source code link

Cardano addresses use blake2b-224 for hashes included in address structure.

-- | Data which is bound to an address and must be revealed in order
-- to spend coins belonging to this address.
data AddrSpendingData
    = PubKeyASD !PublicKey
    -- ^ Funds can be spent by revealing a 'PublicKey' and providing a
    -- valid signature.
    | ScriptASD !Script
    -- ^ Funds can be spent by revealing a 'Script' and providing a
    -- redeemer 'Script'.
    | RedeemASD !RedeemPublicKey
    -- ^ Funds can be spent by revealing a 'RedeemScript' and providing a
    -- valid signature.
    | UnknownASD !Word8 !ByteString
    -- ^ Unknown type of spending data. It consists of a tag and
    -- arbitrary 'ByteString'. It allows us to introduce a new type of
    -- spending data via softfork.
    deriving (Eq, Generic, Typeable, Show)

-- | Type of an address. It corresponds to constructors of
-- 'AddrSpendingData'. It's separated, because 'Address' doesn't store
-- 'AddrSpendingData', but we want to know its type.
data AddrType
    = ATPubKey
    | ATScript
    | ATRedeem
    | ATUnknown !Word8
    deriving (Eq, Ord, Generic, Typeable, Show)

Source code link

Cardano supports different address types. Address types differ in how funds belonging to the address can be spent. Each address type have corresponding type of spending data. Spending data is a part of data which is hashed in order to make the address. Spending data, together with verification data (e.g. a signature) should be provided in TxInWitness in order to spend funds from address.

Currently Cardano supports 3 address types:

  • Public key address (ATPublicKey): extended Ed25519 public key as spending data, corresponding Ed25519 signature as verification data.
  • Script address (ATScript): Plutus validator script as spending data, Plutus redeemer script as verification data.
  • Redeem address (ATRedeem): plain Ed25519 public key as spending data, Ed25519 signature as verification data. Used only in genesis to provide pre-sold ADA to holders.

Also there is special ATUnknown address type added for forward compatibility.

-- | Hash of this data is stored in 'Address'. This type exists mostly
-- for internal usage.
newtype Address' = Address'
    { unAddress' :: (AddrType, AddrSpendingData, Attributes AddrAttributes)
    } deriving (Eq, Show, Generic, Typeable)

Source code link

Internal type Address' denotes a tuple, hash of which is stored as a part of the Address. Not only AddrSpendingData is hashed, but also AddrType and AddrAttributes.

-- | 'Address' is where you can send coins.
data Address = Address
    { addrRoot       :: !(AddressHash Address')
    -- ^ Root of imaginary pseudo Merkle tree stored in this address.
    , addrAttributes :: !(Attributes AddrAttributes)
    -- ^ Attributes associated with this address.
    , addrType       :: !AddrType
    -- ^ The type of this address. Should correspond to
    -- 'AddrSpendingData', but it can't be checked statically, because
    -- spending data is hashed.
    } deriving (Eq, Ord, Generic, Typeable, Show)

Source code link

An Address datatype consists of blake2b-224 hash of Address' tuple, type tag and attributes.

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, NFData)

Source code link.

Field size Type Description
size(Hash) Hash Root hash of Merkle tree

Chain Difficulty

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

Source code link.

Size Type Description
1-10 UVarInt Word64 Chain difficulty

SlotLeaders

-- | 'NonEmpty' list of slot leaders.
type SlotLeaders = NonEmpty StakeholderId

Source code link.

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)

Source code link.

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)

Source code link.

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)

Source code link. Scalar source code link.

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 :: !DLEQ.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)

Source code link.

Field size Type Description
size(Point) Point Extra generator

Message size limit: 33.

Commitment

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

Source code link.

Field size Type Description
size(Point) Point Commitment

PublicKey

-- | Public Key
newtype PublicKey = PublicKey Point
    deriving (Show,Eq,NFData,Binary)

Source code link.

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

Message size limits:

  • Share - 101.
  • EncShare - 101.
  • SecretProof - 64.
  • VssPublicKey - 33.

Source code link.

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) (NonEmpty (AsBinary EncShare)))
    } deriving (Show, Eq, Generic)
Field size Type Value Description
1-9 UVarInt Int n  
n * (sizeof(VssPublicKey) + sizeof(EncShare)) HashMapCommShares   commShares
sizeof(SecretSharingExtra) AsBinary SecretSharingExtra   commExtra
sizeof(SecretProof) AsBinary SecretProof   commProof

where HashMapCommShares = HashMap (AsBinary VssPublicKey) (NonEmpty (AsBinary EncShare))

Message size limit: 33.

CommitmentSignature

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

Source code link.

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)

Source code link.

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, NFData)
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, NFData)

Source code link.

Field size Type Description
size(Secret) AsBinary Secret Revealed secret

Message size limit: 33.

OpeningsMap

type OpeningsMap = HashMap StakeholderId Opening

Source code link.

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)

Source code link.

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

Message size limit: 171.

VssCertificatesMap

-- | VssCertificatesMap contains all valid certificates collected
-- during some period of time.
type VssCertificatesMap = HashMap StakeholderId VssCertificate

Source code link.

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

TxProof

data TxProof = TxProof
    { txpNumber            :: !Word32
    , txpRoot              :: !(MerkleRoot Tx)
    , txpWitnessesHash     :: !(Hash [TxWitness])
    , txpDistributionsHash :: !(Hash [TxDistribution])
    } deriving (Show, Eq, Generic)

Source code link.

Field size Type Description
4 Word32 Tx proof number
size(MerkleRoot) MerkleRoot Tx MerkleRoot
size(Hash) Hash Hash of the list of TxWitness
size(Hash) Hash Hash of the list of TxDistribution

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)

Source code link.

Tag size Tag Type Tag Value Description Field size Field Type
1 Word8 0x00 Tag for CommitmentsProof    
        size(Hash) Hash
        size(Hash) Hash
    0x01 Tag for OpeningsProof    
        size(Hash) Hash
        size(Hash) Hash
    0x02 Tag for SharesProof    
        size(Hash) Hash
        size(Hash) Hash
    0x03 Tag for CertificatesProof    
        size(Hash) Hash

Block Headers

BlockVersion

-- | Communication protocol version.
data BlockVersion = BlockVersion
    { bvMajor :: !Word16
    , bvMinor :: !Word16
    , bvAlt   :: !Word8
    } deriving (Eq, Generic, Ord, Typeable)

Source code link.

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, NFData)

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

-- | Software version.
data SoftwareVersion = SoftwareVersion
    { svAppName :: !ApplicationName
    , svNumber  :: !NumSoftwareVersion
    } deriving (Eq, Generic, Ord, Typeable)

Source code link.

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
size(HeaderHash) HeaderHash Previous block hash
size(MainProof) MainProof Body proof
size(MainConsensusData) MainConsensusData Consensus data
size(MainExtraHeaderData) MainExtraHeaderData MainExtraHeaderData

MainProof

type SscProof SscGodTossing = GtProof

-- | Proof that body of update message contains 'UpdatePayload'.
type UpdateProof = Hash UpdatePayload

-- | Proof of everything contained in the payload.
data BodyProof (MainBlockchain ssc) = MainProof
    { mpTxProof       :: !TxProof
    , mpMpcProof      :: !(SscProof ssc)
    , mpProxySKsProof :: !(Hash DlgPayload)
    , mpUpdateProof   :: !UpdateProof
    } deriving (Generic)

Source code link.

Field size Type Description
size(TxProof) TxProof mpTxProof
size(GtProof) SscProof ssc mpMpcProof
size(Hash) Hash mpProxySKsProof
size(Hash) Hash mpUpdateProof

MainConsensusData

data ConsensusData (MainBlockchain ssc) = MainConsensusData
    { -- | Id of the slot for which this block was generated.
      _mcdSlot       :: !SlotId
    , -- | Public key of the slot leader. It's essential to have it here,
      -- because FTS gives us only hash of public key (aka 'StakeholderId').
      _mcdLeaderKey  :: !PublicKey
    , -- | Difficulty of chain ending in this block.
      _mcdDifficulty :: !ChainDifficulty
    , -- | Signature given by slot leader.
      _mcdSignature  :: !(BlockSignature ssc)
    } deriving (Generic, Show, Eq)

Source code link.

Field size Type Description
size(SlotId) SlotId mcdSlot
size(PublicKey) PublicKey mcdLeaderKey
size(ChainDifficulty) ChainDifficulty mcdDifficulty
64 BlockSignature mcdSignature

MainExtraHeaderData

-- | 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 ()

-- | 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)

Source code link.

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

-- | Header of block contains some kind of summary. There are various
-- benefits which people get by separating header from other data.
--
-- The constructor has `Unsafe' prefix in its name, because there in
-- general there may be some invariants which must hold for the
-- contents of header.
data GenericBlockHeader b = UnsafeGenericBlockHeader
    { -- | Pointer to the header of the previous block.
      _gbhPrevBlock :: !(BHeaderHash b)
    , -- | Proof of body.
      _gbhBodyProof :: !(BodyProof b)
    , -- | Consensus data to verify consensus algorithm.
      _gbhConsensus :: !(ConsensusData b)
    , -- | Any extra data.
      _gbhExtra     :: !(ExtraHeaderData b)
    } deriving (Generic)

-- | Header of Genesis block.
type GenesisBlockHeader ssc = GenericBlockHeader (GenesisBlockchain ssc)

Source code link.

Field size Type Description
4 BHeaderHash b Pointer to the header of the previous block
32 BodyProof b Proof of body
size(GenesisProof) ConsensusData b Consensus data to verify consensus algorithm
size(GenesisConsensusData) ExtraHeaderData b Any extra data

where b = GenesisBlockchain ssc.

GenesisProof

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

Source code link.

Field size Type Description
size(Hash) Hash 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)

Source code link.

Field size Type Description
size(EpochIndex) EpochIndex Index of epoch for which this genesis block is relevant
size(ChainDifficulty) 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)

Source code link.

Tag size Tag Type Tag Value Description Field size
1 Word8 0x00 Tag for GenesisBlockHeader  
        size(GenesisBlockHeader)
    0x01 Tag for MainBlockHeader  
        size(MainBlockHeader)

Block

-- | Either genesis block or main block.
type Block ssc = Either (GenesisBlock ssc) (MainBlock ssc)

Source code link.

Tag size Tag Type Tag Value Description Field size
1 Word8 0x00 Tag for GenesisBlock  
        size(GenesisBlock)
    0x01 Tag for MainBlock  
        size(MainBlock)

Block Exchange Messages

HeaderHash

-- | 'Hash' of block header. This should be @Hash (BlockHeader ssc)@
-- but we don't want to have @[email protected] in 'HeaderHash' type.
type HeaderHash = Hash BlockHeaderStub
data BlockHeaderStub

Source code link.

GetHeaders

-- | 'GetHeaders' message. Behaviour of the response depends on
-- particular combination of 'mghFrom' and 'mghTo'.
--
-- * 'mghTo' resolves to some header (let's call it @[email protected] for
-- convenience) -- node's tip if it's @[email protected], header with hash in
-- @[email protected] if it's @[email protected]
--
-- * If 'mghFrom' is empty, then semantics is "request to return
-- header of block @[email protected]".
--
-- * Otherwise (if 'mghFrom' isn't empty) it represents the set of
-- checkpoints. Responding node will try to iterate headers from @[email protected]
-- to older until it reaches any checkpoint. If it finds checkpoint
-- @[email protected], 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
-- @[email protected] from 'mghFrom' that's in main chain of responding node and
-- then return at most 'recoveryHeadersMessage' headers starting with
-- @[email protected] as the oldest one, returning headers in range @l2 =
-- [cc.next..x]@ where @[email protected] is either @[email protected] (in case @length l2 <
-- [email protected]) 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)

Source code link.

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)

Source code link.

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)

Source code link.

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)

Source code link.

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)

Source code link.

Field size Type Field name
size(Hash) Hash txInHash
4 Word32 txInIndex

Transaction output

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

Source code link.

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 distribution

type TxOutDistribution = [(StakeholderId, Coin)]

Source code link.

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

Field size Type Description
distr_size(n) <Hash,Coin>[n] Array of pairs for StakeholderId and Coin

Transaction output auxilary

-- | Transaction output and auxilary data corresponding to it.
data TxOutAux = TxOutAux
    { toaOut   :: !TxOut             -- ^ Tx output
    , toaDistr :: !TxOutDistribution -- ^ Stake distribution
                                     -- associated with output
    } deriving (Show, Eq)

Source code link.

Field size Type Field name
size(TxOut) TxOut toaOut
size(TxOutDistribution) TxOutDistribution toaDistr

Transaction signature data

-- | Data that is being signed when creating a TxSig.
data TxSigData = TxSigData
    { -- | Input that we're signing (i.e. our signature certifies that we own
      -- funds referenced by this input)
      txSigInput     :: !TxIn
      -- | Outputs of the transaction (i.e. our signature certifies that we
      -- actually want the funds to go to these particular outputs)
    , txSigOutsHash  :: !(Hash (NonEmpty TxOut))
      -- | Distribution of the transaction
    , txSigDistrHash :: !(Hash TxDistribution)
    }
    deriving (Eq, Show, Generic, Typeable)

Source code link.

Field size Type Description
size(TxIn) TxIn txSigInput
size(Hash) Hash txSigOutsHash
size(Hash) Hash txSigDistrHash

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 }
    | RedeemWitness { twRedeemKey :: !RedeemPublicKey
                    , twRedeemSig :: !(RedeemSignature TxSigData) }
    | UnknownWitnessType !Word8 !ByteString
    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

Source code link.

Tag size Tag Type Tag Value Description Field size Field Type Field name
1 Word8 0x00 Tag for PkWitness      
        size(PublicKey) PublicKey twKey
        64 TxSig twSig
    0x01 Tag for ScriptWitness      
        size(Script) Script twValidator
        size(Script) Script twRedeemer
    0x02 Tag for RedeemWitness      
        size(RedeemPublicKey) RedeemPublicKey twRedeemKey
        size(RedeemSignature) RedeemSignature TxSigData twRedeemSig
    0x03 Tag for UnknownWitnessType      
        1 Word8  
          ByteString  

Transaction

-- | Transaction.
data Tx = UnsafeTx
    { _txInputs     :: !(NonEmpty TxIn)  -- ^ Inputs of transaction.
    , _txOutputs    :: !(NonEmpty TxOut) -- ^ Outputs of transaction.
    , _txAttributes :: !TxAttributes     -- ^ Attributes of transaction
    } deriving (Eq, Ord, Generic, Show, Typeable)

Source code link.

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 of transaction

Transaction distribution

-- | Distribution of “fake” stake that follow-the-satoshi would use
-- for a particular transaction.  Length of stored list must be same
-- as length of '_txOutputs' of corresponding transaction.
newtype TxDistribution = TxDistribution
    { getTxDistribution :: NonEmpty TxOutDistribution
    } deriving (Eq, Show, Generic, Typeable)

Source code link.

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
data TxAux = TxAux
    { taTx           :: !Tx
    , taWitness      :: !TxWitness
    , taDistribution :: !TxDistribution
    } deriving (Generic, Show, Eq)

Source code link.

Field size Type Description
size(Tx) Tx Transaction itself
size(TxWitness) TxWitness Witness for transaction
size(TxDistribution) TxDistribution Transaction distribution

Delegation

Please read about Delegation Messages for mechanism explanation. Here you can find description of messages format only.

Proxy Certificate

Similar to Signature.

-- | Proxy certificate, made of ω + public key of delegate.
newtype ProxyCert w = ProxyCert { unProxyCert :: CC.XSignature }
    deriving (Eq, Ord, Show, Generic, NFData, Hashable)

Source code link.

Field size Type Description
64 Word8[64] unProxyCert: 64 bytes of signature string

Message size limit: 64.

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)

Source code link.

Field size Type Description
size(w) w pskOmega
size(PublicKey) PublicKey pskIssuerPk
size(PublicKey) PublicKey pskDelegatePk
size(ProxyCert) ProxyCert w pskCert

Proxy signature

-- | Delegate signature made with certificate-based permission. @[email protected]
-- stays for message type used in proxy (ω in the implementation
-- notes), @[email protected] for type of message signed.
data ProxySignature w a = ProxySignature
    { psigPsk :: ProxySecretKey w
    , psigSig :: CC.XSignature
    } deriving (Eq, Ord, Show, Generic)

Source code link.

Field size Type Description
size(PproxySK) ProxySecretKey psigPsk
64 XSignature psigSig

Proxy Secret Key and Signature for Lightweight Delegation

WARNING: Currently, lightweight delegation is disabled and will be reworked in Shelley release, so information below can be outdated.

Secret Key

-- | Same alias for the proxy secret key (see 'ProxySigLight').
type ProxySKLight = ProxySecretKey (EpochIndex, EpochIndex)

Source code link.

Field size Type Description
1-10 UVarInt Word64 from epoch
1-10 UVarInt Word64 to epoch
size(PublicKey) PublicKey pskIssuerPk
size(PublicKey) 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 its epoch index is inside this range.
type ProxySigLight a = ProxySignature (EpochIndex, EpochIndex) a

Source code link.

Field size Type Description
1-10 UVarInt Word64 from epoch
1-10 UVarInt Word64 to epoch
size(PublicKey) 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

Source code link.

Field size Type Description
1-10 UVarInt Word64 epoch
size(PublicKey) PublicKey pskIssuerPk
size(PublicKey) 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

Source code link.

Field size Type Description
1-10 UVarInt Word64 epoch
size(PublicKey) PublicKey pdDelegatePk
64 ProxyCert EpochIndex pdCert
64 Signature pdSig

Lightweight Delegation Confirmation

WARNING: Currently, lightweight delegation is disabled and will be reworked in Shelley release, so information below can be outdated.

ProxySKLightConfirmation

-- | Confirmation of light cert type.
type ProxySKLightConfirmation = (ProxySKLight, ProxySigLight ProxySKLight)
Field size Description
size(ProxySKLight) Certificate
size(ProxySigLight) Proof for certificate

Update System

Update Vote

-- | ID of software 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)

Source code link.

Field size Type Field
size(PublicKey) PublicKey uvKey
size(Hash) Hash uvProposalId
1 Bool uvDecision
64 Signature uvSignature

Vote Identifier

type VoteId = (UpId, PublicKey, Bool)

Source code link.

Field size Type Description
size(Hash) Hash Hash of update proposal
size(PublicKey) 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
    , bvdMaxHeaderSize     :: !Byte
    , bvdMaxTxSize         :: !Byte
    , bvdMaxProposalSize   :: !Byte
    , bvdMpcThd            :: !CoinPortion
    , bvdHeavyDelThd       :: !CoinPortion
    , bvdUpdateVoteThd     :: !CoinPortion
    , bvdUpdateProposalThd :: !CoinPortion
    , bvdUpdateImplicit    :: !FlatSlotId
    , bvdUpdateSoftforkThd :: !CoinPortion
    } deriving (Show, Eq, Generic, Typeable)

Source code link.

Field size Type Field
1-3 UVarInt Word16 bvdScriptVersion
size(Integer) Integer bvdSlotDuration
size(Integer) Integer bvdMaxBlockSize
size(Integer) Integer bvdMaxHeaderSize
size(Integer) Integer bvdMaxTxSize
size(Integer) Integer bvdMaxProposalSize
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)

Source code link.

Field size Type Field
size(Hash) Hash udAppDiffHash
size(Hash) Hash udPkgHash
size(Hash) Hash udUpdaterHash
size(Hash) 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)

Source code link.

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 = UnsafeUpdateProposal
    { 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.
    , upFrom             :: !PublicKey
    -- ^ Who proposed this UP.
    , upSignature        :: !(Signature UpdateProposalToSign)
    } 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
size(PublicKey) PublicKey   upFrom
size(Signature) Signature UpdateProposalToSign   upSignature

Peer Data

HandlerSpec

data HandlerSpec
    = ConvHandler { hsReplyType :: MessageName}
    | UnknownHandler Word8 ByteString
    deriving (Show, Generic, Eq)

Source code link.

Type Size Value Following data
ConvHandler m where m : UnsignedVarInt < 64 Word8 01xx xxxx none
ConvHandler m where m : Unknown Word8 + TinyBS 0000 0001 ByteString
UnknownHandler w8 bs Word8 + TinyBS w8 bs

Examples:

ghci> hexEncode (ConvHandler (messageName (Proxy :: Proxy MsgGetHeaders)))
"44"
ghci> hexEncode (UnknownHandler 10 "aba")
"0a03616261"

VerInfo

type HandlerSpecs = HashMap MessageName HandlerSpec

newtype InSpecs = InSpecs HandlerSpecs
    deriving (Eq, Show, Generic)

newtype OutSpecs = OutSpecs HandlerSpecs
    deriving (Eq, Show, Generic)

data VerInfo = VerInfo
    { vIMagic        :: Int32
    , vIBlockVersion :: BlockVersion
    , vIInHandlers   :: HandlerSpecs
    , vIOutHandlers  :: HandlerSpecs
    } deriving (Eq, Generic, Show)

Source code link.

Field size Type Field
4 Int32 vIMagic
5 BlockVersion vIBlockVersion
size(HandlerSpecs) HandlerSpecs vIInHandlers
size(HandlerSpecs) HandlerSpecs vIOutHandlers

HandlerSpec is just mapping between message names and how those messages handled: via single-message style or conversation style + message tag. This mapping is encoded as every other Map — list of pairs.

InSpecs and OutSpecs are just wrappers around HandlerSpecs. These wrappers are used only for type-safety to distinguish spec for incoming and outgoing messages.

PeerData

type PeerData = VerInfo

Source code link.

Field size Type Description
size(VerInfo) VerInfo MessageName table

VerInfo we sending is created here.

Examples:

ghci> let ourVerInfo = VerInfo protocolMagic lastKnownBlockVersion mempty outSpecs
ghci> let peerIdExample = PeerId "0123456789ABCD"
ghci> hexEncode ourVerInfo
"0000000000000000000004030800000103020900030801010103020901030802
020103020902030803030103020903"
ghci> hexEncode ((peerIdExample, ourVerInfo) :: PeerData)
"3031323334353637383941424344000000000000000000000403080000010302
0900030801010103020901030802020103020902030803030103020903"