Chapter 2

Blockchain

Blockchain is a distributed ledger technology that allows for the creation of a decentralized network of computers that can store and share information in a secure.

怎么组织区块链相关的知识呢?

  • 区块链原理:技术原理、经济模型等,以及常见的主网
  • Wallet
  • Web3
  • 热点 (Hot / Trend)
    • BRC20

Reference

  • Blockchain Protocols - We research layer 1 blockchain protocols to provide the most accurate, up-to-date information on all of them and make this the easiest way for you to get to know and use them.

  • How does Bitcoin work? - 这是一个学习 Bitcoin 如何工作的网站,内容非常好

Abstract Account

Subsections of Blockchain

区块链概要

区块链原理

  • 双花问题
  • Markel Tree

交易模型

最基础的两个:UTXO 和 账户模型

Deep Understanding of Ethereum

深入理解以太坊

专有名词

EOA, Contract Accounts, Account state, Account nonce, World state, Transaction, Receipt, Block, Uncle block, Nonce, Gas, Gas price, Gas Price Oracle

Zero Knowledge Proof, EVM, Message, RLP, MPT (Merkle Patricia Tree), Patricia Trie, Merkle Tree, Whisper, Light Ethereum Subprotocol, Swarm, LLL, Sperpent, Mutan, Solidity, EIPs(ERC20, ERC721)

Uncle block

Uncle blocks (or Ommer) are created when two or more miners create blocks at nearly the same time. Only one block can be mined and accepted as canonical on the blockchain. The others are uncle blocks, which are not included but still provide a reward to their miners for the work done.

在比特币网络中,uncle block 是没有奖励的;但在以太坊网络中,uncle block 有奖励,并且一个区块最多能引用 2 个 uncle block;

  1. uncle block reward 是奖励给 uncle block 的矿工的,前提是被确认的区块引用了这个 uncle block
  2. 包含了 uncle block 的区块,会获得一个额外的奖励,这个奖励是 uncle block reward 的 1/32 主要作用是:提高网络安全性,提高矿工的积极性
  1. 以太坊为什么要设置区块的叔块奖励?
  2. There are two uncle rewards

在以太坊升级 PoS 后,uncle block 还有吗?

  1. Block header structure change under the Merge to Proof of Stake?
  2. How The Merge Impacts Ethereum’s Application Layer

总结下来,以太坊从 PoW 升级到 PoS 后,部分和 PoW 共识相关的字段就无用了,但是出于兼容和一致性的考虑,这些字段会被设置默认值,其中 uncle (Ommer) 是其中一项。

In order to minimize disruption to tooling and infrastructure, these fields are set to 0, or their data structure’s equivalent, rather than being entirely removed from the data structure. The full changes to block fields can be found in EIP-3675.

账户模型

以太坊中,账户分为:外部账户(EOAs)和合约账户(contract account)

参考

  1. 账户

EOAs

EOAs-外部账户(external owned accouts)是由人们通过私钥创建的账户。 是真实世界的金融账户的映射,拥有该账户私钥的任何人都可以控制该账户。 如同银行卡,到ATM机取款时只需要密码输入正确即可交易。 这也是人类与以太坊账本沟通的唯一媒介,因为以太坊中的交易需要签名, 而只能使用拥有私有外部账户签名。

外部账户特点总结:

  1. 拥有以太余额。
  2. 能发送交易,包括转账和执行合约代码。
  3. 被私钥控制。
  4. 没有相关的可执行代码。

合约账户

含有合约代码的账户。 被外部账户或者合约创建,合约在创建时被自动分配到一个账户地址, 用于存储合约代码以及合约部署或执行过程中产生的存储数据。 合约账户地址是通过SHA3哈希算法产生,而非私钥。 因无私钥,因此无人可以拿合约账户当做外部账户使用。 只能通过外部账户来驱动合约执行合约代码。

// 合约地址生成算法
// sender: 指交易的发起者的地址
// nonce: 指该交易的随机数
Keccak256(rlp([sender,nonce])[12:])

合约账户特点总结:

  1. 拥有以太余额。
  2. 有相关的可执行代码(合约代码)。
  3. 合约代码能够被交易或者其他合约消息调用。
  4. 合约代码被执行时可再调用其他合约代码。
  5. 合约代码被执行时可执行复杂运算,可永久地改变合约内部的数据存储。

账户抽象

在最近的 EIP 中提出了账户抽象的概念(关于账户抽象看 Account abstraction)

账户数据结构

type Account struct {
    Nonce    uint64
    Balance  *big.Int
    Root     common.Hash
    CodeHash []byte
}

Account Model Account Model

以太坊中的交易

指由一个外部账户转移一定资产给某个账户, 或者发出一个消息指令到某个智能合约

参考资料

  1. 以太坊技术与实现 - 交易
  2. 区块链架构之美 - EVM / Transaction
  3. 深入理解以太坊

Ethereum Node Architecture

在 The Merge 之后,以太坊节点有两部分组成:执行客户端 (EL, Execution Client) 和共识客户端 (CL, Consensus Client):

  • EL 负责交易处理,交易广播,状态管理以及对 EVM 的支持
    • Geth 是一种 EL 的实现,还有其他的实现,比如 Parity
  • CL 负责区块创建,区块广播和共识逻辑
  • EL 和 CL 之间的通信使用的是 Engine API, 基于本地的 JSON-RPC
    • CL 是在 The Merge 之后开始生效的,正式从 PoW 切换到 PoS

他们之间的关系如下图

几个规范需要关注

  1. Execution API
  2. Execution Specs
  3. Engine API
  4. Consensus Specs

EVM

Event logs

TODO

  1. https://ethereum.org/en/developers/docs/evm/

Storage Layouts in EVM

深入理解 EVM 的存储布局非常重要,因为它直接影响到合约的 gas 消耗,以及合约的安全性。

Solidity 中有三种内存类型:storage, memory, calldata

  1. memory 类型的变量和参数,用在函数的内部,只在函数执行期间存在,函数执行完毕后,内存被清空
  2. calldata 类型是在外部调用函数时,传递的参数,只读,不能修改
  3. storage 类型的变量,用来存储合约的状态,永久存在,直到合约被销毁

storage memory layout

每个合约都有自己的存储区域,它是一个可以持久化、读写的内存区域,合约只能访问自己的内存空间,不能访问其他合约的;合约的存储空间被划分为一个个的存储槽,每个槽的大小为 256 位,每个槽都有一个唯一的索引,从 0 开始,每个槽都可以存储一个 256 位的值,也就是说,每个槽都可以存储一个 uint256 类型的值,或者 32 个字节的值;并且 EVM 一次需要直接访问 32 字节的数据,槽的总数是 2^256 大小。

这个抽象非常类似虚拟内存,EVM 会记录每个合约中槽的使用情况,最开始槽是没有初始化的

字节序:大端和小端;在 EVM 中 bytes 和 string 使用的是大端;其他类型用的是小端

  1. 如果一个类型不足 32 bytes,会被填充到 32 bytes;但是填充会浪费内存空间,但是带来了 gas 的节约,因为读写成本更低
  2. 但是在某些情况下,会把紧邻的不足 32 bytes 的变量包装在一起,可以节省内存空间,但是会增加 gas 消耗,因为读写成本更高,需要做更多的位运算

References

Subsections of Deep Understanding of Ethereum

构建以太坊网络

测试网络

搭建私有以太坊私有网络

搭建私有网络

# 1. Clone go-ethereum
git clone https://github.com/ethereum/go-ethereum.git

# 2. Build go-ethereum, make sure you have installed go, version >= 1.19
cd go-ethereum && make all

# 3. Copy geth to $GOPATH/bin
cp build/bin/geth $GOPATH/bin

# 3. Create private network with two nodes
cd $HOME/priveth && mkdir node1 node2

# 4. Create accounts
# On node1
geth --datadir $HOME/priveth/node1 account new
# Password: foobar
# Account1 -> Public address of the key:   0x46eE6c1779eB3421Fc355132D867804894eFB1F9
# Account2 -> Public address of the key:   0x4B3Beee7E6E067080f71336fF118E76E53C63cA3

# Signer address -> 0x3D43ed16b178611C0aBA87a00Dd6b2655aa89287

# On node2
geth --datadir $HOME/priveth/node2 account new
# Public address of the key:   0x69b24ce645c0Bbdfcc0FE4297804b82F888DE8Fe

# 5. Create genesis config
geth --datadir $HOME/priveth/node1 init genesis.json
geth --datadir $HOME/priveth/node2 init genesis.json

# 6. Start bootnode, 在其中一个 node 启动即可
bootnode -genkey boot.key
bootnode -nodekey boot.key -addr :30305

# 6. Start 
# Start node1, locate to $HOME/priveth
geth --datadir node1 --port 30306 --bootnodes 'enode://3988b0cf1d24581e50d1a888e0c9588b1f7403477cf621f46c7add34b2bf786865601c4b6a07924e5f1eed250214192355b5ea5571e5887055e464ccb91dce3e@127.0.0.1:0?discport=30305' --networkid 12345 --unlock 0x3D43ed16b178611C0aBA87a00Dd6b2655aa89287 --password node1/walletpwd.txt --authrpc.port 8551 --http.vhosts=*

# Start node2
geth --datadir node2 --port 30307 --bootnodes 'enode://3988b0cf1d24581e50d1a888e0c9588b1f7403477cf621f46c7add34b2bf786865601c4b6a07924e5f1eed250214192355b5ea5571e5887055e464ccb91dce3e@127.0.0.1:0?discport=30305' --networkid 12345 --unlock 0x69b24ce645c0Bbdfcc0FE4297804b82F888DE8Fe --password node2/walletpwd.txt --authrpc.port 8552

# Attach
geth attach node1/geth.ipc

curl --data '{"jsonrpc":"2.0","method":"eth_getBalance", "params": ["0x3D43ed16b178611C0aBA87a00Dd6b2655aa89287", "latest"], "id":2}' -H "Content-Type: application/json" localhost:8551

genesis.json content

{
  "config": {
    "chainId": 12345,
    "homesteadBlock": 0,
    "eip150Block": 0,
    "eip155Block": 0,
    "eip158Block": 0,
    "byzantiumBlock": 0,
    "constantinopleBlock": 0,
    "petersburgBlock": 0,
    "istanbulBlock": 0,
    "muirGlacierBlock": 0,
    "berlinBlock": 0,
    "londonBlock": 0,
    "arrowGlacierBlock": 0,
    "grayGlacierBlock": 0,
    "clique": {
      "period": 5,
      "epoch": 30000
    }
  },
  "difficulty": "1",
  "gasLimit": "800000000",
  "extradata": "0x00000000000000000000000000000000000000000000000000000000000000003D43ed16b178611C0aBA87a00Dd6b2655aa892870000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  "alloc": {
    "3D43ed16b178611C0aBA87a00Dd6b2655aa89287": { "balance": "500000" },
    "69b24ce645c0Bbdfcc0FE4297804b82F888DE8Fe": { "balance": "500000" },
    "46eE6c1779eB3421Fc355132D867804894eFB1F9": { "balance": "300000" },
    "4B3Beee7E6E067080f71336fF118E76E53C63cA3": { "balance": "200000" }
  }
}

可以看这个官方教程来理解更多关于私有网络的搭建: Private network

Wallet

作为用户,Wallet 是区块链的入口;现阶段主流的 Wallet 类型有:HD Wallet,MultiSig wallet,Custodial wallet,Hardware wallet,Paper wallet,MPC Wallet 等。作为开发者,HD Wallet、MultiSig Wallet、MPC Wallet 是需要重点掌握的。

HD wallet

钱包是用来存钱的,在区块链中,我们的数字资产都会对应到一个账户地址上, 只有拥有账户的钥匙(私钥)才可以对资产进行消费(用私钥对消费交易签名)。

他们之间的关系如下:

wallet wallet

私钥通过椭圆曲线生成公钥, 公钥通过哈希函数生成地址,这两个过程都是单向的; 所以,数字钱包实际是一个管理私钥(生成、存储、签名)的工具,注意钱包并不保存资产,资产是在链上的。

生成私钥的本质是在 1 到 2^256 之间选一个数字:生成密钥的第一步也是最重要的一步,是要找到足够安全的熵源,即随机性来源,只要选取的结果是不可预测或不可重复的

HD, Hierarchical Deterministic, 叫做分层确定性钱包,是一种可以从单个种子(seed)衍生出一系列私钥的钱包结构,这些私钥都可以被根种子所控制,这样就可以用一个种子来管理多个账户。

在 Mastering Bitcoin 中,有一章是来介绍 Wallet 的,有必要看一下:Mastering Bitcoin - Wallet, 中文版看这里

HD 钱包允许我们使用一个密钥管理很多个衍生私钥,进而控制多个地址,bip32 规范中使用一种树形结构来进行管理。

HD Bip32 HD Bip32

关于 HD wallet 有一系列规范:

bip39

bip39 规范主要关于生成确定性密钥的助记码,使用一组预定义的单词来表示一串随机数,这样就可以方便的记忆和传输。主要包含两部分:生成助记码和从主机码转成二进制私钥种子。

我们来看看 bip39 的技术细节

如何生成助机词

  1. Create a random sequence (entropy) of 128 to 256 bits.
  2. Create a checksum of the random sequence by taking the first (entropy-length/32) bits of its SHA256 hash.
  3. Add the checksum to the end of the random sequence.
  4. Split the result into 11-bit length segments.
  5. Map each 11-bit value to a word from the predefined dictionary of 2048 words.
  6. The mnemonic code is the sequence of words.

随机熵和单词长度的关系:

Entropy (bits)Checksum (bits)Entropy + checksum (bits)Mnemonic length (words)
128413212
160516515
192619818
224723121
256826424

用助记词生成种子

  1. The first parameter to the PBKDF2 key-stretching function is the mnemonic produced from step 6.
  2. The second parameter to the PBKDF2 key-stretching function is a salt. The salt is composed of the string constant “mnemonic” concatenated with an optional user-supplied passphrase string.
  3. PBKDF2 stretches the mnemonic and salt parameters using 2048 rounds of hashing with the HMAC-SHA512 algorithm, producing a 512-bit value as its final output. That 512-bit value is the seed.

参考实现:

源码解析

func NewEntropy(bitSize int) ([]byte, error) {
    // 验证位数在 128 到 256 之间,并且是 32 的倍数
	if err := validateEntropyBitSize(bitSize); err != nil {
		return nil, err
	}

	entropy := make([]byte, bitSize/8)
    // 这里使用的是 crypto/rand 包,而不是 math/rand 包,因为 math/rand 包是伪随机数生成器
    // 这里显然需要一个真随机数生成器
	_, _ = rand.Read(entropy)

	return entropy, nil
}

// 用法
entropy, _ := bip39.NewEntropy(256)
mnemonic, _ := bip39.NewMnemonic(entropy)
seed := bip39.NewSeed(mnemonic, "hello")

总结一下:bip39 的主要作用就是把随机生成的 seed 转成助记词,方便记忆、传输等,不管是导入、导出还是备份都非常方便,并且从助记词也可以还原出 seed。

bip32 & bip44

从种子生成钱包

上面了解了 bip39,通过 bip39 我们可以获取一个助记词序列,它本质上是一个随机数,这个随机数被用来当作 seed 来生成私钥。bip32 规范主要是定义了从 seed 生成私钥的过程,它定义了一种树形结构来管理私钥,这种树形结构的每个节点都有一个索引,这个索引可以用来生成子节点,这样就可以方便的管理多个私钥。

关于 extended key 也可以看这里:extended keys

参考实现

Keys 和 Addresses

在上面介绍的几个 bip 规范中,其实少了一些细节,比如私钥如何生成公钥?公钥如何生成地址?不同的链使用的算法都一样吗?…

关于这些问题可以参考这篇文章:Mastering Bitcoin - Keys and Addresses

ChainPrivate Key -> Public KeyCompressed Public Key ?Public Key -> AddressAddress Format
BTCECDSA (ecc Secp256k1)YesSHA256 + RIPEMD160Base58
ETHECDSA (ecc Secp256k1)YesKeccak256Hex
BNBECDSA (ecc Secp256k1)YesKeccak256Bech32

对于地址生成算法,不同的链不太一样

  • Bitcoin 采用 SHA256 + RIPEMD160, 然后再进行 Base58 编码
  • Ethereum 采用 Keccak256, 然后再进行 Hex 编码
    • 对公钥做 Keccak-256 哈希运算,然后取最后的 40 位 16 进制字符

实现一个支持多链的 HD 本地钱包

一些想法

  • 这个本地钱包,我们暂时叫他 hdkms,后面都这么叫
  • hdkms 支持管理多个钱包,可以通过指定 name 来区分不同的 hd wallet
    • wallet1 -> mnemonic -> seed -> master private key -> local keystore file1
    • wallet2 -> mnemonic -> seed -> master private key -> local keystore file2
  • hdkms 支持导入助记词、私钥
  • hdkms 支持导出助记词、私钥
  • hdkms 支持生成地址,并且可以指定 index
  • hdkms 可以查看 address 的余额
  • hdkms 支持多链
    • 比如 Bitcoin, Ethereum, Binance Smart Chain etc.
  • hdkms 可以离线签署交易,生成签名后的交易数据
    • 支持 native token 的 transfer
    • 支持 erc20
    • 支持 NFT
    • 支持任意类型的合约
  • hdkms 可以在线广播交易
    • broadcast tx
    • 查询 tx 状态
  • hdkms 一些高级功能
    • 支持 event 监听
    • 支持动态手续费,比如 gas fee,network fee 等
# 考虑怎么使用
#  1. 提供命令行工具,比如 hd-kms 

# 创建钱包
hdkms wallet generate --wallet-name test1 --passphase 123456

hdkms wallet import --wallet-name test1 --mnemonic "" --passphase 123456

$ hdkms wallet export --wallet-name test1
$ {"mnemonic": "xxx", "passphase": ""}

TODOs

  • 搭建起项目的基本框架
    • 搞定 cli 及其扩展子命令
    • 支持多个子命令的调用,比如 hdkms wallet generate, hdkms wallet list …
  • 支持 Ethereum
    • generate wallet
    • mnemonic
    • store mnemonic or private key to local keystore file
    • get address by index
    • query balance by address

Smart Contracts

这里主要是关于智能合约。

  1. 搞明白 hardhat
  2. 用 hardhat 和 openzeplin 写 erc20 合约,部署、运行等

开发工具、框架

工欲善其事,必先利其器。

开发工具

  • Remix - 一个基于浏览器的 IDE,可以用来编写、编译、部署智能合约。

    • Remix 内部也提供了一个交互式的学习 Solidity 的教程
  • Visiual Studio Code & Solidity Extension - 一个 VSCode 的插件,可以用来编写、编译、部署智能合约。

  • Jetbrains 系列 IDE & Solidity Extension - 一个 Jetbrains 系列 IDE 的插件,可以用来编写、编译、部署智能合约。

开发框架

  • Hardhat
  • OpenZeplin
  • Truffle
  • ether.js
  • Ganache
  • TypeScript & JavaScript & ES6 & Node.js

学习资源

语法

变量,数据类型和可见性

Solidity 有三种变量类型:

在 Solidity 的全局命名空间中,存在一些特殊的变量,可以在任何地方访问,不需要声明;主要提供一些区块链信息和通用的处理函数。

  1. 区块和交易相关:block.basefee, msg.sender, msg.data (calldata), msg.value, msg.sig, tx.gasprice, tx.origin, block.gaslimit, block.number, block.timestamp, block.difficulty, block.coinbase, block.chainid, blockhash(uint blockNumber) returns (bytes32), gasleft() returns (uint256)
  2. abi 编解码函数
  3. 错误处理:assert, require, revert
  4. 数学和加密函数:keccak256, sha256, ripemd160, ecrecover, addmod, mulmod 等等
  5. 地址类型:<address>.balance, <address>.code 等等
  6. 合约相关:this, selfdestruct(address recipient)

storage, memory, calldata

Solidity 中有几种变量的存储类型

  1. memory: 一些被临时使用的变量,比如函数参数、本地变量、在执行过程中动态创建的数组;一旦函数执行完毕,这些变量就会被销毁。
  2. storage
  3. calldata

函数 (function)

Payable function

合约安全

EIP

ERC20

我们首先需要熟悉 ERC20 标准协议,可以看 ERC20 Token Standard,非常重要。ERC20 标准规范可以获得更好的重用性,无论是对 web3 应用还是交易所。

接着可以了解 ERC20 的标准实现,可以看 OpenZeplin Contracts - ERC20 部分,代码在这里

Web3

有理由相信 Web3.0 是下一代的价值互联网。

Web3 Architecture Web3 Architecture

References

这里列出了一些 Web3.0 的相关资料,以供参考。

项目

  • tokenlon - 值得信赖的去中心化交易结算协议

Subsections of Web3

Web3 基础设施及工具

区块链的基础设施是非常重要的,这为开发者使用区块链、开发应用等提供了基础的支持,可以大大节省时间和资源。

工具篇

TODO

基础设施篇

  • nodereal - Fastest, Reliable & Instant Web3 Infrastructure
  • quicknode - The Blockchain Development Platform, Learn. Build. Scale.
  • wyre - INFRASTRUCTURE FOR THE NEW ECONOMY, We put the power in your hands with easy-to-use APIs that you can use to delight your users with simple and secure payments and digital asset experiences