diff --git a/address_ch.md b/address_ch.md new file mode 100644 index 0000000..8d1d641 --- /dev/null +++ b/address_ch.md @@ -0,0 +1,48 @@ +# Nebulas 地址设计 + +Nebulas 地址系统是精心设计的。正如你将在下面看到的,账户和智能合约地址都是 “n” 开头的字符串,可以认为它们就是我们信仰的 Nebulas(NAS)。 + +## 账户地址 + +与比特币和以太坊类似,Nebulas 也采用了椭圆曲线算法作为 Nebulas 账户的加密算法。Nebulas 账户的地址是通过**公钥**计算出来的,而公钥则通过用户**密码短语**进行加密的**私钥**计算而来。此外,我们也有校验设计,旨在防止用户因为输入几个错误字符而将 _Nas_ 意外发送给错误的用户账户。 + +具体的计算公式如下: +``` +1. content = ripemd160(sha3_256(public key)) + length: 20 bytes + +--------+--------+------------------+ +2. checksum = sha3_256( | 0x19 + 0x57 | content | )[:4] + +--------+--------+------------------+ + length: 4 bytes + + +--------+---------+-----------------+------------+ +3. address = base58( | 0x19 | 0x57 | content | checksum | ) + +--------+---------+-----------------+------------+ + length: 35 chars +``` + +**0x57** 是一个单字节“类型码”,表示账户地址,**0x19** 则是一个固定的单字节“填充”。 + +在这个阶段,Nebulas 只采用了普通的比特币 [base58](https://en.wikipedia.org/wiki/Base58) 编码模式。一个有效的地址示例:_n1TV3sU6jyzR4rJ1D7jCAmtVGSntJagXZHC_ + +## 智能合约地址 + +计算合约地址和计算账户地址略有不同,合约发送方的密码短语不是必需的,地址和 nonce 才是必需。有关更多信息,请查看 [smart contract](https://github.com/nebulasio/wiki/blob/master/tutorials/%5BEnglish%5D%20Nebulas%20101%20-%2003%20Smart%20Contracts%20JavaScript.md) 和 [rpc.sendTransaction](https://github.com/nebulasio/wiki/blob/master/rpc.md#sendtransaction)。计算公式如下: + +``` +1. content = ripemd160(sha3_256(tx.from, tx.nonce)) + length: 20 bytes + +--------+--------+------------------+ +2. checksum = sha3_256( | 0x19 | 0x58 + content | )[:4] + +--------+--------+------------------+ + length: 4 bytes + + +--------+---------+-----------------+------------+ +3. address = base58( | 0x19 | 0x58 | content | checksum | ) + +--------+---------+-----------------+------------+ + length: 35 chars +``` + +**0x58** 是一个单字节“类型码”,表示账户地址,**0x19** 则是一个固定的单字节“填充”。 + +一个有效的地址示例: _n1sLnoc7j57YfzAVP8tJ3yK5a2i56QrTDdK_ \ No newline at end of file diff --git a/blockchain_ch.md b/blockchain_ch.md new file mode 100644 index 0000000..7a73ecf --- /dev/null +++ b/blockchain_ch.md @@ -0,0 +1,174 @@ +# 区块链 + +## 模型 + +Nebulas 使用了账户模型来替代 UTXO 模型。 +交易的执行将消耗 gas。 + +## 数据结构 + +``` txt +区块结构 ++---------------+----------------+--------------+ +| blockHeader | transactions | dependency | ++---------------+----------------+--------------+ +blockHeader: 头部信息 +transactions: 交易序列 +dependency: 交易之间的依赖关系 + +区块头部结构 ++-----------+--------+--------------+------------+-------------+-------+--------+ +| chainid | hash | parentHash | coinbase | timestamp | alg | sign | ++-----------+--------+--------------+------------+-------------+-------+--------+ ++-------------+-----------+--------------+-----------------+ +| stateRoot | txsRoot | eventsRoot | consensusRoot | ++-------------+-----------+--------------+-----------------+ +chainid: 该区块所属的链id +hash: 区块哈希 +parentHash: 父区块哈希 +coinbase: 接受挖矿奖励的账户 +timestamp: 1970年1月1日以来经过的纳秒数 +alg: 签名算法的类型 +sign: 区块哈希的签名 +stateRoot: 账户状态的根哈希 +txsRoot: 交易状态的根哈希 +eventsRoot: 事件状态的根哈希 +consensusRoot: 共识状态,包含提议者和当前 dynasty 的验证者 + + +交易结构 ++-----------+--------+--------+------+---------+---------+-------------+ +| chainid | hash | from | to | value | nonce | timestamp | ++-----------+--------+--------+------+---------+---------+-------------+ ++--------+------------+------------+ +| data | gasPrice | gasLimit | ++--------+------------+------------+ +chainid: 该区块所属的链id +hash: 交易哈希 +from: 发送者的钱包地址 +to: 接收者的钱包地址 +value: 转账数值 +nonce: 交易nonce +timestamp: 1970年1月1日以来经过的纳秒数 +alg: 签名算法的类型 +sign: 交易哈希的签名 +data: 交易数据,包含了交易类型(普通转账/部署智能合约/调用智能合约)和 payload +gasPrice: 交易消耗的 gas 单价 +gasLimit: 交易可消耗的最大 gas +``` + +## 区块链更新 + +在我们看来,**区块链**只需要关心如何处理新区块,使其安全且高效地增长。更重要的是,**区块链**只能从下面两个渠道获取新区块。 + +### 来自网络的新区块 + +由于网络延迟不稳定,我们无法确定任何新接收的块可以直接连上到我们当前的**链**。因此,我们需要**区块池**来缓存新区块。 + +### 来自本地矿工的新区块 + +首先,我们需要**交易池**来缓存来自网络的交易。然后,我们等待本地**共识**组件创建一个新的区块,例如 DPoS。 + +无论新区块来自哪里,我们都使用相同的步骤来处理,如下所示: + +![](resources/blockpool.png) + +## 世界状态 + +每一个区块都包含当前的世界状态,世界状态由下列四个状态组成。它们都作为[梅克尔树](./merkle_trie.md)来进行维护。 + +### 账户状态 + +当前区块的所有账户都存储在账户状态里。 +账户分为普通账户和智能合约两种。 + +普通账户包括: + +- **钱包地址** +- **余额** +- **nonce**:账户的 nonce,它将以 1 为增量递增 + +智能合约账户包括: + +- **合约地址** +- **余额** +- **出生地**:部署合约的交易哈希 +- **变量**:包含合约中所有的变量值 + +### 交易状态 + +所有提交到链上的交易都存储在交易状态里。 + +### 事件状态 + +当执行交易的时候,会触发很多事件。 +由链上交易触发的所有事件都被存储在事件状态里。 + +### 共识状态 + +共识算法的上下文存储在共识状态里。 + +对于 DPoS 来说,共识状态包括: + +- **timestamp**:当前时间戳 +- **proposer**:当前提议者 +- **dynasty**:当前 dynasty 的验证者 + +### 序列化 + +考虑到以下好处,我们选择了协议缓冲技术来进行常规序列化: + +- 大规模证明。 +- 效率。它省略了关键字,并且使用 varints 编码。 +- 多类型及多语言客户端支持。易于使用的 API。 +- Schema 是良好的通信格式。 +- Schema 适用于版本控制或扩展,即添加新的消息字段,或废弃一个没用的字段。 + +特别是,为了可读性,我们在智能合约里使用 json 进行序列化,而不是使用 protobuf。 + +## 同步 + +有时我们接收到的新区块高度会比它当前的尾部区块高很多。当这种差距发生的时候,我们就需要从对等节点那儿同步区块,从而赶上它们。 + +Nebulas 提供了两种方法来从对等节点同步区块:Chunks Downloader 和 Block Downloader。如果差值大于 32 个区块,我们将选择 Chunks Downloader 以块的形式来下载大量的区块。否则,我们会选择 Block Downloader 来逐个下载区块。 + +### Chunks Downloader + +块是 32 个连续块的集合。Chunks Downloader 允许我们每次最多在当前尾部区块后面下载 10 个块。这种基于块的机制可以帮助我们减少网络包的数量,同时实现更好的安全性。 + +程序逻辑如下: + +```txt +1. A 将其尾部区块发送给 N 个远程对等节点。 +2. 远程对等节点找到包含 A 尾部区块的块 C。 + 然后它们将发送 10 个块的头部,包括块 C 和 9 个 C 的后续块,以及 10 个头部的哈希值 H。 +3. 如果 A 收到大于 N/2 数量的相同哈希值 H,A 就将同步 H 代表的那些块。 +4. 如果 A 取出了所有 H 代表的块并成功将它们上链,则跳转到 1。 +``` + +在 1~3 的步骤里,我们使用了多数决策来确认标准链上的块。然后我们在步骤 4 里以块的形式下载这些区块。 + +**注意:**`ChunkHeader` 包含了 32 个区块哈希的数组和这个数组的哈希。`ChunkHeaders` 则包含了 10 个 `ChunkHeaders` 的数组和这个数组的哈希。 + +下面是这个同步过程的图表: + +![](resources/the-diagram-of-sync-process.png) + +### Block Downloader + +当本地链和标准链之间的差距长度小于 32 时,我们将使用 Block Downloader 来逐个下载缺失的区块。 + +程序逻辑如下: + +```txt +1. C 将最新的区块 B 传给 A,且 A 发现 B 的高度大于当前尾部区块高度。 +2. A 将 B 区块的哈希发回 C,从而下载 B 的父区块。 +3. 如果 A 成功接收了 B 的父区块 B',A 将尝试将 B' 连接到 A 当前的尾部区块上。 + 如果再次失败,A 将回到步骤 2 并继续下载 B' 的父区块。否则,完成。 +``` + +这个过程将不断重复,直到 A 追赶上标准链。 + +下面是这个同步过程的图表: + +![](resources/the-diagram-of-download-process.png) \ No newline at end of file