二进制协议

重要提示:二进制协议即将更改。

所有字段的大小均以字节表示。总是使用大端序。符合类型按定义顺序进行序列化,不带分隔符。

例如,(Word32, Word8) 序列化为5个字节:4个 Word32,1个 Word8

对于依赖于类型 T 的对象的变长结构,我们使用 size(T) 符号。

Word32 是32位无符号整数(uint32)。

为测试 ghcimyObject 对象的序列,应该在卡尔达诺结算层根目录中使用以下命令:

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

常见的 HASKELL 数据类型

Maybe

data Maybe a = Nothing | Just a

Maybe aa 类型或空类型(又名 null, None)。为了区分这两个值,我们在数据之前加了1个字节的标签。

标签大小 标签类型 标签值 描述 字段大小 描述
1 Word8 0x00 Tag for Nothing    
    0x01 Tag for Just    
        size(a) Value of type a

例子:

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 表示类型 a 或 类型 b。为了区分这两个值,我们在数据之前添加了1个字节的标签。

标签大小 标签类型 标签值 描述 字段大小 描述
1 Word8 0x00 Tag for Left    
        size(a) Value of type a
    0x01 Tag for Right    
        size(b) Value of type b

例子:

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

大整数

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

整数的编码有两种:如果它们适合 SmallInt,则它们被写为一个字节标记,并写入该值。如果该整数太大不能放入 SmallInt 中,将其与符号,长度字段写入字节数组。

有关参考,请参考实现

例子:

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

无符号变量长度整数

这种类型稍后将引用为 UVarInt Word16UVarInt Word64 来描述最大可用值。

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

源代码链接

值一次编码为7位,最重要的是是个连续位。因此,从0到127的数字只需要一个字节来编码,从128到16383的数字需要两个字节等等。

这种格式取自 Google 的 Protocol Buffers,但它提供了更多的编码语言(TODO)

例子:

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

微小的可变长度整数

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

源代码链接

字段大小 类型 描述
1-2 UVarInt Word16 Variable length integer up to 2^14 - 1

例子:

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

cardano 中的 ByteString 被编码为以其长度开头的字节序列,其中长度被编码为可变长度整数。如果长度被编码为 TinyVarInt,那么这样的 ByteString 被表示为 TinyBS

列表,非空值和向量空间

有时候我们在数据类型中存储一些对象的列表。你会看到他们被引用为 Vector a[a]。您可以将其读作类型对象的数组 a。这两种标准的 Haskell 数据类型都以相同的方式序列化。如果您看到 NonEmpty a 的类型的,您应该将它视作 [a],但该列表的大小至少为 1

字段大小 类型 描述
1-9 UVarInt Int n Size of array
n * size(a) a[n]   Array with length n of objects of type a

例子:

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 是键到值的映射。在序列化中,HashMap 被表示为 keyvalue 对的列表,并且被序列化为 [(key, value)]

字段大小 类型 描述
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)

例子:

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

网络

MessageName

newtype MessageName = MessageName BS.ByteString

源代码链接

一般来说,我们用一个或两个编码的 UnsignedVarInt 来表示 MessageName。在序列化状态下,它被编码为二进制字符串,因此以字符串的长度为前缀。

例子:

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

卡尔达诺结算层基本数据类型

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

源代码链接

硬币总数量是 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

币被分成百万份,剩余的币用于序列化。

1000999 coin = 1.000999 mega coin

简单的变量以 Word64 限制。序列的总长度在第一个字节中用可变掩码进行编码。

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

专门用于最多只需要36位的组成部分。

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

而浮动部分则需要20位来表示,编码值从0到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

注意:考虑的编码的结束,我们可以在3字节方案中保存一位,但我们不需要,不需要改变方案,我们可以重新使用以前的方案。

有关实现的细节请看这个模块

例子:

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

源代码链接

字段大小 类型 描述
32 Word8[32] 256 bits of hash digest

消息大小限制:32。

所以每当你看到 Hash SomeType 代码,这个字段将占用32个字节。Hash 只在类型安全的代码中使用附加的类型参数,并且对序列化没有影响。

例子:

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

公钥

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

源代码链接

字段大小 类型 描述
32 ByteString 32-byte PointCompressed
32 ByteString 32-byte ChainCode

消息大小限制:64。

签名

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

源代码链接

字段大小 类型 描述
64 Word8[64] 64 bytes of signature string

消息大小限制:64。

Epoch 索引

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

源代码链接

字段大小 类型 描述
1-10 UVarInt Word64 epoch index

消息大小限制:10。

例子:

ghci> hexEncode (EpochIndex 128)
"8001"

本地 Slot 索引

-- | Index of slot inside a concrete epoch.
newtype LocalSlotIndex = LocalSlotIndex
    { getSlotIndex :: Word16
    } deriving (Show, Eq, Ord, Ix, Generic, Hashable, Buildable, Typeable, NFData)

源代码链接

字段大小 类型 描述
1-3 UVarInt Word16 index of local slot

例子:

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)

源代码链接

字段大小 类型 描述
1-10 UVarInt Word64 Epoch index
1-3 UVarInt Word16 Slot index inside a concrete epoch

例子:

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)

源代码链接

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

源代码链接

消息大小限制:每个 Attributes 类型定义它们自己的限制。

一般情况序列化

存储为 totalLen + (k, v) pairs + 一些剩余的部分attrData 存储为 (Word8, v) 对列表,其中 keyWord8 类型,你应该指定如何以这种方式编码 h

字段大小 类型 描述
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

例子:

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

其中 h = 9 :: Word32,我们将 h 编码为两个键值对:值为4字节9,键是01

Attributes ()

在这种特殊情况下,没有 (key, value) 存储 - 只能是任意长度的字节数组。

字段大小 类型 描述
1-4 UVarInt Int64 n Size of attributes in bytes. Should be < 2^28
n Word8[n]   n bytes of data

例子:

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

脚本

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

源代码链接

字段大小 类型 描述
1-3 UVarInt Word16   Script version
1-9 UVarInt Int64 n Size of byte array
n Word8[n]   n bytes of script

例子:

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

地址属性

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

源代码链接

地址属性包含关于地址的附加信息:

  • HD钱包树根(aaPkDerivationPath)的加密导出路径。
  • 与此地址相关的股权分配(aaStakeDistribution

请注意,地址可能不包含加密派生路径。包含在属性中的不包含派生路径的地址通常从级别 0(钱包)公共密钥导出,并用做钱包的 ID。这样的地址并不意味着要包含任何资金。

地址有效载荷

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

源代码链接

HDAddressPayload 数据类型表示派生路径([Word32]),通过 ChaChaPoly1305 算法对称算法加密。加密密钥可以是 ByteString 32个字节中的任何一个,只有该地址的所有者才知道。

在目前的 Cardano 实现中,加密密钥是使用 PBKDF2 派生算法和 HMAC-SHA512 伪随机算法,进行500次迭代,将 address-hashing 字符串作为盐,从钱包的根公钥获得的32字节密钥。源代码链接

地址属性使用加密标签进行加密,从而可以检查属性是否被成功解密。因此,通过使用从钱包的根公共密钥导出的加密密钥来解密该地址的属性,可以确定给定的地址是否属于给定的钱包。这是实现属性解密和加密的源代码链接

地址分配

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

源代码链接

在 Byron 时代,所有的地址应该以 BootstrapEraDistr 作为股权分配 - 这意味着所有的股权都被自动委派给引导权益所有人。

在 Shelley 时代,地址使用 SingleKeyDistrUnsafeMultiKeyDistr 将股权委派给代表他们的权益所有人 ID。

地址

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

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

源代码链接

卡尔达诺地址使用 blake2b-224 作为地址结构的散列。

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

源代码链接

卡尔达诺支持不同的地址类型。地址类型根据资金的可用性不同而有所不同。每个地址类型都有相应的支出数据类型。消费数据是为了生成地址的散列数据的一部分。要从地址中花费资金,应该为消费数据,同验证数据(例如签名)提供 TxInWitness

目前 Cardano 提供3种地址类型:

  • 公钥地址(ATPublicKey):扩展了 Ed25519 公钥作为支出数据,对应的 Ed25519 签名作为验证数据。
  • 脚本地址(ATScript):作为支出数据的 Plutus 验证脚本,Plutus 赎回脚本作为验证数据
  • 兑现地址(ATRedeem):Ed25519 公钥作为支出数据,Ed25519 签名作为验证数据。仅用于为持有人预售 ADA。

还有为向前兼容而添加的特殊地址类型 ATUnknown

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

源代码链接

内部类型 Address 表示一个一个元组,作为 Address 的部分哈希存储。不仅 AddrSpendingData 会被哈希,AddrTypeAddrAttributes 也会被哈希。

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

源代码链接

一个 Address 数据类型由 Address 元组,类型标签,属性组成。

公钥地址

addrPkAttributes 是 HD 钱包必须的字段。

大小 类型 描述
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

例子:

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"

你可以注意到第一个例子,0xCF52C5EC001e380dea393a631ad563154a13bc5ee49fa4b62a60218358b5dcb875e00161 的 CRC32。

脚本地址

大小 类型 描述
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

例子:

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

您可以注意到,4字节的 0x61c5be8e 前缀是 011c7ec20301993e369571c6225e1e563812198433801820a2d7328756dc 的 CRC32 的前缀。

未知地址

大小 类型 描述
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

例子:

让我们对 UnknownAddressType 3 "a" 编码。没有 CRC32,它会被编码为 0x0301610x030161 的 CRC32 是 0xDEA907C4。因此我们有:

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)

源代码链接

字段大小 类型 描述
size(Hash) Hash Root hash of Merkle tree

链难度

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

源代码链接

大小 类型 描述
1-10 UVarInt Word64 Chain difficulty

SlotLeaders

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

源代码链接

字段大小 类型 描述
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

Pvss 的类型前缀在 Crypto.PVSS 模块定义。本节介绍这些类型的二进制格式。

Point

newtype Point = Point { unPoint :: SSL.EcPoint }
    deriving (Generic)

源代码链接

字段大小 类型 描述
33 ByteString Binary represented elliptic curve point (see ecPointFromOct function)

Secret

-- | Secret
newtype Secret = Secret Point
    deriving (Show,Eq,NFData,Binary)

源代码链接

字段大小 类型 描述
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)

源代码链接

Scalar 源代码链接

字段大小 类型 描述
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)
字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
size(Point) Point Extra generator

消息大小限制:33。

Commitment

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

源代码链接

字段大小 类型 描述
size(Point) Point Commitment

PublicKey

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

源代码链接

字段大小 类型 描述
size(Point) Point Public key

密钥共享

一下大多数类型是 PVSS 类型的别名,所以它们以相同的方式被序列化。

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

SecretSharingExtra 的格式:

字段大小 类型 描述
size(ExtraGen) Pvss.ExtraGen   Extra generator
1-9 UVarInt Int n Length of commitments list
n * size(Pvss.Commitment) [Pvss.Commitment]   Commitments

消息大小限制:32:

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

源代码链接

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)
字段大小 类型 描述
1-9 UVarInt Int n  
n * (sizeof(VssPublicKey) + sizeof(EncShare)) HashMapCommShares   commShares
sizeof(SecretSharingExtra) AsBinary SecretSharingExtra   commExtra
sizeof(SecretProof) AsBinary SecretProof   commProof

其中 HashMapCommShares = HashMap (AsBinary VssPublicKey) (NonEmpty (AsBinary EncShare))

消息大小限制:33。

CommitmentSignature

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

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
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)
字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
size(Secret) AsBinary Secret Revealed secret

消息大小限制:33。

OpeningsMap

type OpeningsMap = HashMap StakeholderId Opening

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
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

消息大小限制:171。

VssCertificatesMap

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

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
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)

源代码链接

标签大小 标签类型 标签值 描述 字段大小 字段 类型
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

区块头部

区块版本

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

源代码链接

字段大小 类型 描述
2 Word16 Major version
2 Word16 Minor version
1 Word8 Alt version from initial US spec

软件版本

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)

源代码链接

字段大小 类型 描述
1 UVarInt Int n Length of application name (should be <= 10)
n Word8[n]   svAppName: UTF8 encoded application name
4 Word32   svNumber

主区块头部

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
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

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

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
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)

源代码链接

标签大小 标签类型 标签值 描述 字段大小
1 Word8 0x00 Tag for GenesisBlockHeader  
        size(GenesisBlockHeader)
    0x01 Tag for MainBlockHeader  
        size(MainBlockHeader)

区块

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

源代码链接

标签大小 标签类型 标签值 描述 字段大小
1 Word8 0x00 Tag for GenesisBlock  
        size(GenesisBlock)
    0x01 Tag for MainBlock  
        size(MainBlock)

区块交换信息

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

源代码链接

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)

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 字段
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)

源代码链接

字段大小 类型 描述
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)

源代码链接

字段大小 类型 描述
1-9 UVarInt Int64 n Size of Block in bytes
size(Block) Block   Block with size of n bytes

包含一个 Block。我们对区块大小进行编码,然后对区块进行编码,以便在不消耗整个区块的情况下,如果大小错误,我们可以拒绝区块。

Transaction sending

要发送转账,您需要创建 TxAux 数据类型并将其发送到节点。本节介绍了成功执行发送所需的所有数据类型。

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)

源代码链接

字段大小 类型 字段 name
size(Hash) Hash txInHash
4 Word32 txInIndex

转账输出

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

源代码链接

字段大小 类型 字段 name
size(Address) Address txOutAddress
size(Coin) Coin txOutValue

例子:

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

转账输出分配

type TxOutDistribution = [(StakeholderId, Coin)]

源代码链接

定义 distr_size(n) = n * (size(Hash) + size(Coin))

字段大小 类型 描述
distr_size(n) <Hash,Coin>[n] Array of pairs for StakeholderId and Coin

转账输出辅助词

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

源代码链接

字段大小 类型 字段 name
size(TxOut) TxOut toaOut
size(TxOutDistribution) TxOutDistribution toaDistr

转账签名数据

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

源代码链接

字段大小 类型 描述
size(TxIn) TxIn txSigInput
size(Hash) Hash txSigOutsHash
size(Hash) Hash txSigDistrHash

转账见证

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

源代码链接

标签大小 标签类型 标签值 描述 字段大小 字段 类型 字段 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.
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)

源代码链接

字段大小 类型 描述
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

转账分发

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

源代码链接

虽然转账分发可以使用前面的序列化策略来作为列表的列表存储,但经常发生我们通过了空列表的列表的情况。在这种情况下,我们应该更有效地存储这些列表。

标签大小 标签类型 标签值 描述 字段大小 字段 类型
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 + auxiliary data
data TxAux = TxAux
    { taTx           :: !Tx
    , taWitness      :: !TxWitness
    , taDistribution :: !TxDistribution
    } deriving (Generic, Show, Eq)

源代码链接

字段大小 类型 描述
size(Tx) Tx Transaction itself
size(TxWitness) TxWitness Witness for transaction
size(TxDistribution) TxDistribution Transaction distribution

委派

请阅读委派信息机制的解释,在这里你只能找到消息格式的描述。

代理证书

类似于 Signature

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

源代码链接

字段大小 类型 描述
64 Word8[64] unProxyCert: 64 bytes of signature string

消息大小限制:64。

例子:

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

代理密钥

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

源代码链接

字段大小 类型 描述
size(w) w pskOmega
size(PublicKey) PublicKey pskIssuerPk
size(PublicKey) PublicKey pskDelegatePk
size(ProxyCert) ProxyCert w pskCert

代理签名

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

源代码链接

字段大小 类型 描述
size(PproxySK) ProxySecretKey psigPsk
64 XSignature psigSig

代理密钥和轻量级委派的签名

警告:目前,轻量级委派被禁用,并在 Shelley 版本进行了重新编写,因此一下信息可能已过时。

密钥

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

源代码链接

字段大小 类型 描述
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

例子:

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

签名

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

源代码链接

字段大小 类型 描述
1-10 UVarInt Word64 from epoch
1-10 UVarInt Word64 to epoch
size(PublicKey) PublicKey pdDelegatePk
64 ProxyCert (EpochIndex, EpochIndex) pdCert
64 Signature pdSig

例子

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

代理密钥和重量级代理签名

密钥

-- | Correspondent SK for no-ttl proxy signature scheme.
type ProxySKHeavy = ProxySecretKey EpochIndex

源代码链接

字段大小 类型 描述
1-10 UVarInt Word64 epoch
size(PublicKey) PublicKey pskIssuerPk
size(PublicKey) PublicKey pskDelegatePk
64 ProxyCert EpochIndex pskCert

签名

-- | Simple proxy signature without ttl/epoch index
-- constraints. 'EpochIndex' inside is needed for replay attack
-- prevention.
type ProxySigHeavy a = ProxySignature EpochIndex a

源代码链接

字段大小 类型 描述
1-10 UVarInt Word64 epoch
size(PublicKey) PublicKey pdDelegatePk
64 ProxyCert EpochIndex pdCert
64 Signature pdSig

轻量级委派确认

警告:目前,轻量级委派被禁用,并在 Shelley 版本进行了重新编写,因此一下信息可能已过时。

ProxySKLightConfirmation

-- | Confirmation of light cert type.
type ProxySKLightConfirmation = (ProxySKLight, ProxySigLight ProxySKLight)
字段大小 描述
size(ProxySKLight) Certificate
size(ProxySigLight) Proof for certificate

更新系统

更新投票

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

源代码链接

字段大小 类型 字段
size(PublicKey) PublicKey uvKey
size(Hash) Hash uvProposalId
1 Bool uvDecision
64 Signature uvSignature

投票标识符

type VoteId = (UpId, PublicKey, Bool)

源代码链接

字段大小 类型 描述
size(Hash) Hash Hash of update proposal
size(PublicKey) PublicKey Public key
1 Bool Vote result

有关字段的更多描述,请参阅 UpdateVote 消息描述。VoteId(uvProposalId, uvKey, uvDecision)

区块版本数据

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

源代码链接

字段大小 类型 字段
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

更新数据

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

源代码链接

字段大小 类型 字段
size(Hash) Hash udAppDiffHash
size(Hash) Hash udPkgHash
size(Hash) Hash udUpdaterHash
size(Hash) Hash udMetadataHash

系统标签

-- | 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 按 UTF-8 编码为 ByteString

字段大小 类型 字段
1-9 UVarInt Int64 n Size of text in bytes
n Word8[n]   n bytes of UTF-8 encoded text

更新协议

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)
字段大小 类型 字段
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

对等点数据

HandlerSpec

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

源代码链接

类型 大小 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

例子:

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)

源代码链接

字段大小 类型 字段
4 Int32 vIMagic
5 BlockVersion vIBlockVersion
size(HandlerSpecs) HandlerSpecs vIInHandlers
size(HandlerSpecs) HandlerSpecs vIOutHandlers

HandlerSpec 只是在消息名称和这些消息的处理方式之间进行映射:通过单消息风格或对话风格+消息标签。这个映射被编码为其他 Map - 列表对。

InSpecsOutSpecs 只是 HandlerSpecs 的封装。这些封装只用于保证类型安全,以区分传入和传出消息的规范。

PeerData

type PeerData = VerInfo

源代码链接

字段大小 类型 描述
size(VerInfo) VerInfo MessageName table

我们发送的 VerInfo这里创建。

例子:

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"