• parity开源解析 parity-scale-codec

    用于Parity Substrate框架中的类型的SCALE(Simple Concatenated Aggregate Little-Endian)数据格式的Rust实现。

    SCALE是一种轻量级格式,允许编码(和解码),使其非常适合资源受限的执行环境,如区块链运行时和低功耗,低内存设备。

    它是一种极轻量级的编码格式,专为资源受限的执行上下文(如Substrate运行时)中的高性能,无副本编码和数据解码而设计。它 不以任何方式自我描述,并假设解码上下文具有关于编码数据的所有类型知识。

使用方法

  1. toml引用

    [dependencies.codec]
    default-features = false
    features = ['derive']
    package = 'parity-scale-codec'
    version = '1.0.5'
    
  2. 需要对结构体声名宏:#[derive(Encode, Decode)]

  3. 序列化调用x.encode(),反序列化调用 x::decoce

代码使用示例:

use codec::{Encode, Decode};#[derive(Debug, Encode, Decode)]
struct Test {a:u64,b:Vec<u8>,
}let t = Test{a:1,b:vec![1,1,1,1,2,1,1,1,1,1,2,1,1,1]};
let ten = t.encode();
println!("{:?}",t.encode());
let tdec = Test::decode(&mut &ten[..]).unwrap();
println!("{:?}",tdec);

输出结果:

[1, 0, 0, 0, 0, 0, 0, 0, 56, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1]

Test { a: 1, b: [1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1] }

  1. 可以对结构体字段声明#[codec(compact)] 在序列化的时候对其使用压缩

代码使用示例:

#[derive(Debug, Encode, Decode)]
struct TestCompact {#[codec(compact)]a: u64,b: Vec<u8>,
}let tc = TestCompact { a: 1, b: vec![1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1] };let tcen = tc.encode();println!("{:?}", tc.encode());let tcdec = TestCompact::decode(&mut &tcen[..]).unwrap();println!("{:?}", tcdec);

输出结果:

[4, 56, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1]

Test { a: 1, b: [1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1] }

对比输出结果可以看出,同样的u64值为1的字段,在压缩前占用 [1, 0, 0, 0, 0, 0, 0, 0] 而压缩后只占用 [4],所以在存储和传输的时候对字段启用压缩是很好的解决方案.

parity-scale-codec 不能对有符号的数据编码解码

  • Rust标准解析库 serde

Serde是一个有效和一般地对Rust数据结构进行序列化和反序列化的框架。

Serde生态系统由知道如何序列化和反序列化自身的数据结构以及知道如何序列化和反序列化其他事物的数据格式组成。Serde提供了这两个组相互交互的层,允许使用任何支持的数据格式序列化和反序列化任何支持的数据结构。

设计

许多其他语言依赖运行时反射来序列化数据,而Serde则建立在Rust强大的特征系统上。知道如何序列化和反序列化自身的数据结构是实现Serde SerializeDeserializetraits(或使用Serde的derive属性在编译时自动生成实现)的数据结构。这避免了反射或运行时类型信息的任何开销。事实上,在许多情况下,Rust编译器可以完全优化数据结构和数据格式之间的交互,使Serde序列化与手写序列化器执行相同的速度,以便选择特定的数据结构和数据格式。

数据格式

以下是社区为Serde实施的部分数据格式列表。

  • JSON,许多HTTP API使用的无处不在的JavaScript Object Notation。
  • Bincode,一种紧凑的二进制格式,用于Servo渲染引擎中的IPC。
  • CBOR,一种简洁的二进制对象表示,专为小消息大小而设计,无需进行版本协商。
  • YAML,一种流行的人性化配置语言,不是标记语言。
  • MessagePack,一种类似于紧凑型JSON的高效二进制格式。
  • TOML,Cargo使用的最小配置格式。
  • Pickle,Python世界中常见的一种格式。
  • RON,生锈的对象表示法。
  • BSON,MongoDB使用的数据存储和网络传输格式。
  • Avro,Apache Hadoop中使用的二进制格式,支持模式定义。
  • JSON5,JSON的超集,包括ES5的一些产品。
  • 明信片,一种no_std和嵌入式系统友好的紧凑二进制格式。
  • URL,x-www-form-urlencoded格式。
  • Envy,一种将环境变量反序列化为Rust结构的方法。 (仅反序列化)
  • Envy Store,一种将AWS参数存储参数反序列化为Rust结构的方法。(仅反序列化)

数据结构

开箱即用,Serde能够以上述任何格式序列化和反序列化常见的Rust数据类型。例如String&strusizeVec<T>HashMap<K,V>在所有支持。

使用方法

  1. toml引用

    [dependencies]serde = "1.0.63"serde_derive = "1.0.27"
    [dependencies.bincode]features = ["i128"]
    
  2. 需要对结构体声名宏:#[derive(Serialize, Deserialize)]

  3. 序列化调用serialize(),反序列化调用 deserialize()

代码使用示例:

#[macro_use]
extern crate serde_derive;
extern crate bincode;use bincode::{deserialize, serialize};#[derive(Serialize, Deserialize, PartialEq, Debug)]
struct Test {a:u64,b:Vec<u8>,
}fn main() {let t = Test{a:1,b:vec![1,1,1,1,2,1,1,1,1,1,2,1,1,1]};let encoded: Vec<u8> = serialize(&t).unwrap();println!("{:?}",encoded);let tdec :Test = deserialize( &encoded[..]).unwrap();println!("{:?}",tdec);
}

输出结果:

[1, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1]

Test { a: 1, b: [1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 2, 1, 1, 1] }

  • RLP编码和解码

    RLP(Recursive Length Prefix,递归的长度前缀)是一种编码规则,可用于编码任意嵌套的二进制数组数据。RLP编码的结果也是二进制序列。RLP主要用来序列化/反序列化数据。

    RLP编码的定义只处理以下2类底层数据:

    • 字符串(string)是指字节数组。例如,空串”“,再如单词”cat”,以及句子”Lorem ipsum dolor sit amet, consectetur adipisicing elit”等。

    • 列表(list)是一个可嵌套结构,里面可包含字符串和列表。例如,空列表[],再如一个包含两个字符串的列表[“cat”,”dog”],再比如嵌套列表的复杂列表[“cat”, [“puppy”, “cow”], “horse”, [[]], “pig”, [“”], “sheep”]。

    所有上层类型的数据需要转成以上的2类数据,才能进行RLP编码。转换的规则RLP编码不统一规定,可以自定义转换规则。例如struct可以转成列表;int可以转成二进制序列(属于字符串这一类, 必须去掉首部0,必须用大端模式表示);map类型可以转换为由k和v组成的结构体、k按字典顺序排列的列表:[[k1,v1],[k2,v2]…] 等。

RLP编码规则
RLP编码的重点是给原始数据前面添加若干字节的前缀,而且这个前缀是和数据的长度相关的,并且是递归的;

RLP编码中的长度是数据的实际存储空间的字节大小,去掉首位0的正整数,用大端模式表示的二进制格式表示;RLP编码规定数据(字符串或列表)的长度的长度不得大于8字节。因为超过8字节后,一个字节的前缀就不能存储了。

  1. 如果字符串的长度是1个字节,并且它的值在[0x00, 0x7f] 范围之间,那么其RLP编码就是字符串本身。即前缀为空,用前缀代表字符串本身;否则,如果一个字符串的长度是0-55字节,其RLP编码是前缀跟上(拼接)字符串本身,前缀的值是0x80加上字符串的长度。由于在该规则下,字符串的最大长度是55,因此前缀的最大值是0x80+55=0xb7,所以在本规则下前缀(第一个字节)的取值范围是[0x80, 0xb7];

  2. 如果字符串的长度大于55个字节,其RLP编码是前缀跟上字符串的长度再跟上字符串本身。前缀的值是0xb7加上字符串长度的二进制形式的字节长度(即字符串长度的存储长度)。即用额外的空间存储字符串的长度,而前缀中只存字符串的长度的长度。例如一个长度是1024的字符串,字符串长度的二进制形式是\x04\x00,因此字符串长度的长度是2个字节,所以前缀应该是0xb7+2=0xb9,由此得到该字符串的RLP编码是\xb9\x04\x00再跟上字符串本身。因为字符串长度的长度最少需要1个字节存储,因此前缀的最小值是0xb7+1=0xb8;又由于长度的最大值是8个字节,因此前缀的最大值是0xb7+8=0xbf,因此在本规则下前缀的取值范围是[0xb8, 0xbf];

  3. 由于列表的任意嵌套的,因此列表的编码是递归的,先编码最里层列表,再逐步往外层列表编码。如果一个列表的总长度(payload,列表的所有项经过编码后拼接在一起的字节大小)是0-55字节,其RLP编码是前缀依次跟上列表中各项的RLP编码。前缀的值是0xc0加上列表的总长度。在本规则下前缀的取值范围是[0xc0, 0xf7]。本规则与规则2类似;如果一个列表的总长度大于55字节,它的RLP编码是前缀跟上列表的长度再依次跟上列表中各元素项的RLP编码。前缀的值是0xf7加上列表总长度的长度。编码的第一个字节的取值范围是[0xf8, 0xff]。本规则与规则3类似;

RLP解码规则

根据RLP编码规则和过程,RLP解码的输入一律视为二进制字符数组,其过程如下:

根据输入首字节数据,解码数据类型、实际数据长度和位置;根据类型和实际数据,解码不同类型的数据;继续解码剩余的数据;

总结

与其他序列化方法相比,RLP编码的优点在于使用了灵活的长度前缀来表示数据的实际长度,并且使用递归的方式能编码相当大的数据。

当接收或者解码经过RLP编码后的数据时,根据第1个字节就能推断数据的类型、大概长度和数据本身等信息。而其他的序列化方法, 不能根据第1个字节获得如此多的信息量。

代码使用示例:

type Tmp struct {A uint64B string
}func TestRlp(t *testing.T){var tt Tmptt.A = 1tt.B= "ssssssssssss"c,_ := rlp.EncodeToBytes(tt)fmt.Println(c)
}

输出结果:

[206 1 140 115 115 115 115 115 115 115 115 115 115 115 115]

  • Amino编码规则

    Amino是一个对象编码规范。它是Proto3的一个子集,具有接口支持的扩展。其中Amino主要与Proto2兼容(但不与Proto2兼容)。

Amino目标

  • 通过支持接口将奇偶校验引入逻辑对象和持久对象。
  • 具有唯一/确定性的价值编码。
  • 二进制字节必须可以使用模式进行解码。
  • 架构必须可升级。
  • 必须在没有模式的情况下解析足够的结构。
  • 编码器和解码器逻辑必须相当简单。
  • 序列化必须相当紧凑。
  • 必须保持足够兼容的JSON格式(但不能与JSON进行一般转换)

Amino vs JSON

JavaScript Object Notation(JSON)是人类可读的,结构良好的,非常适合与Javascript的互操作性,但效率很低。Protobuf3,BER,RLP都存在这些问题,因为我们需要更紧凑和有效的二进制编码标准。Amino为复杂对象(例如嵌入对象)提供了高效的二进制编码,这些对象可以自然地与您最喜爱的现代编程语 此外,Amino还具有完全兼容的JSON编码。

type Tmp struct {A uint64B string
}func TestTmp(t *testing.T) {tt := Tmp{A: 1,B: "ssssssssssss",}cdc := amino.NewCodec()c, _ := cdc.MarshalBinaryLengthPrefixed(tt)fmt.Println(c)
}

输出结果:

[16 8 1 18 12 115 115 115 115 115 115 115 115 115 115 115 115]

* 为什么flatbuffers这么高效?

1.序列化数据访问不经过转换,即使用了分层数据。这样我们就不需要初始化解析器(没有复杂的字段映射)并且转换这些数据仍然需要时间。

2.flatbuffers不需要申请更多的空间,不需要分配额外的对象。

CKB 特点

  • 使用 Rust 作为主要开发语言 通过不同的功能模块实现一个紧凑,完整的区块链底层系统 基于 Cell 模型来扩展 UTXO
  • 模型并支持存储通用状态 基于 Cell 模型和 CKB-VM 实现智能合约,合约计算验证分离,计算发生在链下,验证则在链上
  • 支持智能合约的代码逻辑和状态分离 CKB-VM 使用 RISC-V 指令集,支持使用如 Ruby,Python 等高级语言来编写智能合约
  • 使用 Flatbuffer 实现数据结构的序列化,无需解析直接访问序列化数据以及基于 Zero-copy 的高效内存使用效率
  • 系统内部基于消息和 Channel 实现模块通讯机制,在高性能,以及底层存储访问和索引访问使用共享内存加锁实现 安全的 P2P
  • 网络实现,具有更好的网络安全性,对 DoS 和日蚀攻击等有更好的抵抗性 更高效的节点间发现,同步协议

CKB P2P Network 示意图

CKB 主要模块

SRC 模块
存储了 Main 函数,是整个项目的编译入口模块。

CORE 模块
用于保存 CKB 的核心数据结构的定义,包括 Block,Cell,Transaction 等核心数据结构。

SPEC 模块
链的共识配置,该配置会写入创世块。不同配置的节点直接无法通信。

SHARED 模块
用于保存各个模块公用的逻辑和代码。

DB 模块
封装了底层的数据持久化层,CKB 底层存储使用的是 KV 数据存储,对应的实现有两种,一种是基于 RocksDB 的实现,利用 RocksDB 将数据持久化到磁盘。另外一种实现是基于内存的模拟持久化实现,主要用于测试和开发等场景。

CHAIN 模块
实现了区块链数据结构。使用 DB 模块进行持久化。Chain 主要指责是记录和更新本地累计工作量最高的链,并维护链上数据的索引。在更新链时需要进行验证,并同时更新索引。

POOL 模块
Pool 模块的主要功能是实现交易池,CKB 的 Pool 的特点是根据网络状况动态调整出块时间,这样会更合理的利用网络资源和带宽。交易池的设计和实现的最大挑战是要同时兼顾多个目标并取得平衡。包括考虑交易的排序,依赖关系,以及整体性能,尤其是降低节点之间需要同步的数据并且合理的使用缓存。

PROTOCOL 模块
用于存放节点间消息的结构定义,以及消息体的 Builder。消息使用 Flatbuffers 序列化。

NETWORK 模块
点对的通讯的底层实现相关代码,对 Rust-libp2p 进行了封装,并将一些基础协议通过服务的方式暴露出来。通过对 Libp2p 的抽象实现未来可定制的 Libp2p 更加方便。

SYNC 模块
实现了 CKB 的网络同步协议,包括两个协议,分别是 Synchronizer 协议和 Relayer 协议。Synchronizer 协议的工作方式是 Head-first,更高效的利用网络带宽提升区块下载效率,用于节点之间高速下载区块数据。Relayer 协议是节点之间用来处理广播和转发新的交易。Sync 模块在 Bitcoin 的 Head-first 同步,Compact Block 等协议的基础上,结合了交易提交区,叔伯快统计等功能。

CKB-VM 模块
CKB-VM 是一个独立的实现。从实现角度,CKB-VM 是 RISC-V 的硬件 CPU 指令集的实现,所有的实现完全遵循 RISC-V 的标准。所以可以将 CKB-VM 当作一个 General Sandbox Runtime Module,CKB-VM 在项目中的作用是验证状态和执行智能合约,让整个系统的计算层保持了最大限度的灵活性,如通过更新 Cell 中存储的系统合约可以实现添加新的加密算法或其他功能等,并且 CKB-VM 的 Sandbox 的隔离性设计为执行合约提供了强大的运行安全保障。

MINER 模块
通过可插拔实现了不同的共识算法替换,目前为了开发方便,实现了 CPU 和 Cuckoo 两套内置共识算法,并且可方便增加外部实现的共识算法,如 ProgPow 算法。

NOTIFY 模块
是一套用于内部模块之间消息通讯的 Pub/Sub 模块。

DEVTOOLS 模块
包含用 Ruby 实现的脚本,用于开发过程中方便向区块链发送测试数据。

  • **GRIN 序列化格式 **rustc-serialize

    NOTE: This crate is deprecated in favor of serde. No new feature development will happen in this crate, although bug fixes proposed through PRs will still be merged. It is very highly recommended by the Rust Library Team that you use serde, not this crate.

  • **以太坊2.0 序列化 ** SimpleSerialize SSZ

    Constants

    Name Value Description
    BYTES_PER_CHUNK 32 Number of bytes per chunk.
    BYTES_PER_LENGTH_OFFSET 4 Number of bytes per serialized length offset.
    BITS_PER_BYTE 8 Number of bits per byte.

    Typing

    Basic types

    Composite types

    • container: ordered heterogeneous collection of values

      • python dataclass notation with key-type pairs, e.g.
      class ContainerExample(Container):foo: uint64bar: boolean
      
    • vector: ordered fixed-length homogeneous collection, with N values

      • notation Vector[type, N], e.g. Vector[uint64, N]
    • list: ordered variable-length homogeneous collection, limited to N values

      • notation List[type, N], e.g. List[uint64, N]
    • bitvector: ordered fixed-length collection of boolean values, with N bits

      • notation Bitvector[N]
    • bitlist: ordered variable-length collection of boolean values, limited to N bits

      • notation Bitlist[N]
    • union: union type containing one of the given subtypes

      • notation Union[type_0, type_1, ...], e.g. union[null, uint64]

    Variable-size and fixed-size

    We recursively define “variable-size” types to be lists, unions, Bitlist and all types that contain a variable-size type. All other types are said to be “fixed-size”.

    Aliases

    For convenience we alias:

    Default values

    Assuming a helper function default(type) which returns the default value for type, we can recursively define the default value for all types.

    Type Default Value
    uintN 0
    boolean False
    Container [default(type) for type in container]
    Vector[type, N] [default(type)] * N
    Bitvector[boolean, N] [False] * N
    List[type, N] []
    Bitlist[boolean, N] []
    Union[type_0, type_1, ...] default(type_0)

    is_zero

    An SSZ object is called zeroed (and thus, is_zero(object) returns true) if it is equal to the default value for that type.

    Illegal types

Serialization

We recursively define the serialize function which consumes an object value (of the type specified) and returns a bytestring of type bytes.

Note: In the function definitions below (serialize, hash_tree_root, signing_root, is_variable_size, etc.) objects implicitly carry their type.

uintN

assert N in [8, 16, 32, 64, 128, 256]
return value.to_bytes(N // BITS_PER_BYTE, "little")

boolean

assert value in (True, False)
return b"\x01" if value is True else b"\x00"

null

return b""

Bitvector[N]

array = [0] * ((N + 7) // 8)
for i in range(N):array[i // 8] |= value[i] << (i % 8)
return bytes(array)

Bitlist[N]

Note that from the offset coding, the length (in bytes) of the bitlist is known. An additional leading 1 bit is added so that the length in bits will also be known.

array = [0] * ((len(value) // 8) + 1)
for i in range(len(value)):array[i // 8] |= value[i] << (i % 8)
array[len(value) // 8] |= 1 << (len(value) % 8)
return bytes(array)

Vectors, containers, lists, unions

# Recursively serialize
fixed_parts = [serialize(element) if not is_variable_size(element) else None for element in value]
variable_parts = [serialize(element) if is_variable_size(element) else b"" for element in value]# Compute and check lengths
fixed_lengths = [len(part) if part != None else BYTES_PER_LENGTH_OFFSET for part in fixed_parts]
variable_lengths = [len(part) for part in variable_parts]
assert sum(fixed_lengths + variable_lengths) < 2**(BYTES_PER_LENGTH_OFFSET * BITS_PER_BYTE)# Interleave offsets of variable-size parts with fixed-size parts
variable_offsets = [serialize(sum(fixed_lengths + variable_lengths[:i])) for i in range(len(value))]
fixed_parts = [part if part != None else variable_offsets[i] for i, part in enumerate(fixed_parts)]# Return the concatenation of the fixed-size parts (offsets interleaved) with the variable-size parts
return b"".join(fixed_parts + variable_parts)

If value is a union type:

Define value as an object that has properties value.value with the contained value, and value.type_index which indexes the type.

serialized_bytes = serialize(value.value)
serialized_type_index = value.type_index.to_bytes(BYTES_PER_LENGTH_OFFSET, "little")
return serialized_type_index + serialized_bytes

Deserialization

Because serialization is an injective function (i.e. two distinct objects of the same type will serialize to different values) any bytestring has at most one object it could deserialize to. Efficient algorithms for computing this object can be found in the implementations.

Note that deserialization requires hardening against invalid inputs. A non-exhaustive list:

  • Offsets: out of order, out of range, mismatching minimum element size.
  • Scope: Extra unused bytes, not aligned with element size.
  • More elements than a list limit allows. Part of enforcing consensus.

Merkleization

We first define helper functions:

  • size_of(B), where B is a basic type: the length, in bytes, of the serialized form of the basic type.
  • chunk_count(type): calculate the amount of leafs for merkleization of the type.
    • all basic types: 1
    • Bitlist[N] and Bitvector[N]: (N + 255) // 256 (dividing by chunk size, rounding up)
    • List[B, N] and Vector[B, N], where B is a basic type: (N * size_of(B) + 31) // 32 (dividing by chunk size, rounding up)
    • List[C, N] and Vector[C, N], where C is a composite type: N
    • containers: len(fields)
  • bitfield_bytes(bits): return the bits of the bitlist or bitvector, packed in bytes, aligned to the start. Length-delimiting bit for bitlists is excluded.
  • pack: Given ordered objects of the same basic type, serialize them, pack them into BYTES_PER_CHUNK-byte chunks, right-pad the last chunk with zero bytes, and return the chunks.
  • next_pow_of_two(i): get the next power of 2 of i, if not already a power of 2, with 0 mapping to 1. Examples: 0->1, 1->1, 2->2, 3->4, 4->4, 6->8, 9->16
  • merkleize(chunks, limit=None): Given ordered BYTES_PER_CHUNK-byte chunks, merkleize the chunks, and return the root:
    • The merkleization depends on the effective input, which can be padded/limited:

      • if no limit: pad the chunks with zeroed chunks to next_pow_of_two(len(chunks)) (virtually for memory efficiency).
      • if limit > len(chunks), pad the chunks with zeroed chunks to next_pow_of_two(limit) (virtually for memory efficiency).
      • if limit < len(chunks): do not merkleize, input exceeds limit. Raise an error instead.
    • Then, merkleize the chunks (empty input is padded to 1 zero chunk):
      • If 1 chunk: the root is the chunk itself.
      • If > 1 chunks: merkleize as binary tree.
  • mix_in_length: Given a Merkle root root and a length length ("uint256" little-endian serialization) return hash(root + length).
  • mix_in_type: Given a Merkle root root and a type_index type_index ("uint256" little-endian serialization) return hash(root + type_index).

We now define Merkleization hash_tree_root(value) of an object value recursively:

  • merkleize(pack(value)) if value is a basic object or a vector of basic objects.
  • merkleize(bitfield_bytes(value), limit=chunk_count(type)) if value is a bitvector.
  • mix_in_length(merkleize(pack(value), limit=chunk_count(type)), len(value)) if value is a list of basic objects.
  • mix_in_length(merkleize(bitfield_bytes(value), limit=chunk_count(type)), len(value)) if value is a bitlist.
  • merkleize([hash_tree_root(element) for element in value]) if value is a vector of composite objects or a container.
  • mix_in_length(merkleize([hash_tree_root(element) for element in value], limit=chunk_count(type)), len(value)) if value is a list of composite objects.
  • mix_in_type(merkleize(value.value), value.type_index) if value is of union type.

Self-signed containers

Let value be a self-signed container object. The convention is that the signature (e.g. a "bytes96" BLS12-381 signature) be the last field of value. Further, the signed message for value is signing_root(value) = hash_tree_root(truncate_last(value)) where truncate_last truncates the last element of value.

Implementations

Language Project Maintainer Implementation
Python Ethereum 2.0 Ethereum Foundation https://github.com/ethereum/py-ssz
Rust Lighthouse Sigma Prime https://github.com/sigp/lighthouse/tree/master/eth2/utils/ssz
Nim Nimbus Status https://github.com/status-im/nim-beacon-chain/blob/master/beacon_chain/ssz.nim
Rust Shasper ParityTech https://github.com/paritytech/shasper/tree/master/utils/ssz
TypeScript Lodestar ChainSafe Systems https://github.com/ChainSafe/ssz-js
Java Cava ConsenSys https://www.github.com/ConsenSys/cava/tree/master/ssz
Go Prysm Prysmatic Labs https://github.com/prysmaticlabs/go-ssz
Swift Yeeth Dean Eigenmann https://github.com/yeeth/SimpleSerialize.swift
C# Jordan Andrews https://github.com/codingupastorm/csharp-ssz
C++ Jiyun Kim https://github.com/NAKsir-melody/cpp_ssz
  • 总结

  1. 如果用基于Substrate运行时,WebAssembly虚拟机方案,可以采用parity-scale,因为它用此做了优化,并且还支持压缩,而且不加入自我描述字段,结构简单,数据压缩率高。第一推荐。
  2. serde库,标准且强大,而且可无缝转自种序列化格式,但是不够专业。第二推荐。

区块链常用序列化分析相关推荐

  1. 第十一课 区块链常用共识算法介绍

    上一节课我们学习了共识算法理论基础,今天我们继续深入学习区块链共识算法,通过这节课我们将了解工作量证明.权威证明.权威授权证明.实用拜占庭容错等相关内容. 在学习课程的时候,你也可以领取BaaS平台为 ...

  2. 常用的数据结构_三分钟了解区块链常用数据结构「默克尔树」

    免责声明:本文旨在传递更多市场信息,不构成任何投资建议.文章仅代表作者观点,不代表火星财经官方立场. 小编:记得关注哦 来源:万向区块链 原文标题:三分钟了解区块链常用数据结构「默克尔树」 默克尔树是 ...

  3. 《迅雷链精品课》第三课:区块链主流框架分析

    上一节课我们学习了区块链的技术架构,系统地分析了区块链平台的6个层次:数据层.网络层.共识层.合约层.应用层.接口层.这节课我们将结合实际看看现在主流区块链项目的技术架构:思考我们在设计具体的业务架构 ...

  4. 《迅雷链精品课》第六课:主流区块链数据存储分析(一)

    上一节课我们学习了区块链中的账户与账本,了解区块链账户的特点和本质.今天我们将系统地学习区块链数据存储,在课程学习前,大家可以先思考下列问题:区块链的数据是如何存储的?区块链如何在没有中心信任节点的情 ...

  5. 区块链技术的分析与应用

    随着信息技术的快速发展,数字货币.智能合约等问题逐渐浮出水面,传统的信息技术面临着诸多挑战.区块链技术作为一种新型的分布式账本技术,具有去中心化.不可篡改.透明等特点,成为了信息技术领域的热门话题.本 ...

  6. 以太坊和区块链实战技术分析详解

    想知道更多关于区块链技术知识,请百度[链客区块链技术问答社区] 链客,有问必答!!以太坊和区块链 一.    以太坊和区块链的关系      从区块链历史上来说,先诞生了比特币,当时并没有区块链这个技 ...

  7. 区块链共识机制分析——论PoW,PoS,DPos和DAG的优缺点

    近期,随着区块链技术在社区中的声音越来越大,业界已经开始从技术角度对区块链进行全方位的解读.作为第一批区块链技术的实现,传统比特币与以太坊在共识机制.存储机制.智能合约机制.跨链通讯机制等领域并没有非 ...

  8. 区块链常用架构是什么?它和保险业又如何结合?

    http://blog.csdn.net/Blockchain_lemon/article/details/79350641 区块链2.0的分类与特点 区块链2.0的典型代表是以太坊(Ethereum ...

  9. 中国区块链标准建设分析

    行业标准化建设是制度化的最高形式,有利于稳定和提高产品.工业和服务质量,提高企业核心竞争力.长期以来,我国政府一直在强调和鼓励建设各个行业标准.区块链作为一个新型技术,它的标准建设是发展区块链技术和实 ...

最新文章

  1. 命令行查看电脑WIFI密码
  2. 第一章新增例题:访问修饰符
  3. 三十五、Scrapy 中的杂知识总结和代理池的编写
  4. dij算法为什么不能处理负权,以及dij算法变种
  5. DIV相对于父DIV底部对齐的实现方法-Div+CSS教程
  6. sersync2 安装,配置
  7. java之设计模式工厂三兄弟之简单工厂模式
  8. java obix_Gson序列化多态对象列表
  9. Pspice17.2安装教程
  10. 苹果服务器消息转发,好消息!微信语音也可以转发啦!不好的消息!目前苹果还不行!...
  11. Hook技术第二讲,之虚表HOOK
  12. 在Kubuntu14.04中安装小企鹅输入法
  13. FPS的瞄准准心的一种写法
  14. 数值计算笔记之非线性方程的求解(一)二分法
  15. 如何做召回dssm,fm,Mind,youtube等模型
  16. java没有舞台_不会偷懒的程序员不是好程序员!
  17. 敏捷与数字化转型银行_使敏捷人超越数字化
  18. HTML5特别篇——代码规范(2)
  19. 清华、北大、浙大的计算机课程资源集都在这里了(转载自 -- AI科技大本营)
  20. 电信手机信号测试软件,科技神器实现联通、电信、移动信号覆盖,亲测,信号杠杠滴!...

热门文章

  1. LDO + 稳压管的扩压电路试用笔记
  2. 华三交换机irf堆叠以及BFD检测配置
  3. RCNN系列论文学习:RCNN、FastRCNN、FaterRCNN、MaskRCNN(包含IOU、NMS介绍)
  4. c语言 signal作用,C语言中的signal函数
  5. 解决maya导出FBX出现问题。
  6. 对LMAX架构以及Event Sourcing模式的一些新思考和问题的记录
  7. UNI-APP,uni.scanCode扫码页面显示英文,uni.showActionSheet自带取消按钮显示英文问题的解决
  8. 如何写好 5000 行的 SQL 代码
  9. hello yocto
  10. r语言中怎样查看函数源代码