├── .gitignore ├── 01-computer-basics └── readme.md ├── 02-data-structures-and-algorithms └── readme.md ├── 03-nodejs ├── EnReadme.md └── readme.md ├── 04-consensus-algorithm └── readme.md ├── 05-ethereum ├── EnReadme.md └── readme.md ├── 06-solidity ├── EnReadme.md └── readme.md ├── 07-golang └── readme.md ├── 08-rust └── readme.md ├── 09-layer2 ├── L2 readme.md ├── eigen readme └── readme.md ├── 10-zkp └── readme.md ├── 11-cosmos └── readme.md ├── 12-wallet └── README.md ├── 13-product └── ReadMe.md ├── 14-database ├── NoSql.md ├── ReadMe.md └── Redis.md ├── 15-java └── ReadMe.md ├── 16-bitcoin └── ReadMe.md ├── 17-cosmos └── ReadMe.md ├── 18-cryptography └── ReadMe.md ├── 19-open └── ReadMe.md ├── LICENSE ├── README.md └── images ├── 2.png ├── 3.png ├── 4.png ├── 5.png ├── 6.png └── pic_chang.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | .idea 12 | -------------------------------------------------------------------------------- /01-computer-basics/readme.md: -------------------------------------------------------------------------------- 1 | # 计算机基础 2 | 3 | ### 1.计算机网络的七层模型 4 | 5 | - 第一层:物理层(Physical Layer) 6 | - 功能:物理层负责在网络设备之间传输原始比特流(0和1)。它是OSI模型的最底层,关注物理连接和信号传输。 7 | - 主要任务: 8 | - 定义物理接口和传输介质(如网线、光纤、无线电波)。 9 | - 规定电气特性(如电压、频率)和信号传输方式。 10 | - 处理比特的传输、同步和物理连接的建立与断开。 11 | - 例子:网卡、集线器(Hub)、电缆(如双绞线、同轴电缆)、光纤、连接器(如RJ45)。 12 | - 协议/技术:以太网(Ethernet)的物理层标准、USB、Wi-Fi的无线信号。 13 | 14 | 15 | - 第二层:数据链路层(Data Link Layer) 16 | - 功能:数据链路层负责在相邻节点之间可靠地传输数据帧(Frame),并处理错误检测与纠正。 17 | - 主要任务: 18 | - 将比特流封装成数据帧,添加帧头和帧尾。 19 | - 实现物理地址(MAC地址)寻址。 20 | - 检测和纠正传输中的错误(如通过校验和)。 21 | - 控制对共享介质的访问(介质访问控制,MAC)。 22 | - 子层: 23 | - LLC(逻辑链路控制):提供流量控制和错误处理。 24 | - MAC(介质访问控制):处理物理寻址和介质访问。 25 | - 例子:交换机(Switch)、网桥(Bridge)。 26 | - 协议:以太网(IEEE 802.3)、Wi-Fi(IEEE 802.11)、PPP(点对点协议)。 27 | 28 | 29 | - 第三层:网络层(Network Layer) 30 | - 功能:网络层负责在不同网络之间路由和转发数据包(Packet),实现端到端的通信。 31 | - 主要任务: 32 | - 逻辑寻址(如IP地址)。 33 | - 数据包的路由选择(确定数据从源到目的地的路径)。 34 | - 处理网络分片与重组(当数据包过大时)。 35 | - 例子:路由器(Router)。 36 | - 协议:IP(IPv4、IPv6)、ICMP(互联网控制消息协议)、IPSec。 37 | 38 | 39 | - 第四层:传输层(Transport Layer) 40 | - 功能:传输层负责提供端到端的数据传输服务,确保数据的可靠性和顺序。 41 | - 主要任务: 42 | - 分段与重组:将上层数据分成小块并在接收端重组。 43 | - 流量控制:防止发送端过载接收端。 44 | - 错误控制:检测和重传丢失或损坏的数据。 45 | - 建立和终止连接(如TCP的三次握手、四次挥手)。 46 | - 例子:TCP(传输控制协议,面向连接,可靠)、UDP(用户数据报协议,无连接,快速但不可靠)。 47 | - 协议:TCP、UDP。 48 | 49 | 50 | - 第五层:会话层(Session Layer) 51 | - 功能:会话层负责建立、管理和终止应用程序之间的会话。 52 | - 主要任务: 53 | - 会话的创建、维护和同步。 54 | - 处理会话中断后的恢复(某些协议支持检查点恢复)。 55 | - 区分多个会话(如区分同一用户打开的多个网页)。 56 | - 例子:远程登录(如Telnet)、文件传输协议的会话管理。 57 | - 协议:NetBIOS、RPC(远程过程调用)。 58 | 59 | 60 | - 第六层:表示层(Presentation Layer) 61 | - 功能:表示层负责数据格式的转换、加密和压缩,确保应用程序能够正确理解接收到的数据。 62 | - 主要任务: 63 | - 数据格式转换(如将文本从ASCII转为EBCDIC)。 64 | - 数据加密与解密(如SSL/TLS)。 65 | - 数据压缩与解压缩(减少传输数据量)。 66 | - 例子:JPEG、GIF(图像格式)、MPEG(视频格式)、SSL/TLS(安全传输)。 67 | - 协议:TLS、JPEG、XML。 68 | 69 | 70 | - 第七层:应用层(Application Layer) 71 | - 功能:应用层是用户与网络的直接接口,为应用程序提供网络服务。 72 | - 主要任务: 73 | - 提供用户界面和网络服务(如发送邮件、浏览网页)。 74 | - 处理应用程序之间的通信需求。 75 | - 例子:浏览器、电子邮件客户端、文件传输工具。 76 | - 协议:HTTP/HTTPS(网页)、FTP(文件传输)、SMTP(邮件发送)、POP3/IMAP(邮件接收)、DNS(域名解析)。 77 | 78 | ### 2.OSI模型的特点与工作流程 79 | 80 | - 分层设计:每层独立工作,互不干扰,上层依赖下层服务。 81 | - 通信过程: 82 | - 发送端:数据从应用层向下传递,每层添加头部信息(封装),直到物理层发送比特流。 83 | - 接收端:数据从物理层向上解封装,每层剥离头部,最终到达应用层。 84 | - 实际应用:OSI是理论模型,现实中常用的是TCP/IP模型(将七层简化为四层:应用层、传输层、互联网层、链路层)。 -------------------------------------------------------------------------------- /02-data-structures-and-algorithms/readme.md: -------------------------------------------------------------------------------- 1 | # 数据结构与算法 2 | 3 | ## 1.Merkle 树 4 | 5 | Merkle树(Merkle Tree,也叫哈希树)是一种基于哈希函数的树形数据结构,常用于分布式系统、区块链技术和数据验证场景中。它由Ralph 6 | Merkle在1979年提出,旨在高效地验证数据的完整性和一致性。以下是对Merkle树的详细描述,包括其结构、工作原理、特点及应用。 7 | 8 | ### 1.1 Merkle树的结构 9 | 10 | Merkle树是一种二叉树(也可以扩展为多叉树),其节点分为以下类型: 11 | 12 | #### 1.1.1 叶节点(Leaf Nodes): 13 | 14 | - 存储原始数据的哈希值。 15 | - 原始数据(如交易记录、文件块)经过哈希函数(如SHA-256)计算后生成固定长度的哈希值,作为叶节点内容。 16 | 17 | #### 1.1.2 非叶节点(Non-Leaf Nodes): 18 | 19 | - 存储其子节点的哈希值。 20 | - 每个非叶节点的哈希值是通过对其两个子节点的哈希值进行拼接(concatenation),然后再次哈希计算得到的。 21 | 22 | #### 1.1.3 根节点(Root Node): 23 | 24 | - 树的顶层节点,称为Merkle根(Merkle Root)。 25 | - 它代表了整棵树所有数据的唯一标识。 26 | 27 | ##### 构造过程示例 28 | 29 | - 假设有4个数据块:D1, D2, D3, D4。 30 | - 1.计算每个数据块的哈希值: 31 | ``H1 = Hash(D1)`` 32 | ``H2 = Hash(D2)`` 33 | ``H3 = Hash(D3)`` 34 | ``H4 = Hash(D4)`` 35 | - 2.两两配对计算中间节点: 36 | ``H12 = Hash(H1 + H2)`` 37 | ``H34 = Hash(H3 + H4)`` 38 | - 3.计算根节点: 39 | ``Merkle Root = Hash(H12 + H34)`` 40 | - 如果数据块数量为奇数,最后一个块会单独处理(有时复制自身配对)。 41 | 42 | ### 1.2 Merkle树的工作原理 43 | 44 | Merkle树的运行依赖于哈希函数的单向性和抗碰撞性。以下是其核心机制: 45 | 46 | - 数据分块与哈希: 47 | - 将大数据分成小块(如区块链中的交易),对每块计算哈希值。 48 | - 自底向上构建: 49 | - 从叶节点开始,两两配对计算父节点的哈希值,逐层向上,直到生成Merkle根。 50 | - 验证过程: 51 | - 要验证某个数据块是否属于树,只需提供从该叶节点到根节点的路径上的哈希值(称为Merkle路径或证明)。 52 | - 通过路径上的哈希值重新计算,检查是否能得到相同的Merkle根。 53 | 54 | #### 验证示例 55 | 56 | - 假设验证D1是否在树中: 57 | - 提供Merkle路径:H2(D1的兄弟节点)、H34(H12的兄弟节点)。 58 | - 计算: 59 | - H12’ = Hash(H1 + H2) 60 | - Root’ = Hash(H12’ + H34) 61 | - 若Root’等于Merkle根,则D1属于该树。 62 | 63 | ### 1.3 Merkle树的特点 64 | 65 | - 高效性: 66 | - 验证复杂度为O(log n),只需提供路径上的哈希值,而无需整个树。 67 | - 适合处理大规模数据(如区块链中的交易)。 68 | - 完整性验证: 69 | - Merkle根是所有数据的“指纹”,任何数据块的改变都会导致根值变化。 70 | - 可扩展性: 71 | - 支持动态添加或删除数据,只需局部更新树结构。 72 | - 分布式友好: 73 | - 各节点只需存储部分数据和Merkle根即可验证全局一致性。 74 | 75 | ### 1.4 Merkle树的应用 76 | 77 | - 区块链: 78 | - 比特币:每个区块的交易被组织成Merkle树,Merkle根存储在区块头中,用于快速验证交易是否包含在区块中。 79 | - 以太坊:使用Merkle Patricia Tree(Merkle树的变种)管理状态、交易和收据。 80 | - 数据同步: 81 | - 在分布式系统中(如IPFS),Merkle树用于比较和同步文件块。 82 | - 文件完整性校验: 83 | - 验证下载文件是否被篡改(如Git使用类似结构)。 84 | - 证书透明性: 85 | - 用于证明SSL证书的有效性(如Google的Certificate Transparency)。 86 | 87 | ### 1.5 Merkle树的优缺点 88 | 89 | - 优点: 90 | - 高效验证:只需少量数据即可验证完整性。 91 | - 抗篡改:哈希函数的特性保证篡改会被检测。 92 | - 节省空间:轻客户端(如SPV节点)只需存储Merkle根和路径。 93 | - 缺点: 94 | - 构建成本:初始构造需要对所有数据进行哈希计算。 95 | - 更新复杂性:添加或删除数据时,需重新计算部分路径和根。 96 | - 依赖哈希函数安全性:若哈希函数被攻破(如SHA-1),Merkle树的安全性会受影响。 97 | 98 | ### 1.6 变种与扩展 99 | 100 | - Merkle Patricia Tree: 101 | - 以太坊使用的优化版本,结合了前缀树(Trie)和Merkle树,适合键值对存储。 102 | - Binary Merkle Tree: 103 | - 标准的二叉树形式。 104 | - Sparse Merkle Tree: 105 | - 用于稀疏数据集,优化存储效率。 106 | 107 | ### 1.7 总结 108 | 109 | Merkle树是一种强大而灵活的数据结构,通过哈希和树形组织实现了高效的数据验证和一致性检查。它在区块链、分布式存储和数据校验中扮演了核心角色。其核心思想是将大量数据的验证简化为对Merkle根和少量路径的检查,极大提升了效率和安全性。 110 | 111 | ## 2. 稀疏 Merkle树 112 | 113 | 稀疏Merkle树(Sparse Merkle 114 | Tree,简称SMT)是Merkle树的一种变种,专门设计用于处理稀疏数据集(即键值对中大多数键没有关联的值,或者值为默认值的情况)。它结合了Merkle树的哈希验证特性和高效的键值存储能力,广泛应用于区块链(如以太坊的账户状态管理)和分布式系统中。以下是对稀疏Merkle树的详细描述,包括其结构、原理、特点及应用。 115 | 116 | ### 2.1 稀疏Merkle树的结构 117 | 118 | 稀疏Merkle树是一个完全二叉树,其设计目标是高效存储和验证稀疏键值对。它的结构特点如下: 119 | 120 | - 固定高度: 121 | - 树的深度(高度)由键的位数(通常是哈希值的长度)决定。例如,若键是256位哈希值,则树高为256。 122 | - 树的叶节点数量固定为2^h(h为树高),即使大部分叶节点可能是空的。 123 | - 键和路径: 124 | - 键(Key)通常是数据的哈希值,决定了数据在树中的位置。 125 | - 键的每一位(0或1)表示从根到叶的路径方向(0为左子树,1为右子树)。 126 | - 叶节点: 127 | - 存储实际的键值对(Key-Value Pair),或者表示“空值”(默认值,通常是0或空哈希)。 128 | - 非空叶节点记录具体数据(如账户余额),空叶节点记录默认值。 129 | - 非叶节点: 130 | - 由其子节点的哈希值计算得出,类似于标准Merkle树。 131 | - 如果某个子树的所有叶节点都为空,则该子树可以用一个默认哈希值表示(通常是全0的哈希)。 132 | - Merkle根: 133 | - 树的顶层哈希值,代表整个数据集的状态。 134 | 135 | #### 结构示例 136 | 137 | 假设键是4位长度(树高为4),可能的叶节点有16个(2^4)。键“1010”对应的路径是从根节点开始:右(1)、左(0)、右(1)、左(0),定位到第10个叶节点(从0计数)。若只有少数键有值(如“1010”有值,其他为空),树仍保持完整结构,但大多数节点用默认值填充。 138 | 139 | ### 2.2 稀疏Merkle树的工作原理 140 | 141 | 稀疏Merkle树的核心思想是通过固定结构和高效率的默认值处理,优化稀疏数据的存储和验证。 142 | 143 | - 数据插入: 144 | - 根据键的二进制位,沿着路径找到对应的叶节点。 145 | - 将值写入该叶节点,更新路径上所有父节点的哈希值,直到根节点。 146 | - 未写入的叶节点保持默认值(通常是0或空哈希)。 147 | - 数据验证: 148 | - 要证明某个键值对是否存在,提供从该叶节点到根的Merkle路径(包括兄弟节点的哈希值)。 149 | - 通过路径重新计算根哈希,验证是否与存储的Merkle根一致。 150 | - 空值优化: 151 | - 如果某个子树的所有叶节点为空,则该子树的哈希值是一个预计算的默认值,无需存储整个子树。 152 | - 这种优化极大减少了存储需求。 153 | 154 | #### 工作示例 155 | 156 | - 数据:键“1010” => 值“42”。 157 | - 树高4,叶节点16个。 158 | - 路径:根 -> 1(右)-> 0(左)-> 1(右)-> 0(左)。 159 | - 更新: 160 | - 叶节点“1010”存储“42”的哈希。 161 | - 沿路径更新父节点哈希,最终得到新Merkle根。 162 | - 验证: 163 | - 提供路径上的兄弟节点哈希,重新计算根哈希。 164 | 165 | ### 2.3 稀疏Merkle树的特点 166 | 167 | - 高效存储: 168 | - 通过默认值优化,大量空节点无需显式存储,只需计算默认哈希。 169 | - 高效证明: 170 | - 验证复杂度为O(log n),与树高成正比。 171 | - 支持零知识证明(如证明某个键不存在)。 172 | - 固定结构: 173 | - 树的大小和结构固定,不随数据量变化,便于并行处理和一致性检查。 174 | - 稀疏性适应: 175 | - 特别适合键空间很大但实际数据稀疏的场景。 176 | 177 | ### 2.4 稀疏Merkle树的优势与劣势 178 | 179 | - 优势: 180 | - 空间效率:通过默认值压缩空子树,存储成本远低于标准Merkle树。 181 | - 非存在性证明:能高效证明某个键没有值(只需证明对应叶节点是默认值)。 182 | - 一致性:固定结构便于分布式系统中各节点达成共识。 183 | - 劣势: 184 | - 初始开销:即使数据量很少,树的高度由键长度决定,可能导致较高的计算成本。 185 | - 更新复杂性:每次插入或修改需更新整条路径上的哈希。 186 | 187 | ### 2.5 稀疏Merkle树的应用 188 | 189 | - 区块链状态管理: 190 | - 以太坊:使用稀疏Merkle树(结合Patricia Trie)存储账户状态。每个账户的地址(160位)作为键,余额和nonce等作为值。 191 | - 稀疏性:以太坊有2^160个可能的地址,但实际账户远少于此,SMT能高效处理。 192 | - 零知识证明: 193 | - 在 zk-SNARKs 或 zk-STARKs 中,SMT用于生成证明,验证数据的存在或不存在。 194 | - 分布式数据库: 195 | - 用于一致性验证和数据同步(如Hyperledger Fabric)。 196 | - 文件系统: 197 | - 在分布式文件存储(如IPFS变种)中,验证稀疏文件块的完整性。 198 | 199 | ### 2.6 稀疏Merkle树与标准Merkle树的区别 200 | 201 | | 特性 | 标准Merkle树 | 稀疏Merkle树 | 202 | |:------:|:----------:|:-------------------:| 203 | | 树结构 | 动态,根据数据量构建 | 固定,完全二叉树 | 204 | | 叶节点数量 | 与数据量相同 | 固定(2^h,h为键长度) | 205 | | 空值处理 | 无特殊优化 | 默认值优化,节省空间 | 206 | | 适用场景 | 数据密集型 | (如交易列表) 稀疏数据(如账户状态) | 207 | | 非存在性证明 | 不支持或效率低 | 支持且高效 | 208 | 209 | ### 2.7 实现要点 210 | 211 | 以下是稀疏Merkle树实现的关键步骤(以伪代码为例): 212 | 213 | ``` 214 | # 初始化树 215 | tree_height = 256 # 假设键为256位 216 | default_hash = hash("0") # 空节点的默认哈希 217 | 218 | # 插入键值对 219 | def insert(key, value): 220 | path = binary(key) # 将键转为二进制路径 221 | leaf = hash(value) # 计算值的哈希 222 | node = root 223 | for bit in path: 224 | if bit == "0": 225 | node.left = update(node.left, leaf) 226 | else: 227 | node.right = update(node.right, leaf) 228 | root = hash(node.left + node.right) 229 | 230 | # 验证键值对 231 | def verify(key, value, proof, root): 232 | computed_hash = hash(value) 233 | for i, sibling in enumerate(proof): 234 | if key[i] == "0": 235 | computed_hash = hash(computed_hash + sibling) 236 | else: 237 | computed_hash = hash(sibling + computed_hash) 238 | return computed_hash == root 239 | ``` 240 | 241 | ### 2.8 总结 242 | 243 | 稀疏Merkle树是一种针对稀疏数据优化的Merkle树变种,通过固定结构和默认值优化,实现了高效的存储、验证和非存在性证明。它在区块链和分布式系统中有着广泛应用,尤其适合键空间巨大但实际数据稀疏的场景。与标准Merkle树相比,它牺牲了一定的灵活性,换来了空间效率和证明能力。 244 | 245 | ## 3. Merkle Patricia树 246 | 247 | Merkle Patricia树(Merkle Patricia Trie,或简称MPT)是一种结合了Merkle树和Patricia树(前缀树,Practical Algorithm to Retrieve 248 | Information Coded in Alphanumeric)特点的数据结构。它最初由以太坊(Ethereum)提出,用于高效存储和验证键值对数据(如账户状态、交易收据等),同时保持数据的完整性和可证明性。以下是对Merkle 249 | Patricia树的详细描述,包括其结构、原理、特点及应用。 250 | 251 | ### 3.1 Merkle Patricia树的结构 252 | 253 | Merkle Patricia树是一种树形结构,融合了Merkle树(哈希验证)和Patricia树(压缩前缀)的特性。它的核心目标是优化键值对的存储和查询,同时支持高效的Merkle证明。MPT的节点类型和组织方式如下: 254 | 255 | - 节点类型(MPT包含四种主要节点类型): 256 | - 叶节点(Leaf Node): 257 | - 存储实际的键值对(Key-Value Pair)。 258 | - 键的剩余部分(未被上层节点匹配的前缀)作为路径,值存储在该节点。 259 | - 格式:`[remaining_key, value]`。 260 | - 扩展节点(Extension Node): 261 | - 用于压缩共享前缀,减少树的高度。 262 | - 包含一个共享前缀(common prefix)和指向下一节点的引用。 263 | - 格式:`[shared_prefix, child_node_hash]`。 264 | - 分支节点(Branch Node): 265 | - 表示键路径的分叉点,包含16个子节点(对应16进制字符0-f)加上一个可选值。 266 | - 格式:`[child_0, child_1, ..., child_15, value]`,其中value通常为空,除非键在此终止。 267 | - 空节点(Null Node): 268 | - 表示路径的终止或空值,通常用空字符串或``null``表示。 269 | - 键的编码 270 | - 键(Key)通常是哈希值(如以太坊中账户地址的Keccak-256哈希),以16进制表示。 271 | - 键被分解为单个字符(nibble,4位),作为路径导航的依据。 272 | - 为区分叶节点和扩展节点,键会添加前缀编码(HP编码,Hex-Prefix Encoding): 273 | - 叶节点:`2`(偶数键长)或`3`(奇数键长)+ 键。 274 | - 扩展节点:`0`(偶数键长)或`1`(奇数键长)+ 键。 275 | - Merkle特性 276 | - 每个节点的哈希值由其内容计算得出(通常用Keccak-256)。 277 | - 非叶节点的哈希值依赖其子节点的哈希值,最终形成Merkle根。 278 | 279 | ##### 结构示例 280 | 281 | 假设存储两个键值对: 282 | 283 | - 键“abcd” -> 值“data1” 284 | - 键“abef” -> 值“data2” 285 | 286 | 树结构可能是: 287 | 288 | ``` 289 | Root (Branch Node) 290 | ├── "ab" (Extension Node) 291 | │ └── Branch Node 292 | │ ├── "cd" (Leaf Node: "data1") 293 | │ └── "ef" (Leaf Node: "data2") 294 | ``` 295 | 296 | - 根节点分叉到“ab”扩展节点。 297 | - “ab”后的分支节点处理“cd”和“ef”两条路径。 298 | 299 | ### 3.2 Merkle Patricia树的工作原理 300 | 301 | MPT通过前缀压缩和哈希验证实现高效存储与证明。 302 | 303 | - 数据插入: 304 | - 将键分解为nibble(4位字符)。 305 | - 从根节点开始,根据键的每个字符选择路径。 306 | - 若遇到共享前缀,创建或更新扩展节点;若路径分叉,创建分支节点;若到达终点,创建叶节点。 307 | - 每次更新节点后,重新计算沿路径的哈希值,直到更新Merkle根。 308 | - 数据查询: 309 | - 根据键的nibble逐步遍历树,匹配扩展节点的前缀或分支节点的子节点。 310 | - 到达叶节点时返回对应的值。 311 | - 数据验证: 312 | - 提供从根到目标叶节点的路径(包括兄弟节点的哈希值)。 313 | - 通过路径上的哈希值重新计算Merkle根,与存储的根对比验证。 314 | 315 | #### 工作示例 316 | 317 | 插入键“abcd” -> 值“data1”: 318 | 319 | - 路径:a -> b -> c -> d。 320 | - 若树为空: 321 | - 根节点为叶节点:`[“abcd”, “data1”]`。 322 | - 若已有键“abef”: 323 | - 根变为扩展节点:`[“ab”, child_hash]`。 324 | - 子节点为分支节点,分叉到“cd”和“ef”。 325 | 326 | ### 3.3 Merkle Patricia树的特点 327 | 328 | - 前缀压缩: 329 | - 通过扩展节点压缩共享前缀,减少树高和存储空间。 330 | - 高效查询: 331 | - 查询复杂度为O(log n),依赖键长而非数据量。 332 | - 完整性验证: 333 | - Merkle根确保数据未被篡改,任何改变都会导致根值变化。 334 | - 稀疏支持: 335 | - 适合稀疏键值对(如以太坊的账户状态),无需为每个可能键分配空间。 336 | 337 | ### 3.4 Merkle Patricia树的优缺点 338 | 339 | - 优点: 340 | - 空间效率:通过前缀压缩减少冗余存储。 341 | - 验证效率:支持Merkle证明,验证复杂度为O(log n)。 342 | - 动态性:支持键值对的插入、更新和删除。 343 | - 一致性:根哈希便于分布式系统达成共识。 344 | - 缺点: 345 | - 复杂性:实现和维护比标准Merkle树复杂。 346 | - 计算开销:每次更新需重新计算路径上的哈希。 347 | - 存储需求:尽管压缩了前缀,仍需存储大量中间节点。 348 | 349 | ### 3.5 Merkle Patricia树的应用 350 | 351 | - 以太坊区块链: 352 | - 状态树(State Trie):存储所有账户的状态(地址 -> 余额、nonce等)。 353 | - 交易树(Transaction Trie):存储区块中的交易。 354 | - 收据树(Receipt Trie):存储交易执行结果。 355 | - Merkle根存储在区块头中,用于验证和轻客户端同步。 356 | - 分布式数据库: 357 | - 用于键值对存储和一致性验证。 358 | - 密码学证明: 359 | - 支持零知识证明,验证数据的存在性或非存在性。 360 | 361 | ### 3.6 MPT与标准Merkle树和稀疏Merkle树的对比 362 | 363 | | 特性 | 标准Merkle树 | 稀疏Merkle树 | Merkle Patricia树 | 364 | |:------:|:----------:|:----------:|:----------------:| 365 | | 结构 | 二叉树 | 固定高度完全二叉树 | 前缀压缩树 | 366 | | 键处理 | 无键,直接哈希 | 键决定路径 | 键分nibble导航 | 367 | | 压缩 | 无 | 默认值优化 | 前缀压缩 | 368 | | 稀疏性支持 | 一般 | 优秀 | 优秀 | 369 | | 应用场景 | 交易列表 | 稀疏状态 | 键值对状态 | 370 | 371 | ### 3.7 实现要点 372 | 373 | 以下是MPT的简化伪代码: 374 | 375 | ```python 376 | class Node: 377 | def __init__(self, type, content): 378 | self.type = type # "leaf", "extension", "branch" 379 | self.content = content 380 | self.hash = compute_hash(content) 381 | 382 | # 插入键值对 383 | def insert(trie, key, value): 384 | nibbles = to_nibbles(key) 385 | return update_node(trie, nibbles, value) 386 | 387 | def update_node(node, nibbles, value): 388 | if not node: # 空树 389 | return Node("leaf", [nibbles, value]) 390 | if node.type == "leaf": 391 | return split_node(node, nibbles, value) 392 | elif node.type == "extension": 393 | prefix = node.content[0] 394 | if match_prefix(nibbles, prefix): 395 | child = update_node(node.content[1], nibbles[len(prefix):], value) 396 | return Node("extension", [prefix, child]) 397 | else: 398 | return split_extension(node, nibbles, value) 399 | elif node.type == "branch": 400 | idx = nibbles[0] 401 | if len(nibbles) == 1: 402 | node.content[idx] = Node("leaf", [[], value]) 403 | else: 404 | node.content[idx] = update_node(node.content[idx], nibbles[1:], value) 405 | return node 406 | 407 | # 计算Merkle根 408 | def compute_root(node): 409 | return node.hash 410 | ``` 411 | 412 | ### 3.8 总结 413 | 414 | Merkle 415 | Patricia树是一种高效的键值存储和验证结构,结合了Patricia树的前缀压缩和Merkle树的哈希完整性。它在以太坊中广泛用于状态管理,通过Merkle根确保数据一致性,并支持轻客户端验证。相比标准Merkle树和稀疏Merkle树,MPT更适合动态键值对场景,但实现复杂度较高。 416 | -------------------------------------------------------------------------------- /03-nodejs/EnReadme.md: -------------------------------------------------------------------------------- 1 | # node interview questions in blockchain 2 | 3 | ## Part 1: JS and Node 4 | 5 | ### 1. What is the output result of the following code? 6 | 7 | #### Code snippet 1 8 | ``` 9 | try { 10 | throw newError('1') 11 | } catch(error) { 12 | console.log('error') 13 | } 14 | ``` 15 | Output result: 16 | 17 | ``` 18 | error 19 | ``` 20 | 21 | #### Code snippet 2 22 | 23 | ``` 24 | try { 25 | setTimeout(function() { 26 | console.log('b'); 27 | }, 0); 28 | } catch (error) { 29 | console.log('error'); 30 | } 31 | console.log('out try catch') 32 | ``` 33 | Output result: 34 | 35 | ``` 36 | out try catch 37 | ``` 38 | 39 | #### Code snippet three 40 | 41 | ``` 42 | try { 43 | new Promise(() => { 44 | throw new Error('new promise throw error'); 45 | }); 46 | } catch (error) { 47 | console.log('error'); 48 | } 49 | ``` 50 | 51 | Output result: 52 | 53 | ``` 54 | Error: new promise throw error 55 | ``` 56 | 57 | #### Code snippet four 58 | 59 | ``` 60 | console.log('Start'); 61 | setTimeout(() => { 62 | console.log('Timeout'); 63 | }, 0); 64 | Promise.resolve().then(() => { 65 | console.log('Promise resolved'); 66 | }); 67 | console.log('End'); 68 | ``` 69 | 70 | Output result: 71 | 72 | ``` 73 | Start 74 | End 75 | Promise resolved 76 | ``` 77 | 78 | ### 2. What does the event loop mechanism do? 79 | 80 | The event loop mechanism is used to manage when the callback function of the asynchronous API returns to the main thread for execution. 81 | 82 | Node.js uses an asynchronous IO model. The synchronous API is executed in the main thread, the asynchronous API is executed in the thread maintained by the underlying C++, and the callback function of the asynchronous API will also be executed in the main thread. 83 | 84 | When a Javascript application is running, when can the callback functions of many asynchronous APIs be called back to the main thread? This is what the event loop mechanism does, managing when the callback function of the asynchronous API returns to the main thread for execution. 85 | 86 | 87 | ### 3. The six stages of EventLoop are those six stages 88 | 89 | 3.1.Timers 90 | Timers: Callback function (setlnterval, setTimeout) used to store timers. 91 | 92 | 3.2.Pending callbacks 93 | Pendingcallbacks: Execute callback functions related to the operating system. For example, the callback function that monitors port operations when starting a server-side application is called here. 94 | 95 | 3.3.idle, prepare 96 | idle, prepare: used internally by the system. (We programmers don’t need to worry about this) 97 | 98 | 3.4.Poll 99 | Poll: Stores the callback function queue for 1/O operations, such as callback functions for file read and write operations. 100 | 101 | Special attention needs to be paid at this stage. If there are callback functions in the event queue, execute them until the queue is cleared, otherwise the event loop will stay in this stage for a while waiting for new callback functions to enter. 102 | 103 | But this waiting is not certain, but depends on the following two conditions: 104 | 105 | - If there is a calling function to be executed in the setlmmediate queue (check phase). In this case there will be no waiting. 106 | - There is a callback function to be executed in the timers queue, and there will be no waiting in this case. The event loop will move to the check phase, then to the Closingcallbacks phase, and finally from the timers phase to the next loop. 107 | 108 | 3.5.Check 109 | Check: Stores the callback function of setlmmediate. 110 | 111 | 3.6.Closingcallbacks 112 | Closingcallbacks: Execute callbacks related to closing events, such as callback functions for closing database connections, etc. 113 | 114 | ### 3. What are macro tasks and micro tasks? 115 | 116 | Like js in the browser, asynchronous code in node is also divided into macro tasks and micro tasks, but the execution order between them is different. Let’s take a look at what macrotasks and microtasks are in Node. 117 | 118 | macro task 119 | setlnterval 120 | settimeout 121 | setlmmediate 122 | I/O 123 | 124 | microtasks 125 | Promise.then 126 | Promise.catch 127 | Promise.finally 128 | process.nextTick 129 | 130 | ### 4. Briefly explain the execution sequence of micro tasks and macro tasks 131 | 132 | In node, the callback function of microtask is placed in the microtask queue, and the callback function of macrotask is placed in the macrotask queue. 133 | 134 | Microtasks have higher priority than macrotasks. When there is an executable callback function in the microtask event queue, the event loop will pause and enter the next stage of the event loop after executing the callback function of the current stage, and will immediately enter the microtask's event queue to start executing the callback function. When the callback function in the microtask queue is executed, the event loop will enter the next segment and start executing the callback function. 135 | 136 | There is another point we need to pay special attention to when it comes to microtasks. That is, although nextTick is also a microtask, its priority is higher than other microtasks. When executing a microtask, other microtasks will not start until all callback functions in nextlick are executed. 137 | 138 | In general, when the main thread synchronization code is executed, the microtasks will be cleared first (if the microtasks continue to generate microtasks, they will be cleared again), and then go to the next event loop stage. And the execution of microtasks is interspersed among the six stages of the event loop. That is, each time the event loop enters the next stage, it will determine whether the microtask queue is empty. If it is empty, it will enter the next stage. Otherwise, the microtasks will be cleared first. queue. 139 | 140 | ### 5. Let’s talk about the new features of ES6 141 | 142 | Class support, modularization, arrow operators, let/const block scope, string templates, destructuring, parameter default values/indefinite parameters/expanded parameters, for-of traversal, generator, Map/Set, Promise 143 | 144 | ### 6. What are the methods inherited by js classes? 145 | 146 | Prototype chaining method, attribute copying method and constructor application method. In addition, since each object can be a class, these methods can also be used for inheritance of object classes. 147 | 148 | prototype chain method 149 | 150 | function Animal() { 151 | this.name = 'animal'; 152 | } 153 | Animal.prototype.sayName = function(){ 154 | alert(this.name); 155 | }; 156 | 157 | function Person() {} 158 | Person.prototype = Animal.prototype; // Person inherits from animal 159 | Person.prototype.constructor = 'Person'; // Update constructor to person 160 | 161 | attribute copy method 162 | 163 | function Animal() { 164 | this.name = 'animal'; 165 | } 166 | Animal.prototype.sayName = function() { 167 | alert(this.name); 168 | }; 169 | 170 | function Person() {} 171 | 172 | for(prop in Animal.prototype) { 173 | Person.prototype[prop] = Animal.prototype[prop]; 174 | } //Copy all attributes of the animal to the human side 175 | Person.prototype.constructor = 'Person'; // Update constructor to be person 176 | 177 | Constructor application method 178 | 179 | function Animal() { 180 | this.name = 'animal'; 181 | } 182 | Animal.prototype.sayName = function() { 183 | alert(this.name); 184 | }; 185 | 186 | function Person() { 187 | Animal.call(this); // Apply, call, and bind methods are all available. The subtle differences will be mentioned later. 188 | } 189 | 190 | ## 7. What is the implementation method of multiple inheritance in js classes? 191 | 192 | This is achieved through the attribute copying method in class inheritance. Because when all prototype properties of the parent class are copied, the subclass will naturally have similar behaviors and properties. 193 | 194 | 195 | ## 8. What does this in js point to? 196 | 197 | object itself 198 | 199 | ## 9. What is the difference between apply, call and bind in JS? 200 | 201 | All three can apply a function to other objects. Note that it is not the own object. apply, call is to directly execute the function call, bind is to bind, and execution needs to be called again. The difference between apply and call is that apply accepts an array as a parameter, while call accepts an unlimited list of parameters separated by commas. 202 | 203 | ``` 204 | function Person() { 205 | } 206 | Person.prototype.sayName() { alert(this.name); } 207 | 208 | var obj = {name: 'michaelqin'}; // Note that this is a normal object, it is not an instance of Person 209 | 1) apply 210 | Person.prototype.sayName.apply(obj, [param1, param2, param3]); 211 | 212 | 2) call 213 | Person.prototype.sayName.call(obj, param1, param2, param3); 214 | 215 | 3) bind 216 | var sn = Person.prototype.sayName.bind(obj); 217 | sn([param1, param2, param3]); // bind needs to be bound first and then executed 218 | sn(param1, param2, param3); // bind needs to be bound first and then executed 219 | ``` 220 | 221 | ## 10. What are caller, callee and arguments respectively? 222 | 223 | The relationship between caller and callee is like the relationship between employee and employee, that is, the relationship between calling and being called. Both return function object references. arguments is a list of all parameters of the function, which is an array-like variable. 224 | 225 | ``` 226 | function parent(param1, param2, param3) { 227 | child(param1, param2, param3); 228 | } 229 | 230 | function child() { 231 | console.log(arguments); // { '0': 'mqin1', '1': 'mqin2', '2': 'mqin3' } 232 | console.log(arguments.callee); // [Function: child] 233 | console.log(child.caller); // [Function: parent] 234 | } 235 | 236 | parent('mqin1', 'mqin2', 'mqin3'); 237 | ``` 238 | 239 | ## 11. What is the architecture of node? 240 | 241 | It is mainly divided into three layers, application app >> V8 and node built-in architecture >> operating system. V8 is the environment in which node runs, and can be understood as a node virtual machine. The built-in architecture of node can be divided into three layers: core module (javascript implementation) >> c++ binding >> libuv + CAes + http. 242 | 243 | 244 | ## 12. What is the difference between apply, call and bind in js? 245 | 246 | All three can apply a function to other objects. Note that it is not the own object. apply, call is to directly execute the function call, bind is to bind, and execution needs to be called again. The difference between apply and call is that apply accepts an array as a parameter, while call accepts an unlimited number of parameter lists separated by commas. 247 | 248 | ``` 249 | function Person() {} 250 | Person.prototype.sayName() { alert(this.name); } 251 | 252 | var obj = {name: 'michaelqin'}; // Note that this is a normal object, it is not an instance of Person 253 | 1) apply 254 | Person.prototype.sayName.apply(obj, [param1, param2, param3]); 255 | 256 | 2) call 257 | Person.prototype.sayName.call(obj, param1, param2, param3); 258 | 259 | 3) bind 260 | var sn = Person.prototype.sayName.bind(obj): 261 | sn([param1, param2, param3]); // bind needs to be bound first and then executed 262 | sn(param1, param2, param3); // bind needs to be bound first and then executed 263 | ``` 264 | 265 | ## 13. What are caller, callee and arguments respectively? 266 | The relationship between caller and callee is like the relationship between employer and employee, that is, the relationship between calling and being called. Both return function object references. arguments is a list of all parameters of the function, which is an array-like variable. 267 | ``` 268 | function parent(param1, param2, param3) { 269 | child(param1, param2, param3); 270 | } 271 | 272 | function child() { 273 | console.log(arguments); // { '0': 'mqin1', '1': 'mqin2', '2': 'mqin3' } 274 | console.log(arguments.callee); // [Function: child] 275 | console.log(child.caller); // [Function: parent] 276 | } 277 | 278 | parent('mqin1', 'mqin2', 'mqin3'); 279 | ``` 280 | 281 | ## 13. The difference between ES MAP and SET 282 | 283 | JavaScript's default object representation {} can be regarded as the Map or Dictionary data structure in other languages, that is, a set of key-value pairs. But there is a small problem with JavaScript objects, that is, the keys must be strings. But in fact, Number or other data types are also very reasonable as keys. To solve this problem, the latest ES6 specification introduces a new data type Map. 284 | 285 | Map is a structure of key-value pairs and has extremely fast search speed. 286 | 287 | Set is similar to Map in that it is also a collection of keys but does not store values. Since keys cannot be repeated, there are no duplicate keys in Set. 288 | 289 | ## 14. Let’s briefly talk about the functions of set, WeakSet, map and WeakMap. 290 | 291 | ##### Set: 292 | 293 | - Members are unique, ordered and non-duplicate. 294 | - [value, value], the key value and the key name are consistent (or only the key value, no key name). 295 | - Can be traversed, the methods are: add, delete, has. 296 | 297 | ##### WeakSet: 298 | 299 | - Members are objects. 300 | - Members are all weak references and can be recycled by the garbage collection mechanism. They can be used to save DOM nodes and are not likely to cause memory leaks. 301 | - Cannot be traversed, methods include add, delete, and has. 302 | 303 | ##### Map: 304 | 305 | - Essentially a collection of key-value pairs, similar to a collection. 306 | - It can be traversed, and there are many methods to convert it to various data formats. 307 | 308 | ##### WeakMap: 309 | 310 | - Only objects are accepted as key names (except null), and other types of values ​​are not accepted as key names. 311 | - The key name is a weak reference, the key value can be arbitrary, the object pointed to by the key name can be garbage collected, and the key name is invalid at this time. 312 | - Cannot be traversed, methods include get, set, has, delete. 313 | 314 | ## 15. JS closures, design patterns, etc. 315 | I won’t write the answer here, it’s relatively simple. 316 | -------------------------------------------------------------------------------- /03-nodejs/readme.md: -------------------------------------------------------------------------------- 1 | # 区块链中的 node 面试题目 2 | 3 | ## 第一部分:JS 与 Node 4 | 5 | ### 1.以下代码输出的结果是什么 6 | 7 | #### 代码片段一 8 | ``` 9 | try { 10 | throw new Error('1') 11 | } catch(error) { 12 | console.log('error') 13 | } 14 | ``` 15 | 输出结果: 16 | 17 | ``` 18 | error 19 | ``` 20 | 21 | #### 代码片段二 22 | 23 | ``` 24 | try { 25 | setTimeout(function() { 26 | console.log('b'); 27 | }, 0); 28 | } catch (error) { 29 | console.log('error'); 30 | } 31 | console.log('out try catch') 32 | ``` 33 | 输出结果: 34 | 35 | ``` 36 | out try catch 37 | ``` 38 | 39 | #### 代码片段三 40 | 41 | ``` 42 | try { 43 | new Promise(() => { 44 | throw new Error('new promise throw error'); 45 | }); 46 | } catch (error) { 47 | console.log('error'); 48 | } 49 | ``` 50 | 51 | 输出结果: 52 | 53 | ``` 54 | Error: new promise throw error 55 | ``` 56 | 57 | #### 代码片段四 58 | 59 | ``` 60 | console.log('Start'); 61 | setTimeout(() => { 62 | console.log('Timeout'); 63 | }, 0); 64 | Promise.resolve().then(() => { 65 | console.log('Promise resolved'); 66 | }); 67 | console.log('End'); 68 | ``` 69 | 70 | 输出结果: 71 | 72 | ``` 73 | Start 74 | End 75 | Promise resolved 76 | ``` 77 | 78 | ### 2.事件循环机制做的是什么事情? 79 | 80 | 事件循环机制用于管理异步API的回调函数什么时候回到主线程中执行。 81 | 82 | Node.js采用的是异步IO模型。同步API在主线程中执行,异步API在底层的C++维护的线程中执行,异步API的回调函数也会在主线程中执行。 83 | 84 | 在Javascript应用运行时,众多异步API的回调函数什么时候能回到主线程中调用呢?这就是事件环环机制做的事情,管理异步API的回调函数什么时候回到主线程中执行。 85 | 86 | 87 | ### 3. EventLoop 的六个阶段分别是那六个阶段 88 | 89 | 3.1.Timers 90 | Timers:用于存储定时器的回调函数(setlnterval,setTimeout)。 91 | 92 | 3.2.Pendingcallbacks 93 | Pendingcallbacks:执行与操作系统相关的回调函数,比如启动服务器端应用时监听端口操作的回调函数就在这里调用。 94 | 95 | 3.3.idle,prepare 96 | idle,prepare:系统内部使用。(这个我们程序员不用管) 97 | 98 | 3.4.Poll 99 | Poll:存储1/O操作的回调函数队列,比如文件读写操作的回调函数。 100 | 101 | 在这个阶段需要特别注意,如果事件队列中有回调函数,则执行它们直到清空队列,否则事件循环将在此阶段停留一段时间以等待新的回调函数进入。 102 | 103 | 但是对于这个等待并不是一定的,而是取决于以下两个条件: 104 | 105 | - 如果setlmmediate队列(check阶段)中存在要执行的调函数。这种情况就不会等待。 106 | - timers队列中存在要执行的回调函数,在这种情况下也不会等待。事件循环将移至check阶段,然后移至Closingcallbacks阶段,并最终从timers阶段进入下一次循环。 107 | 108 | 3.5.Check 109 | Check:存储setlmmediate的回调函数。 110 | 111 | 3.6.Closingcallbacks 112 | Closingcallbacks:执行与关闭事件相关的回调,例如关闭数据库连接的回调函数等。 113 | 114 | ### 3. 宏任务与微任务有那些 115 | 116 | 跟浏览器中的js一样,node中的异步代码也分为宏任务和微任务,只是它们之间的执行顺序有所区别。我们再来看看Node中都有哪些宏任务和微任务 117 | 118 | 宏任务 119 | setlnterval 120 | setimeout 121 | setlmmediate 122 | I/O 123 | 124 | 微任务 125 | Promise.then 126 | Promise.catch 127 | Promise.finally 128 | process.nextTick 129 | 130 | ### 4. 简单说一下微任务和宏任务的执行顺序 131 | 132 | 在node中,微任务的回调函数被放置在微任务队列中,宏任务的回调函数被放置在宏任务队列中。 133 | 134 | 微任务优先级高于宏任务。当微任务事件队列中存在可以执行的回调函数时,事件循环在执行完当前阶段的回调函数后会暂停进入事件循环的下一个阶段,而会立即进入微任务的事件队列中开始执行回调函数,当微任务队列中的回调函数执行完成后,事件循环才会进入到下一个段开始执行回调函数。 135 | 136 | 对于微任务我们还有个点需要特别注意。那就是虽然nextTick同属于微任务,但是它的优先级是高于其它微任务,在执行微任务时,只有nextlick中的所有回调函数执行完成后才会开始执行其它微任务。 137 | 138 | 总的来说就是当主线程同步代码执行完毕后会优先清空微任务(如果微任务继续产生微任务则会再次清空),然后再到下个事件循环阶段。并且微任务的执行是穿插在事件循环六个阶段中间的,也就是每次事件循环进入下个阶段前会判断微任务队列是否为空,为空才会进入下个阶段,否则先清空微任务队列。 139 | 140 | ### 5. 说说 ES6 有哪些新特性 141 | 142 | 类的支持,模块化,箭头操作符,let/const块作用域,字符串模板,解构,参数默认值/不定参数/拓展参数, for-of遍历, generator, Map/Set, Promise 143 | 144 | ### 6. js类继承的方法有哪些 145 | 146 | 原型链法,属性复制法和构造器应用法. 另外,由于每个对象可以是一个类,这些方法也可以用于对象类的继承. 147 | 148 | 原型链法 149 | 150 | function Animal() { 151 | this.name = 'animal'; 152 | } 153 | Animal.prototype.sayName = function(){ 154 | alert(this.name); 155 | }; 156 | 157 | function Person() {} 158 | Person.prototype = Animal.prototype; // 人继承自动物 159 | Person.prototype.constructor = 'Person'; // 更新构造函数为人 160 | 161 | 属性复制法 162 | 163 | function Animal() { 164 | this.name = 'animal'; 165 | } 166 | Animal.prototype.sayName = function() { 167 | alert(this.name); 168 | }; 169 | 170 | function Person() {} 171 | 172 | for(prop in Animal.prototype) { 173 | Person.prototype[prop] = Animal.prototype[prop]; 174 | } // 复制动物的所有属性到人量边 175 | Person.prototype.constructor = 'Person'; // 更新构造函数为人 176 | 177 | 构造器应用法 178 | 179 | function Animal() { 180 | this.name = 'animal'; 181 | } 182 | Animal.prototype.sayName = function() { 183 | alert(this.name); 184 | }; 185 | 186 | function Person() { 187 | Animal.call(this); // apply, call, bind方法都可以.细微区别,后面会提到. 188 | } 189 | 190 | ## 7.js类多重继承的实现方法是怎么样的? 191 | 192 | 就是类继承里边的属性复制法来实现.因为当所有父类的prototype属性被复制后,子类自然拥有类似行为和属性. 193 | 194 | 195 | ## 8.js 里面的 this 指向的是什么 196 | 197 | 对象本身 198 | 199 | ## 9. Js 的 apply, call 和 bind 有什么区别? 200 | 201 | 三者都可以把一个函数应用到其他对象上,注意不是自身对象.apply,call是直接执行函数调用,bind是绑定,执行需要再次调用.apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表 202 | 203 | ``` 204 | function Person() { 205 | } 206 | Person.prototype.sayName() { alert(this.name); } 207 | 208 | var obj = {name: 'michaelqin'}; // 注意这是一个普通对象,它不是Person的实例 209 | 1) apply 210 | Person.prototype.sayName.apply(obj, [param1, param2, param3]); 211 | 212 | 2) call 213 | Person.prototype.sayName.call(obj, param1, param2, param3); 214 | 215 | 3) bind 216 | var sn = Person.prototype.sayName.bind(obj); 217 | sn([param1, param2, param3]); // bind需要先绑定,再执行 218 | sn(param1, param2, param3); // bind需要先绑定,再执行 219 | ``` 220 | 221 | ## 10. caller, callee和arguments分别是什么? 222 | 223 | caller, callee之间的关系就像是employer和employee之间的关系,就是调用与被调用的关系,二者返回的都是函数对象引用.arguments是函数的所有参数列表,它是一个类数组的变量. 224 | 225 | ``` 226 | function parent(param1, param2, param3) { 227 | child(param1, param2, param3); 228 | } 229 | 230 | function child() { 231 | console.log(arguments); // { '0': 'mqin1', '1': 'mqin2', '2': 'mqin3' } 232 | console.log(arguments.callee); // [Function: child] 233 | console.log(child.caller); // [Function: parent] 234 | } 235 | 236 | parent('mqin1', 'mqin2', 'mqin3'); 237 | ``` 238 | 239 | ## 11. node的构架是什么样子的 240 | 241 | 主要分为三层,应用app >> V8及node内置架构 >> 操作系统. V8是node运行的环境,可以理解为node虚拟机.node内置架构又可分为三层: 核心模块(javascript实现) >> c++绑定 >> libuv + CAes + http. 242 | 243 | 244 | ## 12. js 的 apply, call 和 bind 有什么区别? 245 | 246 | 三者都可以把一个函数应用到其他对象上,注意不是自身对象.apply,call是直接执行函数调用,bind是绑定,执行需要再次调用.apply和call的区别是apply接受数组作为参数,而call是接受逗号分隔的无限多个参数列表, 247 | 248 | ``` 249 | function Person() {} 250 | Person.prototype.sayName() { alert(this.name); } 251 | 252 | var obj = {name: 'michaelqin'}; // 注意这是一个普通对象,它不是Person的实例 253 | 1) apply 254 | Person.prototype.sayName.apply(obj, [param1, param2, param3]); 255 | 256 | 2) call 257 | Person.prototype.sayName.call(obj, param1, param2, param3); 258 | 259 | 3) bind 260 | var sn = Person.prototype.sayName.bind(obj); 261 | sn([param1, param2, param3]); // bind需要先绑定,再执行 262 | sn(param1, param2, param3); // bind需要先绑定,再执行 263 | ``` 264 | 265 | ## 13. caller,callee 和 arguments分别是什么 266 | caller, callee 之间的关系就像是 employer和 employee之间的关系,就是调用与被调用的关系,二者返回的都是函数对象引用.arguments是函数的所有参数列表,它是一个类数组的变量. 267 | ``` 268 | function parent(param1, param2, param3) { 269 | child(param1, param2, param3); 270 | } 271 | 272 | function child() { 273 | console.log(arguments); // { '0': 'mqin1', '1': 'mqin2', '2': 'mqin3' } 274 | console.log(arguments.callee); // [Function: child] 275 | console.log(child.caller); // [Function: parent] 276 | } 277 | 278 | parent('mqin1', 'mqin2', 'mqin3'); 279 | ``` 280 | 281 | ## 13. ES 的 MAP 和 SET 的区别 282 | 283 | JavaScript的默认对象表示方式{}可以视为其他语言中的Map或Dictionary的数据结构,即一组键值对。但是JavaScript的对象有个小问题,就是键必须是字符串。但实际上Number或者其他数据类型作为键也是非常合理的。为了解决这个问题,最新的ES6规范引入了新的数据类型Map。 284 | 285 | Map 是一组键值对的结构,具有极快的查找速度。 286 | 287 | Set 和 Map 类似,也是一组key的集合,但不存储value。由于key不能重复,所以,在Set中,没有重复的key。 288 | 289 | ## 14. 简单说一下 set,WeakSet, map,WeakMap 功能作用 290 | 291 | ##### Set: 292 | 293 | - 成员唯一、有序且不重复。 294 | - [value, value],键值与键名是一致的(或者说只有键值,没有键名)。 295 | - 可以遍历,方法有:add、delete、has。 296 | 297 | ##### WeakSet: 298 | 299 | - 成员都是对象。 300 | - 成员都是弱引用,可以被垃圾回收机制回收,可以用来保存DOM节点,不容易造成内存泄漏。 301 | - 不能遍历,方法有add、delete、has。 302 | 303 | ##### Map: 304 | 305 | - 本质上是键值对的集合,类似集合。 306 | - 可以遍历,方法很多可以跟各种数据格式转换。 307 | 308 | ##### WeakMap: 309 | 310 | - 只接受对象作为键名(null除外),不接受其他类型的值作为键名。 311 | - 键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收,此时键名是无效的。 312 | - 不能遍历,方法有get、set、has、delete。 313 | 314 | ## 15. JS 的闭包,设计模式等 315 | 这里不写答案了,比较简单 316 | 317 | 318 | ## 16. nodeJS 最大内存限制是多大,如果正常开发的项目出现内部问题,怎么解决 319 | 320 | 321 | ## 第二部分:Vue, React 与 包管理 322 | 323 | ## 第三部分: node 常用后端开发框架 324 | 325 | ### 1.使用过什么 node 后端开发框架, 分别说一下他们的工作流 326 | 327 | ### 2.接口权限验证有哪些方式 328 | 329 | ### 3.restapi, websocket api, rpc, graphql 接口使用场景 330 | 331 | ### 4.如何在后端解决跨越问题 332 | 333 | 334 | ## 第四部分: 数据库 335 | 336 | ### 1.写链表查询 sql 337 | 338 | ### 2.数据库常见的优化方式有哪些 339 | 340 | ### 3.什么情况下会出现数据库死锁 341 | 342 | ### 4.mysql 数据分析工具有哪些,怎么操作 343 | 344 | ### 6.可以简单描述一下数据库主从在工程实战中怎么实现吗 345 | 346 | ### 7. mongoDB 和 mysql 的区别,实际工作中怎么选择数据库 347 | 348 | 349 | ## 第五部分: node 与 区块链 350 | 351 | ### 1.请用 nodejs 写一段合约事件监听的代码 352 | 353 | ### 2.请用 nodejs 比特币以太坊地址生成与离线签名 354 | 355 | ### 3.请用 nodejs 比特币以太坊地址生成与离线签名 356 | 357 | ### 4. DAPP 浏览器实现方式有那些,底层原理是什么 358 | 359 | 360 | 361 | 362 | -------------------------------------------------------------------------------- /04-consensus-algorithm/readme.md: -------------------------------------------------------------------------------- 1 | # 共识算法 2 | 3 | 4 | Chaineye 共识算法面试题汇集,供想要学习的小伙伴学习。 5 | 6 | 推特:@seek_web3 7 | 8 | Chainey 社群: 官网 chaineye.info | Chaineye Rust 教程 | 微信: LGZAXE, 加微信之后拉各社群 9 | 10 | 所有代码和教程开源在github: https://github.com/0xchaineye/chaineye-blockchain-interview 11 | 12 | ---------------------------------------------------------------------------------------------------------------------------------------------------------- 13 | 14 | 15 | ## 1.POW 16 | 17 | 18 | ## 2.POS 19 | 20 | 21 | ## 3.DPOS 22 | 23 | 24 | ## 4.POA 25 | 26 | 27 | ## 5.Paxos 28 | 29 | 30 | ## 6.Raft 31 | 32 | 33 | ## 7.PBFT 34 | 35 | 36 | ## 8.BFT 37 | 38 | 39 | ## 9.Fabric-pbft 40 | 41 | 42 | ## 10.Tendermint-bft 43 | 44 | 45 | 46 | ## 11.Hostuff-bft 47 | 48 | 49 | 50 | ## 12 avalanche-pbft 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /05-ethereum/EnReadme.md: -------------------------------------------------------------------------------- 1 | # Ethereum 2 | ### 1. Explain in detail the process of packaging Ethereum transactions 3 | Ethereum is a blockchain network that allows users to send and receive ether (ETH) and other tokens and execute smart contracts on the chain. These transactions need to be packaged into blocks and verified and confirmed by miners. 4 | 5 | The following is a detailed description of the process of packaging Ethereum transactions: 6 | 7 | Create Transaction: A transaction is created and signed by the sender. It includes the sender's Ethereum address, the receiver's Ethereum address, the transaction amount, and some optional transaction data. 8 | 9 | Broadcast transactions: Transactions need to be broadcast to all nodes on the network. This works by sending the transaction to a node, which then broadcasts it to other nodes it knows about. 10 | 11 | Transaction pool: After transactions are broadcast, they will enter the pending transaction pool. In the transaction pool, transactions wait to be included in new blocks. 12 | 13 | Packing transactions: Miners will select some transactions from the transaction pool and package them into new blocks. Miners usually choose transactions with the highest gas fees because they can receive higher rewards from them. Once miners have packed transactions into blocks, they are considered confirmed and executed. 14 | 15 | Confirmed Transactions: Once a new block is created and added to the blockchain containing new transactions, those transactions are considered confirmed and executed. 16 | 17 | Transaction fee: Transaction fee is paid by the sender and is calculated by consuming a certain amount of gas. Gas is a unit of measurement in the Ethereum network used to measure the computing resources required to execute transactions. Miners receive these transaction fees as a reward to encourage them to participate in the process of packaging transactions. 18 | 19 | In short, the process of Ethereum transaction packaging involves creating transactions, broadcasting transactions, transaction pools, packaging transactions, confirming transactions, and transaction fees. This process is automated and performed by nodes and miners in the blockchain network. 20 | 21 | ### 2. Why does Ethereum fork? When encountering a forked wallet, how should layer2 similar applications be handled? 22 | ### 3. What is the data structure used at the bottom of Ethereum? Can you explain it in detail? 23 | Ethereum uses a variety of different data structures at the bottom, the most important of which are the Merkle Patricia Tree and the state database. 24 | 25 | Merkle Patricia Tree is an efficient data structure used to store and manage the state of Ethereum accounts and transactions. It is a tree structure where each node contains a hash value and a key-value pair. Key-value pairs are used to store account addresses, transaction hashes and other information, while hash values ​​are used to quickly verify the integrity and consistency of data. Merkle Patricia Tree is widely used in Ethereum's account status tree and transaction status tree. 26 | 27 | Another important data structure is the state database. It is the global state storage of Ethereum, including information such as balances, codes, storage and contract status of all accounts. The implementation of the state database is based on LevelDB, which can store and retrieve state data efficiently. 28 | 29 | In addition to this, Ethereum also uses other data structures, such as Bloom Filter and B-Tree, to optimize the performance and efficiency of the blockchain. Bloom Filter is used to quickly retrieve whether an element exists in a certain collection, while B-Tree is a self-balancing tree commonly used in database indexes. 30 | 31 | ### 4. What cryptographic algorithms are used by the Ethereum wallet, and briefly explain the principles of these algorithms? 32 | ### 5. Briefly explain the gas mechanism of Ethereum 33 | ### 6. Do you understand EIP155 of Ethereum? What problems does it mainly exist to solve? 34 | ### 7. Do you know Eip712 of Ethereum? What problems does it mainly exist to solve? 35 | EIP712 is a standard on Ethereum that defines a message structure and signing method for secure message verification and authorization in decentralized applications (DApps). 36 | 37 | Specifically, EIP712 allows DApps to verify user authorization for certain operations without revealing private keys. It ensures message integrity and authentication, as well as preventing replay attacks and other fraud by using predefined message formats and signature methods. 38 | 39 | Another important use of EIP712 is to enable DApps to interact with smart contracts more securely and directly. By using the message format defined by the EIP712 standard, smart contracts can verify messages from DApps, thereby protecting the contract from fraudulent attacks. 40 | 41 | In summary, EIP712 provides a secure and standardized message verification and authorization method for DApps and smart contracts in the Ethereum ecosystem. 42 | 43 | ### 8. Do you understand EIP1155? What problems does it mainly exist to solve? 44 | ### 9. Briefly explain the underlying operating mechanism of Ethereum’s EVM 45 | Ethereum's EVM (Ethereum Virtual Machine) is one of the core components of the Ethereum network. It is a stack-based virtual machine that provides a running environment for smart contracts. The following is a brief description of the underlying operating mechanism of EVM: 46 | 47 | 1. Instruction set: EVM adopts a streamlined instruction set, including arithmetic operations, comparison operations, logical operations, memory operations, stack operations, etc. Each instruction has an opcode and some operands. When the instruction is executed, the EVM reads the opcode from the instruction set and operates according to the operands. 48 | 49 | 2. Stack: The operation of EVM is based on stack. There is a stack in EVM, and all operands are pushed into the stack instead of using registers. The size of the stack is fixed and cannot be adjusted, so smart contracts need to be written according to the size of the stack. 50 | 51 | 3. Storage: There is also a memory in the EVM in which data can be stored. The memory is allocated by word (32 bytes). The memory is accessible via the contract's address and memory index, and each memory cell has an initial value of zero. 52 | 53 | 4. Memory: EVM also has a memory area that can be used to store temporary data. The instructions in the EVM can read and write data in the memory, but the size of the memory is dynamically adjusted, and the size is increased by 32 bytes each time. 54 | 55 | 5. State transition: When a smart contract is called, the EVM will load its code into memory and execute it one by one according to the instructions. When an instruction is executed, the EVM changes the state of the contract, such as changing the value in memory, pushing data onto the stack, etc. Once the smart contract is executed, the EVM saves the final state to the Ethereum blockchain. 56 | 57 | ### 10.What is the implementation principle of EVM memory? 58 | EVM (Ethereum Virtual Machine) is a virtual machine on the Ethereum blockchain that is used to execute the code of smart contracts. In the EVM, memory is a byte array used to store temporary data while executing smart contracts. 59 | 60 | The implementation principle of EVM memory is as follows: 61 | 62 | Memory is a memory space composed of byte arrays. In EVM, memory size is in bytes, with a maximum size of 2^256 bytes. 63 | 64 | The memory is stored permanently, which means it can be accessed during the life cycle of the smart contract. 65 | 66 | Each byte of memory has a unique index value called a memory address. The memory address is an Ethereum address ranging from 0 to 2^256-1. 67 | 68 | Smart contracts can access data in memory through memory addresses. The memory address can be used directly in the code of the smart contract or passed through function parameters. 69 | 70 | Memory read and write operations are expensive operations, so memory usage should be minimized in smart contracts. 71 | 72 | Read operations from memory are free, but write operations require gas. The gas cost of a write operation depends on the number of bytes written. 73 | 74 | In short, EVM memory is a memory space composed of byte arrays used to store temporary data when executing smart contracts. The memory address uniquely identifies each byte in the memory, and smart contracts can access the data in the memory through the memory address. Memory usage should be minimized in smart contracts because both read and write operations are expensive operations. 75 | 76 | ### 11. Briefly explain the block packaging mechanism of Ethereum nodes 77 | In the Ethereum network, each block contains a set of transactions, which can be ordinary transactions or smart contract calls. When a node mines a new block on the Ethereum network, it needs to perform the following steps to package the transaction: 78 | 79 | Select a group of transactions to be packaged from the transaction pool, and usually the transactions with the highest transaction fees are selected first for packaging. 80 | 81 | These transactions are sorted according to certain rules to ensure that each node has the same transaction order, thereby ensuring that all nodes reach consensus. 82 | 83 | Calculate the hash value of the block header and write it into the new block together with the transaction data. 84 | 85 | New blocks are broadcast to the entire network for other nodes to verify and confirm. 86 | 87 | In Ethereum, smart contracts are written in high-level programming languages ​​like Solidity and run through the EVM. When a smart contract is called, the EVM will execute the contract code and write the results to the blockchain. Smart contract calls can also be packaged into blocks as a transaction. 88 | 89 | ### 12. What is the calculation mechanism of Ethereum’s block header? 90 | Ethereum's block header calculation mechanism is a hash-based mechanism, which consists of the following parts: 91 | 92 | Parent block hash: points to the hash value of the previous block in the chain where the current block is located, used to ensure the non-tamperability of the blockchain. 93 | 94 | Status root hash: represents the root hash of the status of all accounts in the current block, used to ensure the legitimacy of all transactions that occur in the current block. 95 | 96 | Transaction root hash: represents the hash value of the hash tree root node of all transactions in the current block, used to verify the validity of all transactions in the block. 97 | 98 | Timestamp: Specifies the creation timestamp of the block, which is used to verify the timing of the block. 99 | 100 | Difficulty target value: an integer that limits the hash value of the current block to be less than this value to ensure that the proof of work of the current block complies with network rules. 101 | 102 | These components will be integrated together and calculated through a hash function to produce a 32-byte block header hash value, which is used to uniquely identify the block. Every node on the blockchain can use this hash to verify the validity of the block and add it to the blockchain. 103 | 104 | ### 13. What types of nodes are there in Ethereum and what are the usage scenarios of these nodes? 105 | ### 14.What is EIP4484? Please briefly explain 106 | ### 15.What is EIP4337? Please briefly explain 107 | ### 16. Let’s talk about Ethereum’s Bloom filter in detail 108 | Bloom Filter in Ethereum is a data structure used to quickly retrieve data. It is often used in state transition data storage (State Trie) in Ethereum to improve the efficiency of state queries. 109 | 110 | A Bloom filter consists of a bit array and a set of hash functions. The length of the bit array is fixed, usually several million to tens of millions of bits. The hash function maps the input to a position on the bit array and sets the value of that position to 1. When querying for an element, the element is input into the hash function. If the positions corresponding to all hash functions are 1, the element may be in the data set; otherwise, the element must not be in the data set. 111 | 112 | Ethereum's bloom filter is used to record whether a transaction address has been used before. In a block, each transaction contains a sender address and a receiver address. When a block is processed, the addresses of all transactions are added to the bloom filter. When you need to query whether an address has been used in the future, you only need to input the address into the Bloom filter. If the positions corresponding to all hash functions are 1, the address may have been used; otherwise, the address The address must not have been used before. 113 | 114 | The advantage of the Bloom filter is that it can quickly determine whether an element is in the data set without traversing the entire data set. However, due to the existence of hash functions, Bloom filters have a certain false positive rate. If an element is misjudged to be in the data set, it may cause unnecessary queries and reduce system performance. Therefore, when using Bloom filters, it is necessary to weigh the relationship between false positive rate and performance and choose appropriate parameter configuration. 115 | 116 | ### 17. How to prevent misjudgments of Bloom filters 117 | The misjudgment of Bloom filter is caused by the collision caused by the hash function. Therefore, in order to reduce the possibility of misjudgment, the following methods can be adopted: 118 | 119 | Increase the number of hash functions: Increasing the number of hash functions can reduce the probability of collisions, thereby reducing the possibility of misjudgment. 120 | 121 | Increase the size of the bloom filter: Increasing the size of the bloom filter allows it to accommodate more elements, thereby reducing the possibility of false positives. 122 | 123 | Adjust the parameters of the Bloom filter: You can adjust the parameters of the Bloom filter according to the specific situation, such as the type of hash function, the size of the Bloom filter, etc. 124 | 125 | Combining multiple bloom filters: Multiple bloom filters can be combined to further reduce the possibility of false positives. 126 | 127 | Combine with other data structures: Bloom filters can be combined with other data structures such as red-black trees, hash tables, etc. to improve accuracy. 128 | 129 | ### 18. Let’s talk about the ETH Casper FFG consensus mechanism in detail 130 | The Ethereum Casper FFG (Friendly Finality Gadget) consensus mechanism is a hybrid consensus mechanism upgraded from Ethereum 2.0. It combines the advantages of Proof of Stake (PoS) and Proof of Work (PoW) algorithms and aims to improve the security, scalability and decentralization of the network. 131 | 132 | In Casper FFG, verification nodes need to lock a certain amount of Ethereum (ETH) as collateral to be eligible to participate in bookkeeping. These verification nodes are called validators, and their task is to verify transactions and package blocks. Unlike pure PoS, Casper FFG still requires PoW miners to produce blocks, but they no longer directly generate new ether coins, but receive transaction fees as rewards. 133 | 134 | Casper FFG introduces a concept called "bonded validator voting". Validators can choose to vote on a block to indicate whether they approve of it. If more than 2/3 of the stake (i.e. the amount of staked ether) supports a block, then the block is considered final and the transaction is determined to be irreversible. This is the so-called "finality", which is an important feature of the PoS algorithm and can ensure the security of the network. 135 | 136 | Casper FFG also introduces a concept called a "punitive mechanism". If a validator performs poorly or acts intentionally evil, they may lose their staked ether, which is known as "slashing." If a validator votes for two Conflicting blocks will also be punished. This penalty mechanism can effectively prevent misbehavior of validators and maintain the security and stability of the network. 137 | 138 | Overall, Casper FFG aims to improve the security and scalability of the Ethereum network while maintaining its decentralized nature. 139 | 140 | ### 19. What do the epoch and slot of the beacon chain represent respectively, and how many slots does an epoch contain? 141 | The beacon chain is a major chain in Ethereum 2.0, which adopts the PoS consensus mechanism. In the beacon chain, time is divided into a series of epochs and slots. 142 | 143 | An epoch refers to a period of time, which consists of a certain number of slots. In the beacon chain, each epoch lasts 6.4 minutes and each slot lasts 12 seconds. Therefore, an epoch contains 32 slots. 144 | 145 | In the beacon chain, each slot is composed of a validator, and the validator needs to perform verification work and package blocks in each slot. Over time, validators will take turns participating in different slots to ensure the security and decentralization of the network. 146 | 147 | By dividing time into epochs and slots, the beacon chain can achieve a more efficient consensus mechanism and also facilitate validators to take turns to verify. 148 | 149 | ### 20. In what state is the beacon chain block irreversible? 150 | In the beacon chain, a block is considered irreversible when the following conditions are met: 151 | 152 | Blocks must be fully confirmed: A block is considered fully confirmed when the voting results of all validators on the block have been recorded on the chain, and more than 2/3 of the validators in the voting results have confirmed the block. 153 | 154 | Blocks must be in a finality epoch: a finality epoch is a confirmed epoch in which every slot has been voted for and at least 2/3 of the validators participated in the voting. Only blocks in finality epochs are considered irreversible because they have received sufficient confirmations and support. 155 | 156 | Therefore, a block containing a fully confirmed block is only irreversible in an epoch in which finality voting has been completed. In this case, the block is considered the final state on the blockchain and transactions within it cannot be changed or reversed. 157 | 158 | ### 21. How to calculate the attack cost of the beacon chain 159 | Recovering a finalized block requires at least 1/3 of the validators to burn their deposits. Calculate the attack cost according to this formula: Attack cost: 1/3 * (number of validators * 32) * ETH/USDT structure 160 | 161 | ### 22. For wallets and layer2 applications, how to set the security height of beacon chain deposits and withdrawals more appropriately? 162 | ### 23. Briefly describe the role of EIP-3651 163 | Speaking of EIP-3651, we must first introduce a change in EIP-2929: when the target is not in accessed_addresses, charge COLD_ACCOUNT_ACCESS_COST (cold account access cost) gas and add the address to accessed_addresses. Otherwise, WARM_STORAGE_READ_COST (warm storage read cost) gas is charged, and warm read gas consumption is relatively low. 164 | 165 | Nowadays, COINBASE direct payment is becoming more and more popular, but the current price of accessing COINBASE is higher; this is due to the fact that under the access list framework introduced by EIP-2929, COINBASE calculates gas according to the cold account access cost. In EIP- 3651 Afterwards, accessed_addresses will include the address returned by COINBASE (0x41). 166 | 167 | Benefit: After modification, COINBASE will reduce gas consumption when paying ERC20 tokens. 168 | 169 | ### 24. Briefly describe the role of EIP-3855 170 | EIP-3855, introduces a new instruction (0x5f) to push the constant value 0 onto the stack. The instruction set for PUSH in the Yellow Book currently only has PUSH1-PUSH32, which is used to push 1 byte onto the stack and 32 bytes onto the stack. 171 | 172 | The existing instruction implementation pushes the 0 value onto the stack by executing PUSH1 0 , which costs 3 gas in the runtime and an additional 200 gas (2 bytes of storage cost) 173 | 174 | With the PUSH0 instruction, there is no need to consume this additional 200 gas. 175 | 176 | Benefits: Currently, about 11% of PUSH operations just push 0, so this EIP can save a certain amount of gas after execution, and can also slightly improve the existing TPS of Ethereum. 177 | 178 | ### 25. Briefly describe the role of EIP-3860 179 | The current maximum initcode is MAX_CODE_SIZE: 24576 (EIP-170), and the new initcode's maximum is (MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE = 49152), which means that the contract size can be doubled, and contract developers can deploy richer functions. (Excessive contract code will lead to unsuccessful deployment. PS: The L2 project has also been partially modified to support a higher contract size limit) 180 | 181 | In addition, a 2 gas fee is introduced for each 32-byte initcode chunk to represent the cost of jumpdest-analysis. Because during contract creation, the client must perform jumpdest analysis on the initcode before execution. Execution work scales linearly with the size of the initcode. 182 | 183 | This means that initcode will cost 0.0625 gas per byte, and the contract deployment gas cost will increase slightly. 184 | 185 | Benefits: The gas fee for contract deployment is slightly increased, but the contract size can be doubled, allowing contract developers to write richer functional codes. 186 | 187 | ### 26. Briefly describe the role of EIP-4895 188 | The main content is to determine the main process of withdrawing money from the beacon chain to EVM. After the deployment is completed, the Ethereum beacon chain pledge withdrawal function will be activated. 189 | 190 | Benefits: Activate the Ethereum Beacon Chain pledge withdrawal function. 191 | 192 | ### 27. Explanation of Ethereum’s state tree, transaction tree, block tree and storage tree 193 | The state tree is one of the most important data structures in Ethereum. It is used to store the status of all accounts in the Ethereum network. Each Ethereum account has a corresponding status entry, which includes information such as account address, balance, code and storage data. The state tree is a Merkle-Patricia tree that stores all account states in the leaf nodes of the tree. Each leaf node contains a hash value of the account status, which is calculated from the account's address and other status data. Through the status tree, the Ethereum network can quickly verify whether the status of an account is legal. 194 | 195 | State Trie: The state tree is one of the most important data structures in Ethereum. It is used to store the status of all accounts in the Ethereum network. Each Ethereum account has a corresponding status entry, which includes information such as account address, balance, code and storage data. The state tree is a Merkle-Patricia tree that stores all account states in the leaf nodes of the tree. Each leaf node contains a hash value of the account status, which is calculated from the account's address and other status data. Through the status tree, the Ethereum network can quickly verify whether the status of an account is legal. , the entire state tree is constructed by hashing calculations on each account's nonce (used to prevent transaction replay), balance, contract code and other information. 196 | 197 | Transaction Trie: A Merkle tree structure used to store all transactions. Each leaf node represents the hash value of a transaction, and each parent node represents the hash value of the hash values ​​of the two child nodes below it. This tree structure can be used to verify whether a transaction exists in a certain block and to verify the correctness of the transaction. 198 | 199 | Block Trie: It is also a Merkle Patricia tree, used to store the hash values ​​of all blocks. Each leaf node represents the hash value of a block, and each parent node represents the hash value of the hashes of the two child nodes below it. This tree structure can be used to verify whether the block is valid and whether it belongs to the main chain of Ethereum. 200 | 201 | Storage Trie: used to store data in Ethereum contracts, including variables, mappings, arrays, etc. The storage tree is a Merkle Patricia tree, with each node representing a key-value pair. This tree structure can be used to verify the correctness and integrity of contract data. 202 | -------------------------------------------------------------------------------- /05-ethereum/readme.md: -------------------------------------------------------------------------------- 1 | # 以太坊 2 | 3 | ### 1.详细说明一下以太坊交易打包的过程 4 | 5 | 以太坊是一种区块链网络,它允许用户发送和接收以太币(ETH)以及其他代币,并在链上执行智能合约。这些交易需要被打包进区块中,并由矿工来验证和确认。 6 | 7 | 以下是以太坊交易打包的过程的详细说明: 8 | 9 | - 创建交易:交易是由发送者创建并签名的。它包括发送方的以太币地址、接收方的以太币地址、交易金额以及一些可选的交易数据。 10 | 11 | - 广播交易:交易需要被广播到网络上的所有节点。这可以通过将交易发送给一个节点,然后该节点将其广播给它所知道的其他节点。 12 | 13 | - 交易池:交易被广播后,它们将进入待处理的交易池。在交易池中,交易会等待被打包到新的区块中。 14 | 15 | - 打包交易:矿工会从交易池中选择一些交易,并将它们打包到新的区块中。矿工通常会选择交易费用(gas 16 | fee)最高的交易,因为他们可以从中获得更高的奖励。一旦矿工已经将交易打包进区块,它们就被认为是已经被确认并执行了。 17 | 18 | - 确认交易:一旦新的区块被创建并添加到区块链上,其中包括新的交易,那么这些交易就被认为是已经被确认并执行了。 19 | 20 | - 交易费用:交易费用是由发送方支付的,它是通过消耗一定量的gas来计算的。gas是以太坊网络中的计量单位,用于衡量执行交易所需的计算资源。矿工会收取这些交易费用作为奖励,以鼓励他们参与打包交易的过程。 21 | 22 | 总之,以太坊交易打包的过程涉及创建交易、广播交易、交易池、打包交易、确认交易以及交易费用。这个过程是自动化的,并且由区块链网络中的节点和矿工来执行。 23 | 24 | ### 2.以太坊为什么会分叉,遇到分叉钱包,layer2 类似的应用该怎么处理 25 | 26 | ### 3.以太坊底层用的数据结构是什么,可以详细说一下 27 | 28 | 以太坊底层使用了多种不同的数据结构,其中最重要的是Merkle Patricia Tree和状态数据库。 29 | 30 | Merkle Patricia 31 | Tree是一种高效的数据结构,用于存储和管理以太坊账户和交易的状态。它是一种树形结构,其中每个节点都包含一个哈希值和一个键值对。键值对用于存储账户地址、交易哈希等信息,而哈希值用于快速验证数据的完整性和一致性。Merkle 32 | Patricia Tree被广泛应用于以太坊的账户状态树和交易状态树中。 33 | 34 | 另一个重要的数据结构是状态数据库。它是以太坊的全局状态存储,包括所有账户的余额、代码、存储和合约状态等信息。状态数据库的实现基于LevelDB,可以高效地存储和检索状态数据。 35 | 36 | 除此之外,以太坊还使用了其他数据结构,如Bloom Filter和B-Tree,用于优化区块链的性能和效率。Bloom 37 | Filter用于快速检索一个元素是否存在于某个集合中,而B-Tree则是一种自平衡树,常用于数据库索引中。 38 | 39 | ### 4.以太坊的钱包使用的是哪些密码学算法,并简要说明一下这些算法的原理 40 | 41 | ### 5.简单说明一下以太坊的 gas 机制 42 | 43 | ### 6.了解以太坊的 EIP155 吗,它主要是为了解决什么问题而存在的 44 | 45 | ### 7.了解以太坊的 Eip712 吗,它主要是为了解决什么问题而存在的 46 | 47 | EIP712是以太坊上的一种标准,它定义了一种消息结构和签名方法,用于在去中心化应用程序(DApp)中进行安全的消息验证和授权。 48 | 49 | 具体来说,EIP712允许DApp在不泄露私钥的情况下验证用户对某些操作的授权。它通过使用预定义的消息格式和签名方法,确保消息的完整性和身份验证,以及防止重放攻击和其他欺诈行为。 50 | 51 | EIP712的另一个重要用途是使DApp能够与智能合约进行更安全、更直接的交互。通过使用EIP712标准定义的消息格式,智能合约可以验证来自DApp的消息,从而保护合约不受欺诈性攻击。 52 | 53 | 总之,EIP712为以太坊生态系统中的DApp和智能合约提供了一种安全且标准化的消息验证和授权方法。 54 | 55 | ### 8.了解 EIP1155 吗,它主要是为了解决什么问题而存在的 56 | 57 | ### 9. 简要说明 Ethereum 的 EVM 的底层运行机制 58 | 59 | 以太坊(Ethereum)的EVM(Ethereum Virtual Machine)是以太坊网络的核心组件之一,它是一个基于栈的虚拟机,为智能合约提供了运行环境。以下是EVM的底层运行机制的简要说明: 60 | 61 | 1.指令集:EVM采用了一组精简的指令集,包括算术操作、比较操作、逻辑操作、内存操作、栈操作等。每个指令都有一个操作码和一些操作数,当指令被执行时,EVM会从指令集中读取操作码,并按照操作数进行操作。 62 | 63 | 2.栈:EVM的操作是基于栈的,EVM中有一个栈,所有操作数都被压入栈中,而不是使用寄存器。栈的大小是固定的,不能调整,因此智能合约需要根据栈的大小进行编写。 64 | 65 | 3.存储:EVM中还有一个存储器,可以将数据存储在其中,存储器是按字(32字节)进行分配的。存储器可以通过合约的地址和存储器索引进行访问,每个存储器单元都有一个初始值为零。 66 | 67 | 4.内存:EVM还有一个内存区域,可以用来存储临时数据。EVM中的指令可以在内存中读取和写入数据,但是内存的大小是动态调整的,每次增加的大小是32个字节。 68 | 69 | 5.状态转换:当一个智能合约被调用时,EVM会将它的代码加载到内存中,并根据指令逐个执行。当执行指令时,EVM会改变合约的状态,例如更改存储器中的值、将数据压入栈中等。一旦智能合约执行完毕,EVM会将最终状态保存到以太坊区块链中。 70 | 71 | ### 10.EVM 存储器实现原理是什么 72 | 73 | EVM(以太坊虚拟机)是以太坊区块链上的虚拟机,用于执行智能合约的代码。在 EVM 中,存储器是一个字节数组,用于在执行智能合约时存储临时数据。 74 | 75 | EVM 存储器的实现原理如下: 76 | 77 | 存储器是一个由字节数组构成的内存空间。在 EVM 中,存储器的大小是以字节为单位的,最大大小为2^256个字节。 78 | 79 | 存储器是永久存储的,意味着在智能合约的生命周期内都可以被访问到。 80 | 81 | 存储器的每个字节都有一个唯一的索引值,称为存储器地址。存储器地址是一个以太坊地址,范围从0到2^256-1。 82 | 83 | 智能合约可以通过存储器地址访问存储器中的数据。存储器地址可以在智能合约的代码中直接使用,也可以通过函数参数传递。 84 | 85 | 存储器的读取和写入操作都是昂贵的操作,因此在智能合约中应该尽量减少存储器的使用量。 86 | 87 | 存储器的读取操作是免费的,但写入操作需要支付 gas 费用。写入操作的 gas 费用取决于写入的字节数。 88 | 89 | 总之,EVM 90 | 存储器是一个由字节数组构成的内存空间,用于在执行智能合约时存储临时数据。存储器地址唯一地标识存储器中的每个字节,智能合约可以通过存储器地址访问存储器中的数据。在智能合约中应该尽量减少存储器的使用量,因为读取和写入操作都是昂贵的操作。 91 | 92 | ### 11. 简要说明以太坊节点的区块打包机制 93 | 94 | 在以太坊网络中,每个区块包含一组交易,这些交易可以是普通交易或智能合约调用。当一个节点在以太坊网络上挖出一个新的区块时,它需要执行以下步骤来打包交易: 95 | 96 | - 从交易池中选择一组待打包的交易,通常会选择交易费用最高的交易优先打包。 97 | 98 | - 将这些交易按照一定的规则排序,以确保每个节点都有相同的交易排序,从而保证所有节点达成共识。 99 | 100 | - 计算区块头的哈希值,并将其与交易数据一起写入新的区块中。 101 | 102 | - 将新区块广播到整个网络,以便其他节点进行验证和确认。 103 | 104 | 在以太坊中,智能合约是用 Solidity 等高级编程语言编写的,并通过 EVM 运行。当智能合约被调用时,EVM 105 | 将会执行合约代码,并将结果写入区块链。智能合约调用也可以作为一个交易被打包到区块中。 106 | 107 | ### 12.以太坊的区块头的计算机制什么 108 | 109 | 以太坊的区块头计算机制是一种基于哈希的机制,它由以下几个部分组成: 110 | 111 | - 父区块哈希:指向当前区块所在链中前一个区块的哈希值,用于保证区块链的不可篡改性。 112 | 113 | - 状态根哈希:表示当前区块中所有账户状态的根哈希,用于确保在当前区块中发生的所有交易的合法性。 114 | 115 | - 交易根哈希:表示当前区块中所有交易的哈希树根节点的哈希值,用于验证区块中所有交易的有效性。 116 | 117 | - 时间戳:指定区块的创建时间戳,用于验证区块的时序性。 118 | 119 | - 难度目标值:一个整数,用于限制当前区块的哈希值必须小于该值,从而保证当前区块的工作量证明符合网络规则。 120 | 121 | 这些组成部分会被整合在一起,通过哈希函数计算得出一个32字节的区块头哈希值,用于唯一标识该区块。区块链上的每个节点都可以使用这个哈希值来验证该区块的有效性,并将其添加到区块链中。 122 | 123 | ### 13.以太坊有哪些类型的节点,这些节点的使用场景是什么 124 | 125 | ### 14.EIP4484 是什么,请简要说明 126 | 127 | ### 15.EIP4337 是什么,请简要说明 128 | 129 | ### 16.详细说一下以太坊的布隆过滤器 130 | 131 | 以太坊中的布隆过滤器(Bloom Filter)是一种用于快速检索数据的数据结构。它通常用于以太坊中的状态转换数据存储(State 132 | Trie)中,以提高状态查询的效率。 133 | 134 | 布隆过滤器由一个位数组和一组哈希函数组成。位数组的长度是固定的,通常为几百万到几千万个比特。哈希函数将输入映射到位数组上的位置,并将该位置的值设置为 135 | 1。当查询某个元素时,将该元素输入到哈希函数中,如果所有哈希函数所对应的位置都为 1,则该元素可能在数据集中;否则,该元素一定不在数据集中。 136 | 137 | 以太坊的布隆过滤器被用于记录一个交易地址是否曾经被使用过。在区块中,每个交易都会包含一个发送者地址和一个接收者地址。当一个区块被处理时,所有交易的地址都被添加到布隆过滤器中。当以后需要查询某个地址是否曾经被使用过时,只需要将该地址输入到布隆过滤器中,如果所有哈希函数所对应的位置都为 138 | 1,则该地址可能被使用过;否则,该地址一定没有被使用过。 139 | 140 | 布隆过滤器的优点是可以快速判断一个元素是否在数据集中,而不需要遍历整个数据集。然而,由于哈希函数的存在,布隆过滤器有一定的误判率。如果一个元素被误判为在数据集中,可能会导致不必要的查询,降低系统性能。因此,在使用布隆过滤器时,需要权衡误判率和性能之间的关系,选择适当的参数配置。 141 | 142 | ##### 布隆过滤器的风险概览 143 | 144 | | 风险 | 原因 | 影响 | 缓解措施 | 145 | |:-------------------:|:--------:|:----------:|:---------------:| 146 | | 误报(False Positive) | 哈希冲突 | 额外开销或错误判断 | 增加位数组大小,优化哈希函数 | 147 | | 无法删除 | 位共享 | 动态场景失效 | 使用计数布隆过滤器 | 148 | | 假阴性(False Negative) | 位数组被篡改 | 漏判关键数据 | 保护位数组完整性 | 149 | | 容量超载 | 插入超出设计容量 | 误报率失控 | 预估容量或用可扩展版本 | 150 | | 哈希函数质量 | 分布不均匀 | 性能下降 | 选择高质量哈希函数 | 151 | | 隐私泄露 | 模式分析 | 信息暴露 | 提高误报率或加密键 | 152 | | 不可逆性 | 单向设计 | 无法恢复数据 | 配合其他数据结构 | 153 | | 场景误用 | 不匹配需求 | 系统失效 | 评估需求,选择合适工具 | 154 | 155 | ### 17. 如何防止布隆过滤器的误判 156 | 157 | 布隆过滤器的误判是由于哈希函数产生的冲突导致的。因此,为了减少误判的可能性,可以采取以下方法: 158 | 159 | 增加哈希函数的数量:增加哈希函数的数量可以减少冲突的概率,从而减少误判的可能性。 160 | 161 | 增加布隆过滤器的大小:增加布隆过滤器的大小可以使其能够容纳更多的元素,从而减少误判的可能性。 162 | 163 | 调整布隆过滤器的参数:可以根据具体情况调整布隆过滤器的参数,如哈希函数的种类、布隆过滤器的大小等。 164 | 165 | 组合多个布隆过滤器:可以将多个布隆过滤器组合起来使用,从而进一步降低误判的可能性。 166 | 167 | 结合其他数据结构:可以将布隆过滤器与其他数据结构结合使用,如红黑树、哈希表等,以提高准确性。 168 | 169 | ### 18.详细说一下 ETH Casper FFG共识机制 170 | 171 | 以太坊 Casper FFG (Friendly Finality Gadget) 共识机制是以太坊2.0升级后的一种混合共识机制。它结合了权益证明 (Proof of 172 | Stake, PoS) 和工作量证明 (Proof of Work, PoW) 两种算法的优势,旨在提高网络的安全性、可扩展性和去中心化程度。 173 | 174 | 在 Casper FFG 中,验证节点需要锁定一定数量的以太币 (ETH) 作为抵押品,来获得参与记账的资格。这些验证节点称为验证人,他们的任务是验证交易并打包区块。与纯 175 | PoS 不同,Casper FFG 仍然需要 PoW 矿工来生产区块,但他们不再直接产生新的以太币,而是获取交易手续费作为奖励。 176 | 177 | Casper FFG 引入了一个称为 "权益投票" (bonded validator voting) 178 | 的概念。验证人可以选择对一个区块进行投票,以表明他们是否认可该区块。如果超过2/3的权益 (即抵押的以太币数量) 179 | 支持某个区块,那么该区块被认为是最终确认的,交易也将被确定为不可逆转。这就是所谓的 "最终性" (finality),它是 PoS 180 | 算法的一个重要特征,可以保证网络的安全性。 181 | 182 | Casper FFG 还引入了一个称为 "惩罚机制" (punitive mechanism) 183 | 的概念。如果验证人表现不佳或故意作恶,他们可能会失去抵押的以太币,这就是所谓的 "抵押惩罚" (slashing) 184 | 。如果验证人投票支持两个相互矛盾的区块,也会被惩罚。这种惩罚机制可以有效地防止验证人的不端行为,维护网络的安全性和稳定性。 185 | 186 | 总的来说,Casper FFG 的目标是在提高以太坊网络的安全性和可扩展性的同时,保持其去中心化的特性。 187 | 188 | ### 19.信标链的 epoch 和 slot 分别代表什么,一个 epoch 包含多少个 slot 189 | 190 | 信标链是以太坊2.0中的一条主要链,它采用了 PoS 共识机制。在信标链中,时间被划分为一系列 epoch 和 slot。 191 | 192 | 一个 epoch 指的是一段时间,它由一定数量的 slot 组成。在信标链中,每个 epoch 的持续时间为 6.4 分钟,每个 slot 的持续时间为 12 193 | 秒。因此,一个 epoch 包含 32 个 slot。 194 | 195 | 在信标链中,每个 slot 都由一个验证人组成,验证人需要在每个 slot 中进行验证工作并打包区块。随着时间的推移,验证人会轮流参与不同的 196 | slot,以确保网络的安全性和去中心化程度。 197 | 198 | 通过划分时间为 epoch 和 slot,信标链可以实现更高效的共识机制,同时也方便验证人进行轮流验证工作。 199 | 200 | ### 19.信标链区块在什么状态下是不可逆转的 201 | 202 | 在信标链中,一个区块在满足以下条件时被认为是不可逆转的: 203 | 204 | 区块必须被完全确认:一个区块被认为是完全确认的,需要满足所有验证人在该区块上的投票结果已经被记录在链上,并且投票结果中超过2/3的验证人已经确认了该区块。 205 | 206 | 区块必须在一个最终性 epoch 中:一个最终性 epoch 是一个已经被确认的 epoch,其中每个 slot 都已经被投票确认,并且至少有 2/3 207 | 的验证人参与了投票。只有在最终性 epoch 中的区块才被认为是不可逆转的,因为它们已经获得了足够的确认和支持。 208 | 209 | 因此,只有在一个已经完成最终性投票的 epoch 中,包含一个完全确认的区块才是不可逆转的。在这种情况下,该区块被视为区块链上的最终状态,其中的交易将无法被更改或撤销。 210 | 211 | ### 20.信标链的攻击成本怎么计算 212 | 213 | 恢复一个已最终确认的区块需要至少 1/3 的验证者烧掉他们的存款,根据这个公式计算一下攻击成本: 214 | 攻击成本:1/3 * (验证者数量 * 32) * ETH/USDT架构 215 | 216 | ### 21.对于钱包和 layer2 应用来说,信标链充提安全高度怎么设定比较合适 217 | 218 | ### 22. 简述 EIP-3651 的作用 219 | 220 | 说到 EIP-3651,得先介绍一下 EIP-2929 一个改动:当目标不在 accessed_addresses 中,收取 221 | COLD_ACCOUNT_ACCESS_COST(冷账户访问成本)gas,并将地址添加到 accessed_addresses。否则,收取 222 | WARM_STORAGE_READ_COST(暖存储读取成本)gas,暖读取消耗 gas 相对较低。 223 | 224 | 如今 COINBASE 直接支付正变得越来越受欢迎,但目前访问 COINBASE 的价格较高;这是由于在 EIP -2929 引入的访问列表框架下,COINBASE 225 | 是按冷账户访问成本计算 gas 的,在 EIP-3651 后,accessed_addresses 将包括 COINBASE (0x41) 返回的地址。 226 | 227 | 好处:修改后,COINBASE 在支付 ERC20 代币时会减少 gas 消耗。 228 | 229 | ### 23. 简述 EIP-3855 的作用 230 | 231 | EIP-3855,引入一条新指令(0x5f),将常量值 0 压入堆栈,黄皮书关于 PUSH 的指令集,目前只有 PUSH1-PUSH32,作用是将 1 字节压入堆栈,到 232 | 32 字节压入堆栈 233 | 234 | 现有指令实现将 0 值压入堆栈需要通过执行 PUSH1 0 ,在 runtime 中需要消耗 3 gas,并且额外需要消耗 200 gas(2 字节的存储成本) 235 | 236 | 有了 PUSH0 指令后,就不需要消耗这额外的 200 gas了。 237 | 238 | 好处:目前大约有 11% 的 PUSH 操作只是压入 0,因此这个 EIP 执行后可以节省一定量的 gas,也能稍微提高以太坊的现有的 TPS。 239 | 240 | ### 24. 简述 EIP-3860 的作用 241 | 242 | 目前 initcode 的最大为 MAX_CODE_SIZE: 24576(EIP-170),新的 initcode 的最大为 (MAX_INITCODE_SIZE = 2 * MAX_CODE_SIZE = 243 | 49152),这意味合约大小可以扩展一倍,合约开发者可以部署更丰富的功能。(合约代码过大会导致部署不成功,PS:L2 244 | 项目也部分已修改,支持更高的合约大小上限) 245 | 246 | 此外为每 32 字节的 initcode chunk引入 2 个 gas 费用,以表示 jumpdest-analysis 的成本。因为在合约创建期间,客户端必须在执行之前对 247 | initcode 执行 jumpdest 分析。执行工作与 initcode 的大小成线性关系。 248 | 249 | 这意味着 initcode 每字节将添加成本 0.0625 gas,合约部署 gas 成本微微上涨。 250 | 251 | 好处:合约部署 gas 费微微上调,但合约大小可以扩展一倍,合约开发者写更丰富的功能代码。 252 | 253 | ### 25. 简述 EIP-4895 的作用 254 | 255 | 主要内容是确定信标链提款至 EVM 的主要流程,部署完成后,以太坊信标链质押提款功能将被激活。 256 | 257 | 好处:激活以太坊信标链质押提款功能。 258 | 259 | ### 26. 以太坊的状态树,交易树,区块树和存储树解释 260 | 261 | 状态树是以太坊中最重要的数据结构之一,它用于存储以太坊网络中的所有账户状态。每个以太坊账户都有一个与之对应的状态条目,其中包括账户地址、余额、代码和存储数据等信息。状态树是一棵Merkle-Patricia树,它将所有账户状态存储在树中的叶子节点。每个叶子节点包含一个账户状态的哈希值,这个哈希值是由账户的地址和其它状态数据计算得到的。通过状态树,以太坊网络可以快速验证一个账户的状态是否合法。 262 | 263 | 状态树(State Trie) 264 | :状态树是以太坊中最重要的数据结构之一,它用于存储以太坊网络中的所有账户状态。每个以太坊账户都有一个与之对应的状态条目,其中包括账户地址、余额、代码和存储数据等信息。状态树是一棵Merkle-Patricia树,它将所有账户状态存储在树中的叶子节点。每个叶子节点包含一个账户状态的哈希值,这个哈希值是由账户的地址和其它状态数据计算得到的。通过状态树,以太坊网络可以快速验证一个账户的状态是否合法。,通过对每个账户的nonce(用于防止交易重播)和余额、合约代码等信息进行哈希计算,来构建整个状态树。 265 | 266 | 交易树(Transaction Trie) 267 | :用于存储所有交易的Merkle树结构,每个叶子节点代表一笔交易的哈希值,每个父节点代表它下面的两个子节点的哈希值的哈希值。这个树结构可以用于验证交易是否在某个区块中存在,以及验证交易的正确性。 268 | 269 | 区块树(Block Trie):也是一个Merkle 270 | Patricia树,用于存储所有区块的哈希值。每个叶子节点代表一个区块的哈希值,每个父节点代表它下面的两个子节点的哈希值的哈希值。这个树结构可以用于验证区块是否有效,以及验证区块是否属于以太坊的主链。 271 | 272 | 存储树(Storage Trie):用于存储以太坊合约中的数据,包括变量、映射和数组等。存储树是一个Merkle 273 | Patricia树,每个节点代表一个键值对。这个树结构可以用于验证合约数据的正确性和完整性。 274 | 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /06-solidity/EnReadme.md: -------------------------------------------------------------------------------- 1 | # solidity 2 | #### Chaineye solidity interview questions are collected for those who want to learn. 3 | 4 | Twitter: @seek_web3 5 | 6 | Chainey community: official website chaineye.info | Chaineye Rust tutorial | WeChat: LGZAXE, add WeChat and then pull the community 7 | 8 | All code and tutorials are open source on github: https://github.com/0xchaineye/chaineye-blockchain-interview 9 | 10 | ### 1. In actual production, what should be done if you want to ensure that the contract addresses of the test network and the main network are consistent? 11 | Option 1: The contract address is determined by nonce and address, so just ensure that nonce and address are the same. 12 | 13 | new_address = hash(sender, nonce) 14 | Option 2: Using create2, the whole idea behind this opcode is to make the resulting address independent of future events. No matter what happens on the blockchain, contracts can always be deployed on pre-computed addresses. 15 | 16 | The new address is a function of: 17 | 18 | 0xFF, a constant to prevent collisions with CREATE 19 | 20 | Sender's own address 21 | 22 | Salt (any value provided by the sender) 23 | 24 | The bytecode of the contract to be deployed 25 | 26 | new_address = hash(0xFF, sender, salt, bytecode) 27 | CREATE2 guarantees that if deployed using and provided sender, it will be stored in .bytecodeCREATE2saltnew_address 28 | 29 | Because the bytecode is included in this calculation, other agents can rely on the fact that if the contract is ever deployed to new_address, it will be one they know. This is the key concept behind counterfactual deployment. 30 | 31 | ### 2. Pure and view usage principles and scenarios of solidity smart contracts 32 | pure and view principle 33 | pure: Do not read or modify the variables on the block, and use the local CPU resources to calculate our functions. So it is easy to understand without consuming any resources. 34 | 35 | view: But since view needs to read the value on the blockchain, why doesn’t it need to consume gas? 36 | 37 | It's actually very simple, because as a full node, all information will be saved synchronously and locally. 38 | 39 | Then if we want to check the resources on the blockchain, we can also directly query the data on a full node. 40 | 41 | I don’t need nodes all over the world to know. They all handle this matter at the same time. I also don’t need to record the call to this function on the blockchain. 42 | 43 | So the view still doesn't consume gas. 44 | 45 | view: can be called freely as it just "views" the state of the blockchain without changing it 46 | 47 | pure: can also be called freely, neither reading nor writing to the blockchain 48 | 49 | ### 3. How to convert fixed byte array to dynamic byte array 50 | To convert a fixed-length byte array into a dynamic-length byte array, you need to first create a dynamic array and assign values ​​one by one. 51 | ``` 52 | pragma solidity ^0.4.23; 53 | contract fixTodynamic{ 54 | bytes6 name = 0x6a6f6e736f6e; 55 | 56 | function Todynamic() view public returns(bytes){ 57 | //return bytes(name); 58 | bytes memory newName = new bytes(name.length); 59 | 60 | //for loop assigns values ​​one by one 61 | for(uint i = 0;iAAA) ZZZ; 423 | uint256 public C; 424 | uint256 public D; 425 | } 426 | ``` 427 | 428 | V2 版本的合约 429 | 430 | ``` 431 | contract A { 432 | struct AAA { 433 | uint256 public A; 434 | uint256 public B; 435 | uint256 public X; 436 | uint256 public Y; 437 | } 438 | uint256 public Z; 439 | mapping(uint256=>AAA) ZZZ; 440 | uint256 public C; 441 | uint256 public D; 442 | } 443 | ``` 444 | 445 | - 上面做法没有问题 446 | 447 | #### 32.代码题二 448 | 449 | V1 版本的合约, 运行很久了 450 | 451 | ``` 452 | contract A { 453 | struct AAA { 454 | uint256 public A; 455 | uint256 public B; 456 | } 457 | uint256 public Z; 458 | AAA ZZZ; 459 | uint256 public C; 460 | uint256 public D; 461 | } 462 | ``` 463 | 464 | V2 版本的合约 465 | 466 | ``` 467 | contract A { 468 | struct AAA { 469 | uint256 public A; 470 | uint256 public B; 471 | uint256 public X; 472 | uint256 public Y; 473 | } 474 | uint256 public Z; 475 | AAA ZZZ; 476 | uint256 public C; 477 | uint256 public D; 478 | } 479 | ``` 480 | 481 | 482 | ### 47.谈谈你对 address(this), tx.origin 和 msg.sender 的语句解释 483 | 484 | 485 | ### 48.有那些方式可以保证不同网络中的合约地址一致,部署合约的时候怎么玩,create 和 create2 底层实现 486 | 487 | 488 | ### 49.transfer, call 和 send 的区别联系 489 | 490 | 491 | ### 50.合约的升级方式有哪些,详细说明一下他们之间的区别 492 | 493 | 494 | ### 51.怎么理解 ABI,合约,CallData, Bindings 区别联系 495 | 496 | 497 | ### 52.什么是函数选择器?它有哪些使用场景 498 | 499 | #============================================================================================================================ 500 | # EIP 协议篇 501 | #============================================================================================================================= 502 | 503 | ### 53.ERC20, ERC721, ERC1155 协议详解 504 | 505 | 506 | ### 54.简述 EIP4844 507 | 508 | 509 | ### 55.简述 ERC4337--AA 钱包 510 | 511 | 512 | ### 56.什么是 EIP-1167 最小代理,举例说明它的应用场景 513 | 514 | 515 | ### 57.EIP4788 作用是什么 516 | 517 | 518 | ### 58.请谈谈你对 ERC-712 和 ERC-2612 的理解, permit 和 permit2 519 | 520 | 521 | ### 59.用于区分不同 EVM 链的是协议是那一个 522 | 523 | - EIP155 524 | 525 | ### 60.简要说明 ERC 1559 作用 526 | 527 | 528 | ### 61.简要说明 ERC 1271 作用 529 | 530 | #============================================================================================================================ 531 | # 项目篇(桥,LSD,再质押,Gas Oracle,Price Feed,Fraud proof,Zk Verifier) 532 | #============================================================================================================================= 533 | 534 | ### 62.描述一下 LSD 产品的业务流程 535 | 536 | 537 | ### 63.简述 Eigenlayer 的架构 538 | 539 | 540 | ### 64.EigenLayer BLS 验签流程描述 541 | 542 | 543 | ### 65.EigenLayer 排队取款的业务流程 544 | 545 | 546 | ### 66.如何设计 Price Feed 项目 547 | 548 | 549 | ### 67.根据你已有的知识来设计一个桥的产品,任意链跨转到任意链,支持的链和资产由项目方决定 550 | 551 | 552 | ### 68.OpStack 的桥的业务流程 553 | 554 | 555 | ### 67.假设我做 L2, 手续不收 ETH,收我项目方自己发行 token(比方 Metis 收的就是 Metis, Mantle 收的 MNT, x-layer 收的是 OKB),Gas Oracle 应当怎么做? 556 | 557 | 558 | ### 68.如何处理合约的approve授权抢跑问题。 559 | 560 | 561 | ### 69.如何判断一个合约是安全的erc20合约?----这个有点奇怪。不懂为啥这样问? 562 | 563 | 564 | ### 70.如何让自己的合约,少受到三明治攻击? 565 | 566 | 567 | ### 71.合约uint 和uint256区别,topic怎么计算出来的 568 | 569 | 570 | ### 72.按你的理解设计一个uniswap,怎么避免被夹 571 | -------------------------------------------------------------------------------- /08-rust/readme.md: -------------------------------------------------------------------------------- 1 | # rust 2 | 3 | 4 | ## 1.详细说明 async-std,tokio,actix,native-tls,rustls 等的区别与联系 5 | 6 | - async-std 和 tokio 7 | async-std 和 tokio 都是 Rust 中用于异步编程的库。它们的主要区别在于,async-std 更注重提供类似于 Rust 标准库的 API 和使用习惯,而 tokio 则提供了更为灵活的 API 和自定义能力。在实际使用中,如果需要快速上手,可以选择 async-std;如果需要更高的灵活性和自定义能力,可以选择 tokio。 8 | 9 | - tokio 和 actix 10 | tokio 是一个异步编程框架,提供了用于异步 I/O 操作的基础设施,例如事件循环和异步任务的执行器。而 actix 是一个基于 tokio 的异步 Web 框架,提供了 Web 开发的相关功能和工具,例如 HTTP 服务器和路由等。 11 | 12 | - native-tls 和 rustls 13 | native-tls 和 rustls 都是 Rust 中用于 TLS 加密和解密的库。它们的主要区别在于,native-tls 使用操作系统的本地 TLS 库,例如 OpenSSL,而 rustls 是一个纯 Rust 实现的 TLS 库。在实际使用中,如果需要更高的性能和对操作系统 TLS 库的依赖度较低,可以选择 rustls;如果需要更广泛的兼容性和对操作系统 TLS 库的支持,可以选择 native-tls。 14 | 15 | 总的来说,async-std、tokio、actix、native-tls、rustls 这些库和框架都是 Rust 中非常常用的工具,用于异步编程和网络编程。它们之间的区别和联系需要根据具体的使用场景和需求来选择。 16 | 17 | 18 | ## 第一课:Rust语言基础知识部分 19 | 建议阅读的文档: 20 | [九层之台,起于累土](https://mp.weixin.qq.com/s/irowOHVMUpY_u1kxJ5gZ6Q) 21 | 22 | ### 1. 简单说明可变变量以及不可变变量 23 | 24 | *解答:* 25 | 在Rust中,变量默认是不可变,如果想要一个变量可变必须要在声明变量的时候显式地加上`mut`标志。这也就是在Rust中声明变量被称之为`绑定`变量的原因。其主要目的是为了考虑安全,变化是安全的重要源头之一。 26 | ```Rust 27 | let a = 100; 28 | // a += 1; //放开这行代码,无法通过编译,因为变量`a`默认不可变。 29 | 30 | let mut b = 100; // 用`mut`对变量`a` 作显式标浅 31 | b = b + 1; // ok 32 | ``` 33 | 34 | ### 2. 什么叫变量遮蔽(shadow)?请举例说明。 35 | 解答: 36 | 所谓的变量遮蔽(shadow),是指在同一上下文中,可以对已经声明或定义的变量进行重复的声明或定义,新的变量声明或定义后,原变量失效。新的变量与原变量的关系是,新变量仅仅在名称上与原变量相同,这就意味着新变量的类型可以不同于原变量。 37 | ```Rust 38 | let s = String:from("abc"); 39 | println!("{s}"); 40 | 41 | let s = 100; //此处,对原变量s进行了遮蔽; 42 | s += 20; 43 | ... 44 | ``` 45 | 46 | ### 3. 什么是常量?它在定义时有什么要要求?常量与变量的区别? 47 | 解答: 48 | 1. 常量,是指程序整个的生命周期内都是不可变的量,用来表示全局范围内可被多处使用的不变量,比如一个星期有7天这种永恒的量。常量通常是在编译时就被包含在程序二进制文件中,程序加载内在后也会被单独存放在类型常量或者是静态的内存区域,该内存区域是只读的。 49 | 2. 常量在定义时,不适应类型的自动推断,必须显式地标注其类型。 50 | ```Rust 51 | //const DAYS_IN_WEEK = 7; //Error 52 | const DAYS_IN_WEEK: u8 = 7; // ok 53 | ``` 54 | 3. 常量与变量的区别: 变量,通常是在函数内部的局部空间进行定义的,然后在运行的时候,实时地分配在栈空间里面。可见,变量的默认不可变性设计,主要是出于对安全的考究,其与常量的目的与内存管理机制都有着本质区别。 55 | 56 | ### 4. Rust中的基本(标量)数据类型都有哪些? 57 | 解答: 58 | Rust中的基本数据类型,主要有`整数`、`浮点数`、`字符`、`布尔`、`元组`、`数组`等。 59 | #### 1. 整型 60 | 61 | 分为有符号整数与无符号整数,有符号数用`u`开头,无符号数用`i`开头,同时有长度信息(8、16、32、64、128...),比如`i32`表示有符号且长度为32位的整数,`u32`表示无符号且长度为32的整数。 62 | 63 | * 在绑定变量的时候,如果不显式的指定整数的类型,编译器会自动推断为`i32`类型。 64 | * 还有一种特殊的整型:`usize` 与 `isize`,其长度与目标机器的字长长度一致。如果是32机器,其长度是32位,如果是64位机器,其长度是64位。 65 | * 不同长度的类型在进行转换时,要考虑到安全性问题。一般来说,把一个长度大的数转换成长度小的类型是不安全的,因为发生一部分二进制位被丢失的情况(即所谓的窄化),相反情况是安全的。 66 | 67 | #### 2. 浮点数 68 | 分为32位与64位,分别为`f32`与`f64`。如果未显式指定类型,编译器会自动推断为`f64`类型。 69 | 70 | #### 3.字符 71 | 字符的类型用`char`声明,当右值为字面量的时候,用单引号包裹,如果是双引号会被认为是字符串。 72 | * 字符内部的数值编码是`UniCode`,它所表达的范围特别广,不但可以表达键盘上可打印的字符,还包括各种表情符号、中文、韩文等等。 73 | * 一个字符占用4个字节 74 | 75 | #### 4.布尔 76 | 布尔类型,用`bool`表示,用于描述逻辑上的真假值,有`true`和`false`两种值。 77 | * 占用一个字节 78 | * bool类型是一个独立的类型,与其他类型之间的转换关系 79 | 80 | #### 5. 元组类型 81 | 元组是将多个类型的多个值组合到一个复合类型中的一种基本方式。元组的长度是固定的,声明后无法增长或缩小。 82 | ```Rust 83 | let tup: (i32, f64, u8) = (500,6.4,1); // 定义 84 | println!("1st number is {}", tup.0); // 访问其中的分量 85 | let (x, y, z) = tup; 86 | println!("The value of y is: {}", y); 87 | ``` 88 | 89 | 没有任何值的元组是一种特殊的类型,写作:`()`,该类型称作为`单元类型`,如果一个表达式没有返回值或者函数没有没有返回值,都被隐式地返回单元类型。 90 | 91 | #### 6. 数组 92 | 将多个类型相同的元素依次组合在一起,就是一个数组。 93 | 关于数组需要注意的点: 94 | * 长度固定,一经定义就不可以改变 95 | * 元素必须有相同的类型 96 | * 元素之间,在物理上和逻辑上都是依次线性排列的 97 | * 数组是存储在栈上 98 | * 数组的长度也是类型一可或缺的部分,`[u8;3]` 和 `[u8;4]` 是不同的类型。 99 | 100 | ```Rust 101 | let arr: [i32;5] = [1, 2, 3, 4, 5]; 102 | println!("the 3th element of arr is : {}", arr[2]); 103 | 104 | let mut arr = [0, 0, 0, 0]; 105 | arr[0] = 100; 106 | println!("{:?}", arr); 107 | ``` 108 | 109 | ### 5. 编写一个函数,对一个i32类型的数组进行冒泡排序 110 | 要求:根据传入的布尔参数决定是升序还是降序。 111 | 112 | 解答: 113 | ```Rust 114 | fn bubble_sort(arr: &mut [i32], ascending: bool) { 115 | let len = arr.len(); 116 | for i in 0..len - 1 { 117 | for j in 0..len - i - 1 { 118 | if (ascending && arr[j] > arr[j + 1]) || (!ascending && arr[j] < arr[j + 1]) { 119 | /* 120 | let tmp = arr[j]; 121 | arr[j] = arr[j + 1]; 122 | arr[j + 1] = tmp; 123 | */ 124 | arr.swap(j, j + 1); 125 | } 126 | } 127 | } 128 | } 129 | 130 | #[test] 131 | fn test_bubble_sort() { 132 | let mut arr1 = [5, 3, 8, 6, 2]; 133 | bubble_sort(&mut arr1, true); 134 | println!("升序排序: {:?}", arr1); 135 | 136 | let mut arr2 = [5, 3, 8, 6, 2]; 137 | bubble_sort(&mut arr2, false); 138 | println!("降序排序: {:?}", arr2); 139 | } 140 | ``` 141 | 142 | ## 第二课: 所有权与借用 143 | ### 6. 掌握所有权与借用的内涵,建议阅读以下文章: 144 | [欲知山中事,须问打柴人](https://mp.weixin.qq.com/s/2aM6TQprVoZAjY0PY75g4A) 145 | 146 | 147 | [不以规矩,不能成方圆](https://mp.weixin.qq.com/s/kUDc9xr_a-51RCmg_pg-FA) 148 | 149 | ### 7. 分析下面三段代码,回答能不能通过编译?并分别说明原因。 150 | #### 1. 代码一 151 | ```Rust 152 | fn main() { 153 | let i1 = 100; 154 | let i2 = i1; 155 | println!("{}",i1); 156 | } 157 | ``` 158 | #### 2. 代码二 159 | ```Rust 160 | fn main() { 161 | let s1 = String:from("abc"); 162 | let s2 = s1; 163 | println!("{}",s1); 164 | } 165 | ``` 166 | 167 | #### 3. 代码三 168 | ```Rust 169 | fn f(s: String) {} 170 | fn main() { 171 | let s1 = String:from("abc"); 172 | f(s1); 173 | println!("{}",s1); 174 | } 175 | ``` 176 | 177 | ### 8. 下面代码会不会通过编译?并分别说明原因。 178 | #### 1. 代码一 179 | ```Rust 180 | fn main() { 181 | let ref_a: &i32; 182 | let b = *a; 183 | println!("{}", b); 184 | } 185 | ``` 186 | 187 | #### 2. 代码二 188 | ```Rust 189 | fn dangle() -> &i32 { 190 | let a = 100; 191 | &a 192 | } 193 | 194 | fn main() { 195 | let ref_some = dangle(); 196 | println!("{}", *ref_some); 197 | } 198 | ``` 199 | 200 | #### 3. 代码三 201 | ``` Rust 202 | fn main() { 203 | let mut a = 100; 204 | let ref_a1 = &a; 205 | println!("{}", *ref_a1); 206 | let ref_a2 = &a; 207 | println!("{}", *ref_a2); 208 | let ref_a3 = &a; 209 | println!("{}", *ref_a3); 210 | 211 | let ref_mut_a1 = &mut a; 212 | println!("{}", *ref_mut_a1); 213 | 214 | println!("{}", *ref_a2); //如果把这行代码注释了,又是什么情况? 215 | } 216 | ``` 217 | 218 | ## 第三课:结构体、枚举及常用集合类型 219 | ### 9. 结构体和枚举 220 | #### 9.1 有一个名为 Person 的结构体,包含以下字段: 221 | 222 | * name(字符串类型) 223 | * age(整数类型) 224 | * email(字符串类型) 225 | 226 | #### 要求: 227 | * 定义这个结构体 228 | * 为该结构体实现关联函数`new`,用以构建实例 229 | * 对其字段分别实现`get` 和 `set` 关联方法。 230 | 231 | 解答: 232 | ```Rust 233 | // 1. 定义结构体 Person 234 | #[derive(Debug, Clone)] 235 | struct Person { 236 | name: String, 237 | age: u8, 238 | email: String, 239 | } 240 | // 实现相关的方法和函数 241 | impl Person { 242 | fn new(name: String, age: u8, email: String) -> Self { 243 | Self{name, age, email} 244 | } 245 | 246 | fn set_name(&mut self,name: String) {self.name = name;} 247 | fn get_name(&self) -> &str {self.name.as_str()} 248 | 249 | fn set_age(&mut self, age: u8) {self.age = age;} 250 | fn get_age(&self) -> u8 {self.age} 251 | 252 | fn set_email(&mut self, email: String) {self.email = email} 253 | fn get_email(&self) -> &str {self.email.as_str()} 254 | } 255 | 256 | 257 | ``` 258 | 259 | #### 9.2 有一个名为 Status 的枚举,表示一个任务的状态,包含以下变体: 260 | 261 | * NotStarted 262 | * InProgress(包含一个表示进度的浮点数) 263 | * Completed 264 | 265 | 266 | #### 要求: 267 | * 定义该枚举类型 268 | * 写一个关联方法`send_message`,返回类型为`String`,使用 match 根据三种不同的变体打印相关的信息。对于InProgress类型,打印的信息中需要包含进度信息。 269 | 270 | 解答: 271 | ```Rust 272 | // 定义枚举 Status 273 | enum Status { 274 | NotStarted, 275 | InProgress(f64), 276 | Completed, 277 | } 278 | 279 | impl Status { 280 | fn send_message(status: Status) ->String { 281 | match status { 282 | Status::NotStarted => "还没有开始".to_string(), 283 | Status::InProgress(progress) => format!("正在进行中,当前进度为:{}", progress), 284 | Status::Completed => "已经完成了".to_string(), 285 | } 286 | } 287 | } 288 | ``` 289 | ### 10. Option和match 290 | #### 10.1 定义一个函数 find_person_by_email,接受一个 Vec 和一个 String 作为参数,返回一个 Option,表示在列表中找到的第一个匹配的Person。如果没有找到,返回 None。 291 | 解答: 292 | ```Rust 293 | fn find_person_by_email(people: Vec, email: String) -> Option { 294 | for person in people { 295 | if person.email == email { 296 | return Some(person); 297 | } 298 | } 299 | None 300 | } 301 | ``` 302 | 303 | #### 10.2 定义一个函数print_person_info,使用 match 表达式处理 Option,如果找到,打印出 Person 的信息,如果没有找到,打印出一条消息表示没有找到。 304 | 解答: 305 | 306 | ```Rust 307 | fn print_person_info(person_option: Option) { 308 | match person_option { 309 | Some(person) => println!("Found person: {:?}", person), 310 | None => println!("Person not found"), 311 | } 312 | } 313 | ``` 314 | ### 11. String、Vec和HashMap 315 | #### 11.1 定义一个函数 group_people_by_age,接受一个 Vec 作为参数,返回一个 HashMap>,将 Person 按照年龄进行分组。 316 | 解答: 317 | ```Rust 318 | // 定义函数 group_people_by_age 319 | use std::collections::HashMap; 320 | fn group_people_by_age(people: Vec) -> HashMap> { 321 | let mut age_groups: HashMap> = HashMap::new(); 322 | for person in people { 323 | age_groups.entry(person.age).or_insert(Vec::new()).push(person); 324 | } 325 | age_groups 326 | } 327 | ``` 328 | #### 11.2 使用 String 类型完成以下任务: 329 | 330 | 定义一个函数 to_uppercase_names,接受一个 Vec 作为参数,返回一个新的 Vec,其中包含所有 Person 的大写名字。 331 | 解答: 332 | ```Rust 333 | // 定义函数 to_uppercase_names 334 | fn to_uppercase_names(people: Vec) -> Vec { 335 | let mut uppercase_names = Vec::new(); 336 | for person in people { 337 | uppercase_names.push(person.name.to_uppercase()); 338 | } 339 | uppercase_names 340 | } 341 | ``` 342 | 343 | ## 第四课 泛型与特征 344 | ### 12. 实现一个简单的通知系统,能够发送不同类型的通知消息(如邮件通知、短信通知、推送通知),并且能够存储和处理这些不同类型的通知。要求如下: 345 | 346 | * 定义一个 Notification 特征,包含一个 send 方法,用于发送通知。 347 | * 实现三个结构体 EmailNotification、SmsNotification 和 PushNotification,它们分别代表邮件通知、短信通知和推送通知,并为这些结构体实现 Notification 特征。 348 | * 创建一个 NotificationSender 结构体,该结构体可以存储多个不同类型的通知,并能够调用它们的 send 方法来发送所有的通知。 349 | * 使用泛型和特征对象来实现 NotificationSender 的通知存储。 350 | 351 | 352 | 参考答案: 353 | ```Rust 354 | // 定义 Notification 特征 355 | trait Notification { 356 | fn send(&self); 357 | } 358 | 359 | // 实现 EmailNotification 结构体 360 | struct EmailNotification { 361 | recipient: String, 362 | subject: String, 363 | body: String, 364 | } 365 | 366 | impl Notification for EmailNotification { 367 | fn send(&self) { 368 | println!("Sending email to {}: {}\n{}", self.recipient, self.subject, self.body); 369 | } 370 | } 371 | 372 | // 实现 SmsNotification 结构体 373 | struct SmsNotification { 374 | phone_number: String, 375 | message: String, 376 | } 377 | 378 | impl Notification for SmsNotification { 379 | fn send(&self) { 380 | println!("Sending SMS to {}: {}", self.phone_number, self.message); 381 | } 382 | } 383 | 384 | // 实现 PushNotification 结构体 385 | struct PushNotification { 386 | device_id: String, 387 | message: String, 388 | } 389 | 390 | impl Notification for PushNotification { 391 | fn send(&self) { 392 | println!("Sending push notification to {}: {}", self.device_id, self.message); 393 | } 394 | } 395 | 396 | // 定义 NotificationSender 结构体,使用泛型 397 | struct NotificationSender { 398 | notifications: Vec, 399 | } 400 | 401 | impl NotificationSender { 402 | fn new() -> Self { 403 | NotificationSender { 404 | notifications: Vec::new(), 405 | } 406 | } 407 | 408 | fn add_notification(&mut self, notification: T) { 409 | self.notifications.push(notification); 410 | } 411 | 412 | fn send_all(&self) { 413 | for notification in &self.notifications { 414 | notification.send(); 415 | } 416 | } 417 | } 418 | 419 | // 使用特征对象的版本 420 | struct DynNotificationSender { 421 | notifications: Vec>, 422 | } 423 | 424 | impl DynNotificationSender { 425 | fn new() -> Self { 426 | DynNotificationSender { 427 | notifications: Vec::new(), 428 | } 429 | } 430 | 431 | fn add_notification(&mut self, notification: Box) { 432 | self.notifications.push(notification); 433 | } 434 | 435 | fn send_all(&self) { 436 | for notification in &self.notifications { 437 | notification.send(); 438 | } 439 | } 440 | } 441 | 442 | fn main() { 443 | // 使用泛型的 NotificationSender 444 | let mut email_sender = NotificationSender::new(); 445 | email_sender.add_notification(EmailNotification { 446 | recipient: String::from("user@example.com"), 447 | subject: String::from("Welcome!"), 448 | body: String::from("Thank you for signing up!"), 449 | }); 450 | 451 | let mut sms_sender = NotificationSender::new(); 452 | sms_sender.add_notification(SmsNotification { 453 | phone_number: String::from("123-456-7890"), 454 | message: String::from("Your code is 1234."), 455 | }); 456 | 457 | email_sender.send_all(); 458 | sms_sender.send_all(); 459 | 460 | // 使用特征对象的 DynNotificationSender 461 | let mut dyn_sender = DynNotificationSender::new(); 462 | dyn_sender.add_notification(Box::new(EmailNotification { 463 | recipient: String::from("user@example.com"), 464 | subject: String::from("Welcome!"), 465 | body: String::from("Thank you for signing up!"), 466 | })); 467 | dyn_sender.add_notification(Box::new(SmsNotification { 468 | phone_number: String::from("123-456-7890"), 469 | message: String::from("Your code is 1234."), 470 | })); 471 | dyn_sender.add_notification(Box::new(PushNotification { 472 | device_id: String::from("device123"), 473 | message: String::from("You have a new message."), 474 | })); 475 | 476 | dyn_sender.send_all(); 477 | } 478 | 479 | ``` 480 | 481 | ## 第五课 闭包与生命周期 482 | ### 13. 编写一个函数 apply_twice,它接受一个闭包和一个整数,并将该闭包应用两次。闭包接受并返回一个整数。你需要正确地定义 apply_twice 函数的类型签名,并实现该函数。 483 | ```Rust 484 | fn apply_twice(f: F, x: i32) -> i32 485 | where 486 | F: Fn(i32) -> i32, 487 | { 488 | // 在此实现 489 | } 490 | 491 | // 使用示例 492 | fn main() { 493 | let add_two = |x| x + 2; 494 | let result = apply_twice(add_two, 5); 495 | println!("{}", result); // 应输出 9 496 | } 497 | 498 | ``` 499 | 500 | ### 14. 编写一个函数 generate_adder,它接受一个整数参数 x,并返回一个闭包。该闭包接受一个整数参数 y,并返回 x + y。注意,闭包必须捕获外部变量 x。 501 | ```Rust 502 | fn generate_adder(x: i32) -> impl Fn(i32) -> i32 { 503 | // 在此实现 504 | } 505 | 506 | // 使用示例 507 | fn main() { 508 | let adder = generate_adder(5); 509 | println!("{}", adder(3)); // 应输出 8 510 | } 511 | 512 | ``` 513 | 514 | ### 15. 编写一个函数 longest_with_an_announcement,它接受两个字符串切片和一个闭包作为参数。该闭包接受一个字符串切片,并返回一个字符串切片。函数返回两个字符串切片中较长的一个,并调用闭包来打印一个公告。 515 | ```Rust 516 | fn longest_with_an_announcement<'a, F>(x: &'a str, y: &'a str, ann: F) -> &'a str 517 | where 518 | F: Fn(&str) -> &str, 519 | { 520 | // 在此实现 521 | } 522 | 523 | // 使用示例 524 | fn main() { 525 | let x = "Hello, world!"; 526 | let y = "Hi, Rust!"; 527 | let announcement = |s: &str| { 528 | println!("Announcement: {}", s); 529 | s 530 | }; 531 | let result = longest_with_an_announcement(x, y, announcement); 532 | println!("The longest string is {}", result); // 应输出 "Hello, world!" 533 | } 534 | 535 | ``` 536 | 537 | ## 第六课 项目管理与错误处理 538 | ### 15. 实现一个简单的计算器库 539 | #### 问题:编写一个名为 calculator_workspace 的工作区项目,其中包含两个库包: 540 | 541 | 1. calculator_core:核心计算逻辑。 542 | 2. calculator_io:用于处理输入输出及错误管理。 543 | 544 | calculator_core 包含基本的加减乘除功能,并在计算过程中可能返回自定义错误。 545 | 546 | calculator_io 包含读取用户输入和输出结果的功能,并将 calculator_core 中的错误转换为用户友好的错误信息。 547 | 548 | #### 项目结构 549 | ```css 550 | calculator_workspace/ 551 | ├── Cargo.toml 552 | ├── calculator_core/ 553 | │ ├── Cargo.toml 554 | │ └── src/ 555 | │ └── lib.rs 556 | ├── calculator_io/ 557 | │ ├── Cargo.toml 558 | │ └── src/ 559 | │ └── lib.rs 560 | └── tests/ 561 | └── integration_test.rs 562 | 563 | ``` 564 | -------------------------------------------------------------------------------- /09-layer2/L2 readme.md: -------------------------------------------------------------------------------- 1 | 2 | # rollup整体流程 3 | op-batcher,以及op-proposer两种状态 4 | ``` 5 | 6 | ``` 7 | 8 | # op-node 9 | 10 | # 官方桥 11 | 12 | 13 | -------------------------------------------------------------------------------- /09-layer2/eigen readme: -------------------------------------------------------------------------------- 1 | # 面试题以及答案 2 | 3 | # eigenda disperser数据存储流程 4 | 5 | 6 | # eigenda retriver数据恢复流程 7 | 8 | # da-node 启动注册流程 9 | 10 | # middleware avs 11 | 12 | 13 | # eigenlayer 整体流程 14 | 15 | 16 | -------------------------------------------------------------------------------- /09-layer2/readme.md: -------------------------------------------------------------------------------- 1 | # 一.EigenLayer 面试题 2 | 3 | ## 1.详细描述再质押的流程 4 | 5 | 6 | ## 2.BLS checkSignature 流程, 只针对 EigenLayer 这个项目 7 | 8 | 9 | ## 3.排队取款和完成排队取款的业务流程 10 | 11 | 12 | ## 4.验证组的最大人数限制是多少 13 | 14 | # 二.layer2 面试题 15 | 16 | ## 1. Zk rollup 和 Op rollup 的主要区别是什么 17 | 18 | ## 2. 说一下 Zk rollup 和 Op rollup 和代表项目,并简述项目之间的区别 19 | 20 | ## 3. 简述 L1<->L2 桥充值提现流程 21 | 22 | ## 4. op-batch 和 op-proposer 的作用是什么 23 | 24 | ## 5. 简述 op-node 指导 op-geth 修改区块状态的逻辑 25 | 26 | ## 6. 聊聊您对重新质押协议的理解 27 | 28 | ## 7. 你熟悉那些 DA,举一到两个来说明 29 | 30 | ## 8. 简述 EigenDA 的整体架构 31 | 32 | ## 9. DA 里面选择 KZG 算法的主要原因是什么 33 | 34 | ## 10. EigenDA 的重新质押的意义是什么 35 | 36 | ## 11. EigenDA 和 Celestia DA 的区别是什么 37 | 38 | ## 12. 可以给我讲述一下 op stack 的整体架构吗,可以画图说明 39 | 40 | ## 13. 简述 op bridge 充值和提现交易如何进行关联 41 | 42 | ## 14. op-batcher, rollup 数据 start 的区块是如何获取和使用的 43 | 44 | ## 15. op-proposer rollup 块数的 stateroot 是固定的吗,请说明原因 45 | 46 | ## 16. 简述 op-node 指导 op-geth 改变区块状态的流程 47 | 48 | ## 17. Op 充值的资金锁在那个合约里面 49 | 50 | ## 18. Prove 和 cliam 交易的通过那个合约实现的,方法名是什么 51 | 52 | ## 19. Op 的挑战期是固定的吗,若不是,请说明如何更改调整期 53 | 54 | ## 20. 如何解析到 rollup 到 L1 的交易数据供给浏览器使用 55 | 56 | ## 21. 如何解析到 rollup 到 L1 的 stateroot 数据供给浏览器使用 57 | 58 | ## 22. 如果 OpStack 改造到 DA 应该怎么去改造 59 | 60 | ## 23. OpStack Rollup 有哪些场景被阻赛,怎么去解决这个问题 61 | - transaction under priced 62 | - nonce 重试次数 63 | 64 | ## 24. 什么情况下会阻赛二层的出块 65 | 66 | - 区块推到卡住了 67 | - RPC 节点挂了怎么,使用多方的 RPC 节点 68 | - DA 挂了 69 | 70 | ## 25. op-node 和 op-geth 重启之后会发生什么 71 | 72 | 73 | ## 26.op-stack 欺诈证明的流程 74 | 75 | 76 | ## 27.Mips 虚拟机做了哪些事儿 77 | 78 | 79 | ## 28.如果有 ERC20 在二层和一层发行了,那 op-stack 里面是怎么操作 80 | 81 | 82 | # 三.EigenDA 83 | 84 | ## 1.为什么要 DA 85 | 86 | 87 | ## 2.EigenDA 的代码架构 88 | 89 | 90 | ## 3.EigenDA 和 Celestia 的数据可用性证明有什么区别 91 | 92 | 93 | ## 4.Celestia 命明空间默克尔树验证 94 | 95 | 96 | ## 5.什么是是稀疏默克尔树 97 | 98 | 99 | ## 6.什么是数据炸弹 100 | 101 | 102 | ## 7.什么是MPT树,MPT 的数据的查找,更新,删除的过程 103 | 104 | 105 | # 三.零知识证明算法 106 | 107 | ## 1.什么是零知识证明, 零知识证明的过程 108 | 109 | 110 | ## 2.zk-snark 和 zk-stark 的区别与联系,工作原理 111 | 112 | 113 | ## 3.为什么 Schnorr 更安全还隐私性更高 114 | 115 | 116 | ## 4.常见的 SNARK 零知识算法特点分析 117 | 118 | 119 | ## 5.KZG SRS 作用是什么 120 | 121 | 122 | ## 6.KZG 算法复杂度 123 | 124 | 125 | ## 7.KZG G1 和 G2 代表是什么 126 | 127 | 128 | ## 8.什么 zk 前端,什么是 Zk 后端 129 | 130 | 131 | ## 9.各个链使用的 zk 算法是什么,为什么使用这个算法 132 | 133 | 134 | ## 10.常见的密钥协商算法,密钥交换过程 135 | 136 | 137 | ## 19.PKI 公钥体系概述 138 | 139 | 140 | # 四.Polygon ZkEvm 面试题目 141 | 142 | ## 1. Polygon 整个项目生态 143 | 144 | ## 2. Polygon ZkEvm 里面的各个组件的功能 145 | 146 | ## 3. Polygon ZkEvm Rollup 的流程 147 | 148 | ## 4. Polygon ZkEvm 区块的状态和推导过程 149 | 150 | ## 5. Polygon ZkEvm 一笔交易的生命周期 151 | 152 | ## 6. Polygon ZkEvm 跨链桥简析 153 | 154 | ## 7. 您使用过 Polygon CDK 吗,请简述你是怎么使用的 155 | 156 | ## 8. Polygon ZkEvm 交易执行流程和证明流程介绍 157 | -------------------------------------------------------------------------------- /10-zkp/readme.md: -------------------------------------------------------------------------------- 1 | # 零知识证明 2 | -------------------------------------------------------------------------------- /11-cosmos/readme.md: -------------------------------------------------------------------------------- 1 | # cosmos 2 | -------------------------------------------------------------------------------- /12-wallet/README.md: -------------------------------------------------------------------------------- 1 | # 区块链钱包面试题目 2 | 3 | ## 1.描述一下交易钱包的充值和提现流程 4 | 5 | ### 充值 6 | 7 | - 交易所给用户提供地址,用户把钱转进来 8 | - 扫链:获取最新块高,从上一次解析的块开始到最新块高进行交易解析 9 | - 交易解析完之后,如果交易里面 to 是系统用户,则是充值,解析完成之后上账,并通知业务层 10 | 11 | ### 提现 12 | 13 | - 获取需要的签名参数 14 | - 交易离线签名:组织交易,生成待签名消息摘要,将待签名的消息摘要递给签名机进行签名,签名机签名完成之后返回签名串 15 | - 构建完整的交易并发送区块链网络,将签名时计算出来的交易 Hash 或者发送交易时返回交易 Hash 更新到数据库 16 | - 扫链解析到这笔交易,说明提现成功。 17 | 18 | ## 2.HD 钱包助记词生成的流程 19 | 20 | - 第一步:随机熵生成 21 | - 第二步:计算校验和 22 | - 第三步:组合熵和校验和 23 | - 第四步:分割助记词索引 24 | - 第五步:映射为助记词 25 | 26 | ## 3.助记词的验证过程 27 | 28 | - 第一步:检查助记词数量 29 | - 第二步:检查助记词是否在词汇表 30 | - 第三步:将助记词转换成索引 31 | - 第四步:提取种子的校验和 32 | - 第五步:计算校验和 33 | - 第六步:验证校验和 34 | 35 | ## 4.BIP44 路径 36 | 37 | ``` 38 | m/44'/coin_type'/account'/change/address_index 39 | ``` 40 | 41 | ## 5.门限共享秘密拆分过程 42 | 43 | - 关键点:门限共享秘密拆分是将一个秘密分成多份,只有当足够多的份(达到门限)组合起来才能恢复原秘密,常用 Shamir 的秘密共享方案。 44 | #### 门限共享秘密拆分的定义 45 | 46 | - 秘密-----> n 份,k 份可恢复, 任意 k 份恢复出这个秘密,分别用门限共享算法和逆门限共享秘密算法 47 | 48 | 门限共享秘密拆分是指将一个秘密 S 拆分成 n 个份额(shares),使得只有当至少 k 个份额(k 为门限)组合起来时,才能准确恢复原秘密 S,而少于 k 个份额无法获得任何关于 S 的信息。这被称为 (k, n) 门限方案。例如,在一个 (2,3) 方案中,秘密被分成 3 份,任何 2 份能恢复秘密,但 1 份无法。 49 | 50 | 最常见的方法是 Shamir 的秘密共享方案,基于多项式插值和有限域的数学性质。 51 | 52 | #### Shamir 的秘密共享方案利用 k-1 次多项式来实现门限共享,具体步骤如下: 53 | 1. 选择一个大于秘密的质数 p。 54 | 2. 将秘密 S 设为多项式的常数项,随机生成 k-1 个系数,构成一个 k-1 次多项式 f(x)。 55 | 3. 为每个参与者分配一个唯一标识 x_i(通常从 1 到 n),计算他们的份额 y_i = f(x_i) mod p。 56 | 4. 每个参与者得到一个份额 (x_i, y_i)。 57 | 58 | #### 恢复过程 59 | - 任何 k 个参与者可以用他们的份额通过拉格朗日插值法恢复多项式,计算 f(0) 得到原秘密 S。 60 | #### 以下是门限共享秘密拆分的应用场景对比: 61 | | 场景 | 适用性 | 优势 | 62 | |:-------:|:----------:|:-------------:| 63 | | 分布式密钥管理 | 高,适合多方合作 | 防止单点故障,增强安全性 | 64 | | 数据备份 | 高,适合重要数据分发 | 防止数据泄露,需多方恢复 | 65 | | 访问控制 | 高,适合权限分配 | 灵活控制访问,满足门限要求 | 66 | 67 | ## 5.MPC 算法 Keygen 和 Sign 分别需要经过多少轮共识 68 | 69 | GG18 70 | 71 | - Keygen: 5 轮 72 | - Sign: 9 轮 73 | 74 | GG20 75 | 76 | - Keygen: 5 轮 77 | - Sign: 7 轮 78 | 79 | ## 6.为什么 schnorr 比特币手续费可以降低 80 | 81 | schnorr 签名聚合成一个签名,还有短密钥,这样签名的数据会比 ECDSA 短很多,所以相对于 ECDSA 签名的交易会便宜很多 82 | 83 | ## 7. 为什么比特币早期时候不直接用 schnorr 签名算法 84 | 85 | 因为当时 schnorr 算法在专利期 86 | 87 | ## 8. 相比较之下 EDDSA 性能,安全性都会高一些,为什么比特币以太坊用了 ECDSA,没有用 EDDSA 88 | 89 | - ECDSA 是基于更早的标准(如 FIPS 186-4 和 ANSI X9.62)发展的,因此在密码学界和工业界有较长的使用历史和广泛的标准化支持。它被大量系统和协议(如 90 | TLS 和 Bitcoin)采用,形成了一个庞大的生态系统。 91 | - 虽然 EdDSA 有一些优势,如不容易受到侧信道攻击的影响(如时间攻击和缓存攻击),但 ECDSA 92 | 的安全性也已经过广泛的研究和验证。对于很多开发者和企业来说,使用一个已被长期验证的算法是更为保守和安全的选择。 93 | - EdDSA 通常具有更高的签名速度和较快的验证速度,尤其是在大多数软件实现中。然而,对于已经高度优化的 ECDSA 94 | 实现,性能差异在许多应用中可能并不明显。 95 | - EdDSA 的设计更为简单且更不易出错,特别是在处理随机数生成等方面。然而,ECDSA 由于使用历史更长,开发者更为熟悉其使用和管理。 96 | 97 | ## 9.中心化钱包开发里面的充值,提现,归集,转冷,冷转热开发业务流程描述 98 | 99 | - 💡💡接口返回的 to 是交易所系统里面的用户地址,这笔交易为充值交易; 100 | - 💡💡接口返回的 from 是交易所系统里面的用户地址,这笔交易为提现交易; 101 | - 💡💡接口返回的 to 是交易所的归集地址,from 是系统的用户地址,这笔交易资金归集交易; 102 | - 💡💡接口返回的 to 地址是冷钱包地址,from 地址时热钱包地址,这笔交易热转冷的交易。 103 | - 💡💡接口返回的 from 地址是冷钱包地址,to 地址时热钱包地址,这笔交易冷转热的交易。 104 | 105 | ### 充值 106 | 107 | - 获得最新块高;更新到数据库 108 | - 从数据库中获取上次解析交易的块高做为起始块高,最新块高为截止块高,如果数据库中没有记录,说明需要从头开始扫,起始块高为 0; 109 | - 解析区块里面的交易,to 地址是系统内部的用户地址,说明用户充值,更新交易到数据库中,将交易的状态设置为待确认。 110 | - 所在块的交易过了确认位,将交易状态更新位充值成功并通知业务层。 111 | - 解析到的充值交易需要在钱包的数据库里面维护 nonce, 当然也可以不维护,签名的时候去链上获取 112 | 113 | ### 提现 114 | 115 | - 获取离线签名需要的参数,给合适的手续费 116 | - 构建未签名的交易消息摘要,将消息摘要递给签名机签名 117 | - 构建完整的交易并进行序列化 118 | - 发送交易到区块链网络 119 | - 扫链获取到交易之后更新交易状态并上报业务层 120 | 121 | ### 归集 122 | 123 | - 将用户地址上的资金转到归集地址,签名流程类似提现 124 | - 发送交易到区块链网络 125 | - 扫链获取到交易之后更新交易状态 126 | 127 | ### 转冷 128 | 129 | - 将热钱包地址上的资金转到冷钱包地址,签名流程类似提现 130 | - 发送交易到区块链网络 131 | - 扫链获取到交易之后更新交易状态 132 | 133 | ### 冷转热 134 | 135 | - 手动操作转账到热钱包地址 136 | - 扫链获取到交易之后更新交易状态 137 | 138 | ## 10.有用过 rosetta api, 请简单描述起作用,并举几个钱包常用的接口说明 139 | 140 | Bitcoin Rosetta API 是由 Coinbase 提出的 Rosetta 141 | 标准的一部分,旨在为区块链和钱包提供一个统一的接口标准。这个标准化的接口使得与各种区块链的交互更加容易和一致,无论是对交易数据的读取还是写入。目前已经支持很多链,包含比特币,以太坊等主流链,也包含像 142 | IoTex 和 Oasis 这样的非主流链。 143 | 144 | ### 用到的接口 145 | 146 | - /network/list:返回比特币主网和测试网信息。 147 | - /network/status:返回当前最新区块、已同步区块高度、区块链处理器的状态等。 148 | - /block 和 /block/transaction:返回区块和交易的详细信息,包括交易的输入输出、金额、地址等。 149 | - /account/balance:通过查询比特币节点,返回指定地址的余额。 150 | 151 | ### 发送交易到区块链网络 152 | 153 | - /construction/submit:通过比特币节点提交签名后的交易。 154 | 155 | ### 以下是`Rosetta API`交易流程的表格化表示: 156 | 157 | | 步骤 | 端点 | 输入 | 输出 | 备注 | 158 | |:-------:|:-------------------------:|:------------------:|:-------------:|:----------:| 159 | | 检查余额 | /account/balance | 账户地址 | 余额数组 | 确保有足够 ICP | 160 | | 定义操作 | - | - | 操作数组 | 手动构造 | 161 | | 预处理交易 | /construction/preprocess | 网络标识符、操作 | options, 所需公钥 | 验证操作合法性 | 162 | | 获取元数据 | /construction/metadata | 网络标识符、options | metadata | 获取动态数据 | 163 | | 生成未签名交易 | /construction/payloads | 网络标识符、操作、metadata | 未签名交易、签名负载 | 准备签名 | 164 | | 离线签名 | - | 签名负载、私钥 | 签名结果 | 可离线完成 | 165 | | 组合签名交易 | /construction/combine | 网络标识符、未签名交易、签名 | 签名交易 | 合并签名 | 166 | | 提交交易 | /construction/submit | 网络标识符、签名交易 | 交易哈希 | 提交网络 | 167 | | 等待确认 | /transaction | 网络标识符、交易哈希 | 交易详情(含区块信息) | 检查是否被区块包含 | 168 | 169 | ## 11.ETH2.0 的 epoch, slot 和 block 简述 170 | 171 | ### Slot(时隙) 172 | 173 | 定义:Slot 是以太坊2.0中最基本的时间单位,每个slot都有一个指定的时间长度。在每个 slot 中,可能会有一个区块被提议并添加到链中。 174 | 时间长度:一个 slot 的长度为 12 秒。这意味着每 12 秒会有一个新的 slot。 175 | 功能:在每个 slot 中,网络中的验证者将有机会提议一个新的区块。这些提议者是通过权益证明(PoS)随机选择的。 176 | 177 | ### Epoch(纪元) 178 | 179 | 定义:Epoch 是由多个连续的slot组成的更长时间段。在 Eth2.0 中,Epoch 用于管理和组织验证者的活动。 180 | 组成:一个 Epoch 由 32 个 slot 组成。 181 | 时间长度:由于一个 slot 是12秒,一个 Epoch 的总长度是 384 秒(即6.4分钟)。 182 | 功能:Epoch 是用来实现共识机制的一部分。在每个 Epoch 结束时,网络会进行状态和共识的检查和调整,包括对验证者的奖励和惩罚。 183 | 184 | ### Block(区块) 185 | 186 | 定义:Block 是包含交易和其他相关数据的记录单元。在以太坊2.0中,每个slot可能会有一个区块被提议,但不保证每个 slot 都有区块。 187 | 内容:一个区块包含区块头、交易列表、状态根哈希、签名等数据。 188 | 创建过程:在每个 slot 开始时,网络会随机选出一个验证者来提议区块。该验证者将创建一个包含新交易和其他必要信息的区块,并广播到网络中。 189 | 190 | ## 12.中心化钱包开发中为什么需要确认位,您怎么理解这个确认位的 191 | 192 | 在确认位过了之后交易回滚的难度很大,每条链不一样,根据历史和经验来定,以太坊的话可以参照区块状态来做 193 | 194 | ## 13.简单描述以太坊交易类型,并说明这个交易类型的作用 195 | 196 | - legacy: 历史遗留交易类型,签名体为 `gasLimit` 和 `gasPrice` 197 | - EIP1559: `BaseFee` 和 `MaxPriorityFee` 类型 198 | - EIP4844: `blob` 交易类型 199 | - EIP2930: `Access List` 类型 200 | 201 | ## 14.去中心化和中心化钱包开发中的异同点有哪些? 202 | 203 | ### 密钥管理方式不同 204 | 205 | HD 钱包私钥在本地设备,私钥用户自己控制 206 | 交易所钱包中心化服务器(CloadHSM, TEE 等),私钥项目方控制 207 | 208 | ### 资金存在方式不同 209 | 210 | HD 资金在用户钱包地址 211 | 交易所钱包资金在交易所热钱包或者冷钱包里面 212 | 213 | ### 业务逻辑不一致 214 | 215 | 中心化钱包:实时不断扫链更新交易数据和状态 216 | HD 钱包:根据用户的操作通过请求接口实现业务逻辑 217 | 218 | ## 15.发生硬分叉时,做为钱包的开发,您应当怎么去处理这种状况, 以 ETHPOW 和 ETH2.0 分叉这个过程 219 | 220 | 认可共识比较 221 | 222 | ## 16.TON 支持合约吗?若支持,请说出其合约开发语言 223 | 224 | 是的,TON(The Open Network)支持智能合约。TON 的设计目标之一就是提供一个高性能、可扩展的区块链平台,能够支持复杂的去中心化应用(DApps)和智能合约。 225 | 226 | ### TON 的智能合约开发可以使用以下几种语言: 227 | 228 | - FunC 229 | - 这是 TON 官方支持的主要合约开发语言,类似于 C 语言,语法较为底层,上手难度较高。目前 TON 230 | 生态中的主流应用,如钱包和去中心化交易所(DEX),大多使用 FunC 编写。它适合需要深入控制合约逻辑的开发者。 231 | - Tact 232 | - 由社区支持的高级语言,语法类似于 Solidity,易于上手,适合初学者或希望快速开发简单合约的开发者(如 Token 或 233 | NFT)。不过,Tact 目前仍在快速发展中,功能可能不够完善,例如不支持合约升级,Gas 费用也较高。 234 | - Tolk 235 | - TON 官方最新推出的语言,旨在未来逐步替代 FunC。Tolk 相较 FunC 更简洁,且官方计划将所有文档和示例逐步迁移至 236 | Tolk。目前它处于早期阶段,文档和生态支持还在完善中。 237 | - Fift 238 | - 一种底层语言,类似于汇编语言,主要用于 TON 虚拟机(TVM)的低级操作。一般开发者很少直接使用,更多用于特定场景或调试。 239 | 240 | ### 对于大多数开发者来说: 241 | 242 | - 如果是简单项目(如发行代币或 NFT),Tact 是快速上手的选择。 243 | - 如果需要深入开发复杂逻辑或与现有主流合约交互,FunC 是当前最成熟和广泛使用的语言。 244 | - Tolk 则是面向未来的选择,适合关注长期发展的开发者。 245 | 246 | ## 17.比特币的地址有哪些格式,请说明 247 | 248 | 比特币地址的格式主要基于其前缀和编码方式,可以分为以下几类: 249 | 250 | 1. Legacy地址(P2PKH) 251 | - 前缀:以“1”开头。 252 | - 示例:1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2。 253 | - 描述:这是比特币最初的地址格式,基于Pay-to-Public-Key-Hash(P2PKH),要求接收者使用对应的私钥签名以证明所有权。 254 | - 特点:交易费用较高,二维码和手动输入较不方便,适合与不支持SegWit的老式钱包交互。 255 | 2. P2SH地址 256 | - 前缀:以“3”开头。 257 | - 示例:3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy。 258 | - 描述:Pay-to-Script-Hash(P2SH)地址用于更复杂的脚本支付,例如多签地址或嵌套SegWit地址。 259 | - 特点:支持复杂的交易条件,广泛用于交易所和第三方钱包,但兼容性较Legacy地址稍好。 260 | 3. SegWit地址(Bech32) 261 | - 前缀:以“bc1”开头。 262 | - 子类型: 263 | - P2WPKH地址:通常以“bc1q”开头,例如:bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq。 264 | - P2WSH地址:以“bc1”开头且地址较长,例如:bc1qrp33g0q5c5txsp9arysrxwj7a0r24kheu37q2vc9iam624dmybsceqc2f(52字符)。 265 | - 描述:SegWit(隔离见证)是比特币的一个升级,旨在提高交易效率和降低费用。Bech32编码使用32个字符(小写字母和数字),包含错误纠正码,能检测大部分输入错误。 266 | - 特点:P2WPKH适合标准公钥哈希支付,P2WSH适合脚本哈希支付,交易费用较低,抗输入错误能力强。 267 | 4. Taproot地址(Bech32m) 268 | - 前缀:通常以“bc1p”开头,例如:bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297。 269 | - 描述:Taproot是2021年引入的SegWit版本1升级,使用Bech32m编码(Bech32的修改版本),旨在提高隐私和灵活性,支持Schnorr签名和更复杂的智能合约。 270 | - 特点:提供更好的安全性和较低的费用,多签交易在外观上与单签交易无异,增强了隐私保护。目前采用率逐渐提高,但并非所有钱包均支持。 271 | 272 | - 地址格式的演进与对比 273 | - 比特币地址格式的演进反映了网络的扩展需求和安全性提升: 274 | - Legacy地址:最早的格式,适合早期用户,但交易大小较大,费用较高。 275 | - P2SH地址:引入了对复杂脚本的支持,兼容性较好,常用在多签或嵌套SegWit场景。 276 | - SegWit地址:通过隔离见证减少了交易数据量,降低了费用,Bech32编码提高了用户友好性。 277 | - Taproot地址:作为SegWit的进一步优化,结合Bech32m编码,显著提升了隐私和智能合约能力。 278 | 279 | ### 以下是各格式的对比表: 280 | 281 | | 格式类型 | 前缀 | 示例地址 | 特点 | 282 | |:---------------:|:----:|:--------------------------------------------------------------:|:-----------------------:| 283 | | Legacy (P2PKH) | 1 | 1BvBMSEYstWetqTFn5Au4m4GFg7xJaNVN2 | 原始格式,费用高,适合老式钱包 | 284 | | P2SH | 3 | 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy | 支持复杂脚本,兼容性好 | 285 | | SegWit (P2WPKH) | bc1q | bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq | 标准SegWit支付,费用低,抗输入错误 | 286 | | SegWit (P2WSH) | bc1 | bc1qrp33g0q5c5txsp9arysrxwj7a0r24kheu37q2vc9iam624dmybsceqc2f | 脚本哈希支付,地址较长 | 287 | | Taproot (P2TR) | bc1p | bc1p5d7rjq7g6rdk2yhzks9smlaqtedr4dekq08ge8ztwac72sfr9rusxg3297 | 隐私增强,支持Schnorr签名,采用率提升中 | 288 | 289 | ## 18.描述一些 UTXO 和账户模型的区别 290 | 291 | - 账户模型: 292 | - 账户模型用于像以太坊这样的区块链。每个账户有一个余额,交易时直接从你的余额中扣除,添加到接收者的余额,就像银行转账。 293 | - UTXO: 294 | - UTXO 模型用于像比特币这样的区块链。每个交易有输入和输出,输入是之前未花费的交易输出(UTXO),输出是新的 295 | UTXO。就像你有几张钞票,要买东西时用掉一些,可能还会有找零。 296 | 297 | ### 主要区别 298 | 299 | - 交易处理:UTXO 需要消耗整个 UTXO 并可能创建找零;账户模型直接调整余额,无需找零。 300 | - 状态管理:UTXO 区块链跟踪所有 UTXO;账户区块链跟踪账户余额。 301 | - 隐私:UTXO 链隐私更好,因为交易不直接与账户关联;账户链交易与特定账户相关联。 302 | - 复杂性:账户链更易支持复杂智能合约;UTXO 链脚本能力较简单。 303 | 304 | ### 以下是两者的关键差异,整理为表格形式: 305 | 306 | | 方面 | UTXO模型 | 账户模型 | 307 | |:----:|:--------------------------------------------------------------------:|:------------------------------------------------------:| 308 | | 定义 | 使用未花费交易输出记账方法,无账户/钱包概念,源于比特币白皮书 (https://bitcoin.org/bitcoin.pdf)。 | 类似银行账户的余额管理系统,如以太坊、EOS、Tron。 | 309 | | 示例链 | 比特币、比特币现金、Zcash、Litecoin、Dogecoin、Dash。 | 以太坊、EOS、Tron、以太坊经典。 | 310 | | 交易过程 | 消耗现有 UTXO 创建新 UTXO,UTXO 必须整体消耗(如现金钞票)。 | 可部分花费余额,无需找零。例如,从 100 ETH 发送 37.5 ETH,余额直接变为 62.5 ETH。 | 311 | | 交易示例 | 发送 3.75 BTC,输入一个 10 BTC UTXO:接收者得 3.75 BTC,发送者得 6.25 BTC 找零(新 UTXO)。 | 发送 37.5 ETH:发送者余额变为 62.5 ETH,接收者得 37.5 ETH。 | 312 | | 效率 | 效率较低;需跟踪所有 UTXO 计算余额,对 dApp 开发挑战较大。 | 效率较高;只需检查发送者余额是否足够。 | 313 | | 隐私性 | 隐私性较好,交易不直接与账户关联,通过地址管理。 | 隐私性较差,所有交易与特定账户关联,除非使用额外隐私工具。 | 314 | | 复杂性 | 脚本能力有限,适合简单货币交易,难以支持复杂智能合约。 | 支持图灵完备智能合约,适合 dApp 开发。 | 315 | 316 | ## 20.解释一下什么是 EVM 同源链,举例说明一下 317 | 318 | 1. EVM 同源链是指能够运行以太坊虚拟机(EVM)智能合约的区块链,开发者可以用 Solidity 编写合约并部署到这些链上,体验与以太坊类似。 319 | 2. 这些链允许开发者复用以太坊生态的工具和应用,降低开发成本,同时通常提供更低的交易费用和更高的性能。 320 | 3. 一些 EVM 同源链如 Avalanche C-Chain 和 Polygon,不仅支持以太坊的智能合约,还通过自己的扩展提升了交易速度和隐私保护。 321 | 322 | ### 以下是几个知名的 EVM 同源链及其特点,整理如下表: 323 | 324 | | 链名 | 类型 | 特点 | 示例用例 | 325 | |:-----------------:|:------------:|:----------------------------:|:-------------------------:| 326 | | Polygon | 二层扩展方案 | 低交易费用,高吞吐量,PoS桥接以太坊主网 | NFT 市场(如 OpenSea)、DeFi 协议 | 327 | | BNB Chain | 独立公链 | 高性能,低费用,支持 Binance 生态 | 去中心化交易所、稳定币发行 | 328 | | Avalanche C-Chain | 子网(EVM 兼容) | 快速交易,低延迟,支持多子网架构 | DeFi 应用、跨链桥接 | 329 | | Fantom | 独立公链 | 使用 Lachesis 共识,快速确认,低 gas 费用 | DeFi 协议、链上治理 | 330 | | Arbitrum | 二层扩展方案(乐观卷积) | 低费用,继承以太坊安全性,适合高频交易 | 链上游戏、支付应用 | 331 | | Optimism | 二层扩展方案(乐观卷积) | 低 gas 费用,专注于以太坊生态扩展 | DeFi 协议、链上社交 | 332 | 333 | ## 21.ERC721 和 ERC1155 区别与联系 334 | 335 | - ERC721 和 ERC1155 的区别:ERC721 专为非同质化代币(NFT)设计,每个代币都是独特的;ERC1155 可以管理同质化代币和非同质化代币,更加灵活,支持批量操作。 336 | - ERC721 和 ERC1155 的联系:ERC1155 可以创建非同质化代币,包含了 ERC721 的功能,是更全面的标准。 337 | 338 | ### 以下是 ERC721 和 ERC1155 的详细对比,整理为表格形式: 339 | 340 | | 方面 | ERC721 | ERC1155 | 341 | |:----:|:-------------------------------------------------------:|:---------------------------------------------------:| 342 | | 代币类型 | 仅支持非同质化代币(NFT),每个代币唯一。 | 支持同质化、非同质化和半同质化代币,可在一个合约中管理多种类型。 | 343 | | 批量操作 | 不支持批量转移,需要多次调用 transferFrom 或 safeTransferFrom,gas 成本高。 | 支持批量转移(如 safeBatchTransferFrom),一次交易可处理多个代币,节省 gas。 | 344 | | 用途 | 适合仅需管理独特资产的项目,如数字艺术品或收藏品。 | 适合需要管理多种代币类型的项目,如游戏生态或资产管理平台。 | 345 | | 效率 | 效率较低,适合简单 NFT 应用。 | 效率更高,适合复杂场景,减少合约部署成本。 | 346 | | 批准机制 | 使用 approve 函数,针对特定代币设置批准地址。 | 使用 setApprovalForAll,允许另一个地址管理所有代币,方便市场操作。 | 347 | 348 | ## 22. Cosmos 共识算法是什么 349 | 350 | ## 23. Cosmos 钱包资金精度 351 | 352 | ## 24. Cosmos 签名结构中的 account_number 和 seqence 怎么获取 353 | 354 | ## 25. memo 是什么,在中心化钱包开发中有什么作用,请举例说明那些链带有 Memo, Memo 另一个名字是什么 355 | 356 | ## 26. 简述 Cosmos 的 Interchain Security 和 IBC Protocol 357 | 358 | ## 27. cosmos-sdk 的应用场景 359 | 360 | ## 28. solana 交易签名有有效期说法吗?若有情描述什么场景的签名会出现这种状况,怎么去解决? 361 | 362 | ### 29. 怎么去检验账号处于 active? 363 | 364 | ### 30. solana 代币精度 365 | 366 | ### 31. 什么是 EVM 同源链,EVM 同源链钱包和 Ethereum 钱包开发中有什么区别 367 | 368 | ### 32. Cosmos 新版本和老版本签名异同点点 369 | 370 | ### 33. 简要说明 EOS 账户的激活过程 371 | 372 | ### 34. 和 EOS 同源的有哪些链 373 | 374 | ### 35. SUI 链的特点 375 | 376 | ### 36. Tron 签名和 EVM 链的不同点(和 leagcy 交易类型相比较) 377 | 378 | ### 37.KDA 由多少条链组成,账户这些链之后有关联吗 379 | 380 | ### 38.KDA 跨链转账的流程描述 381 | 382 | ### 39.KDA 共识算法独立吗?矿工奖励有关联吗 383 | 384 | ### 40.ERC721 和 ERC1155 区别 385 | 386 | ### 41.NFT MINT 流程 387 | 388 | ### 42.LSD 产品的质押流程(以 lido 为例说明) 389 | 390 | ### 43. Solana 质押流程 391 | 392 | ### 44. Tezos 质押流程 393 | 394 | ## 45.如何计算一笔 BTC 交易的所需要的 gasFee,有什么方案 395 | 396 | 比特币交易的“gasFee”实际上是指交易费用(transaction fee),以 satoshis 为单位计算,取决于交易大小(以虚拟字节 vB 为单位)和当前每 397 | vB 的费用率。 398 | 399 | ### 交易费用通过以下公式计算: 400 | 401 | 交易费用(sat) = 交易大小(vB) × 每 vB 费用率(sat/vB) 402 | 403 | - 交易大小(vB)根据交易类型不同而变化,例如标准 P2PKH 交易约 226 vB,SegWit P2WPKH 交易约 140 vB。 404 | - 每 vB 费用率由市场决定,可通过 Bitcoin Core、Blockchain.com 或 BitcoinFees 等工具查询。 405 | 406 | | 交易类型 | 典型 vB 大小 | 说明 | 407 | |:----------------:|:--------:|:-----------------------------:| 408 | | 标准 P2PKH 交易 | 约 226 vB | 一个输入、两个输出(一个给接收者,一个找零)。 | 409 | | SegWit P2WPKH 交易 | 约 140 vB | 使用隔离见证,输入和输出较小,费用更低。 | 410 | | 复杂交易(多输入/输出) | 视情况而定 | 每个额外输入约增加 41-62 vB,输出约 31 vB。 | 411 | 412 | ### 每 vB 费用率的获取 413 | 414 | 费用率由市场动态决定,基于当前 mempool(待处理交易池)的拥堵程度。以下是获取费用率的主要方案: 415 | 416 | - Bitcoin Core 的 estimatefee 命令: 417 | - 如果运行 Bitcoin Core 节点,可使用 `estimatefee` 命令,输入期望的确认块数(如 1 块确认),返回建议的费用率。 418 | - 示例:`estimatefee 1` 返回当前网络中 1 块确认所需的费用率(sat/vB)。 419 | - 第三方服务: 420 | - 使用网站如 Blockchain.com 或 BitcoinFees,提供实时费用率建议,通常分为高、中、低优先级。 421 | - 例如,当前高优先级可能为 20 sat/vB,中优先级 10 sat/vB,低优先级 5 sat/vB。 422 | - 钱包工具: 423 | - 钱包如 Electrum 或 BlueWallet 提供动态费用建议,允许用户选择“快速”、“经济”或自定义费用率。 424 | - 例如,选择“快速”可能对应 20 sat/vB,确保下一块确认。 425 | 426 | ### 计算示例 427 | 428 | - 假设一个 SegWit P2WPKH 交易,vB 大小为 140,当前费用率为 10 sat/vB: 429 | - 交易费用 = 140 × 10 = 1400 sat = 0.00001400 BTC 430 | - 再假设一个标准 P2PKH 交易,vB 为 226,费用率为 10 sat/vB: 431 | - 交易费用 = 226 × 10 = 1400 sat = 0.0.00002260 BTC 432 | 433 | 可见,SegWit 交易费用更低。 434 | 435 | ### 以下是计算交易费用的具体方案: 436 | 437 | - 手动计算: 438 | - 确定交易类型,估算 vB 大小(基于输入/输出数量)。 439 | - 从上述来源获取当前费用率,代入公式计算。 440 | - 适合技术用户,但需注意 vB 计算的复杂性。 441 | - 钱包自动计算: 442 | - 大多数比特币钱包(如 Electrum、BlueWallet)自动计算费用,基于用户选择的优先级。 443 | - 用户可自定义费用率,适合灵活控制。 444 | - 在线计算器: 445 | - 使用在线工具如 Bitcoin Transaction Fee Calculator,输入交易详情(输入/输出数量、类型),获取费用估算。 446 | - 适合非技术用户,快速获取结果。 447 | - 动态估算算法: 448 | - Bitcoin Core 使用基于 mempool 的算法,预测不同确认时间所需的费用率。 449 | - 第三方服务可能使用中位数或百分位数(如 90% 交易支付的费用率)估算。 450 | 451 | ### 46.如何计算一笔evm交易的所需要的gasfee,有什么方案 452 | 453 | ## 47.如何处理BTC的交易执行缓慢,有什么方案,分别有什么区别? 454 | 455 | 1. 提高交易费用(Replace By Fee, RBF) 456 | - 如果你是发送方,且钱包支持“替换按费用”(RBF),可以替换原交易,设置更高的费用以加速确认。需要确保原交易未确认且支持RBF。 457 | - 定义:RBF允许发送方替换未确认交易,新的交易需支付更高费用以提高优先级。 458 | - 工作原理:原交易需设置RBF标志(opt-in RBF),新交易必须支付更高的总费用,且输出保持相同或更高。矿工会优先处理费用更高的交易。 459 | - 适用场景:适合发送方,且钱包支持RBF(如Bitcoin Core、Electrum)。 460 | - 实施步骤: 461 | 1. 确认原交易未确认且支持RBF。 462 | 2. 使用钱包的“增加费用”功能(如Blockstream Green)或手动创建新交易。 463 | 3. 设置更高费用率(如从5 sat/vB提高到20 sat/vB)。 464 | - 限制:需钱包支持,部分钱包默认关闭RBF;若原交易已确认,RBF无效。 465 | - 示例:假设原交易费用为1400 sat(140 vB × 10 sat/vB),可替换为费用2260 sat(140 vB × 16 sat/vB)以加速。 466 | 2. 使用子支付父(Child Pays for Parent, CPFP) 467 | - 如果你是接收方,且控制了被卡交易的输出,可以创建一个新交易,花费该输出并支付高费用,激励矿工同时确认两者。适合接收方操作。 468 | - 定义:接收方创建一个新交易,花费被卡交易的输出,并支付高费用,激励矿工同时确认两者。 469 | - 工作原理:新交易(子交易)的高费用率使矿工愿意同时确认其依赖的父交易。比特币共识规则要求父交易先于子交易被确认。 470 | - 适用场景:适合接收方,且控制了被卡交易的输出地址。 471 | - 实施步骤: 472 | 1. 确认被卡交易输出地址由你控制(如Electrum中查看详情)。 473 | 2. 创建新交易,输入为被卡交易的输出,输出为你的新地址,设置高费用(如20 sat/vB)。 474 | 3. 广播新交易,等待矿工确认。 475 | - 限制:需有足够余额支付子交易费用;若父交易输出已被花费,CPFP不可用。 476 | - 示例:若父交易费用低(5 sat/vB),子交易可设20 sat/vB,总费用率提高,矿工更倾向确认。 477 | 3. 交易加速服务 478 | - 通过第三方服务(如ViaBTC或BitAccelerate)提交交易ID,支付费用让矿工优先处理。服务有免费和付费选项,但需注意潜在风险。 479 | - 定义:第三方服务通过与矿池合作,优先处理用户提交的交易,通常需支付费用。 480 | - 工作原理:用户提交交易ID(TXID),服务通过多节点重播或直接请求矿池优先处理。部分服务免费,部分收费。 481 | - 适用场景:当RBF和CPFP不可用时,或用户不熟悉技术操作。 482 | - 常见服务: 483 | - ViaBTC:提供免费(限时)和付费服务,付费可加速1000次/小时,费用0.0001 BTC/KB (ViaBTC Transaction Accelerator)。 484 | - BitAccelerate:免费重播交易至10节点,无需注册 (BitAccelerate)。 485 | - BTC.com:批处理加速,费用低,但需支付加速费。 486 | - 限制:效果不确定,可能存在诈骗风险;收费服务需额外成本。 487 | - 示例:提交TXID至ViaBTC,支付0.0001 BTC/KB,交易可能1小时内确认。 488 | 4. 等待网络恢复 489 | - 如果不急,可以等待网络拥堵缓解,交易自然确认,但可能需要较长时间。 490 | - 定义:不采取行动,等待网络拥堵缓解,交易自然被矿工确认。 491 | - 工作原理:比特币每10分钟生成一个块,块大小1MB,拥堵时低费用交易被延迟。网络恢复后,交易可能被处理。 492 | - 适用场景:不急需资金,且能接受较长等待时间(如周末网络较空闲)。 493 | - 限制:可能需数小时或数天,交易可能被内存池丢弃。 494 | - 示例:若当前费用率10 sat/vB,等待至周末费用率降至5 sat/vB,交易可能被确认。 495 | 496 | #### 令人惊讶的细节:SegWit的优势 497 | 498 | 令人惊讶的是,使用SegWit交易因体积小(约140 vB vs 226 vB),费用低,更易被矿工优先确认,适合未来交易预防延迟。 499 | 500 | ### 以下是各方案的对比表: 501 | 502 | | 方案 | 适用角色 | 费用 | 技术要求 | 效果不确定性 | 适用场景 | 503 | |:------:|:------:|:--------:|:----------:|:------:|:--------------:| 504 | | RBF | 发送方 | 无额外(原交易) | 中等(需RBF支持) | 低 | 发送方,钱包支持RBF | 505 | | CPFP | 接收方 | 无额外(原交易) | 中等(需控制输出) | 低 | 接收方,控制输出地址 | 506 | | 交易加速服务 | 发送/接收方 | 可能需付费 | 低(提交TXID) | 中等至高 | 其他方案不可用,需快速确认 | 507 | | 等待网络恢复 | 发送/接收方 | 无 | 无 | 高 | 不急需资金,能接受长等待时间 | 508 | 509 | - 选择建议: 510 | - 优先尝试RBF(发送方)或CPFP(接收方),无额外成本,效果较确定。 511 | - 若无法操作,可选择信誉良好的交易加速服务,但注意费用和风险。 512 | - 若不急,可等待网络恢复,但需耐心。 513 | 514 | ### 48.如何处理EVM的交易执行缓慢,有什么方案,分别有什么区别? 515 | 516 | ### 49.请设计一个合约的多签钱包 517 | 518 | ### 50.你怎么对接的tee?怎么请求的tee?tee之间是如何相互通讯的? 519 | 520 | ### 51.mpc密钥的安全性保证 521 | 522 | ### 52.Solana 代币的特殊性 523 | 524 | ### 53.aptos代币的特殊性 525 | 526 | ### 54.椭圆曲线算法底层实现,以及rsv是什么?分别介绍一下 527 | 528 | ### 55.ecdsa 和 eddsa 区别 529 | 530 | ### 56.签名机有支持HD钱包方式吗? 531 | 532 | ### 57.签名的安全传输方案 533 | 534 | ### 58.BTC和ETH签名的全流程 535 | 536 | ### 59.交易里面如何处理合约充值 537 | 538 | ### 60.什么是 Bitcoin 交易延展性,隔离见证是如何消除了交易延展性 539 | 540 | ### 61.solana地址大小写敏感怎么处理,比如你发给A的地址却发到了a的地址,有遇到过吗,怎么处理 541 | 542 | ### 62. solana没有abi 你怎么处理合约交易 543 | 544 | ### 63 要统计很多代币合约的热度,交易量,用户排行榜等,这个系统等架构数据库啊,技术选型你怎么设计? 545 | 546 | ### 64 正常派生和硬化派生的区别与联系 547 | 548 | ## 65 以太坊RLP序列化时ChainID的处理 549 | 550 | - RLP(Recursive Length Prefix):RLP是以太坊中用于序列化数据结构的编码方式,主要用于编码交易、区块等数据结构。 551 | 552 | - ChainID的作用:ChainID用于区分不同的以太坊网络(如主网、测试网等),防止重放攻击(即在一个网络上的交易被复制到另一个网络)。 553 | 554 | - ChainID在RLP中的处理: 555 | 556 | - 在以太坊交易中,ChainID是EIP-155引入的,用于签名交易。 557 | 558 | - 559 | 在RLP编码中,ChainID会被包含在交易的签名数据中,具体格式为:[nonce, gasPrice, gasLimit, to, value, data, chainId, 0, 0]。 560 | 561 | - 签名时,ChainID会被编码到签名的v值中,公式为:v = recovery_id + 35 + chainId * 2。 562 | 563 | - 总结:ChainID通过RLP编码和签名机制,确保交易只能在特定网络中生效。 564 | 565 | ## 66 Protobuf序列化之后的二进制结构 566 | 567 | - Protobuf(Protocol Buffers):是一种高效的二进制序列化格式,由Google开发,用于结构化数据的序列化和反序列化。 568 | 569 | - 二进制结构: 570 | 571 | - Protobuf使用Tag-Length-Value(TLV)格式编码数据。 572 | 573 | - Tag:由字段编号和数据类型组成,占用1个或多个字节。 574 | 575 | - 字段编号:用于标识字段。 576 | 577 | - 数据类型:标识字段的类型(如Varint、64-bit、Length-delimited等)。 578 | 579 | - Length:对于长度可变的数据类型(如字符串、字节数组),会有一个长度字段。 580 | 581 | - Value:字段的实际值。 582 | 583 | - 示例: 584 | 585 | - 对于字段int32 id = 1;,如果id = 42,编码结果为:08 2A。 586 | 587 | - 08:Tag(字段编号1,数据类型Varint)。 588 | 589 | - 2A:Value(42的Varint编码)。 590 | 591 | - 总结:Protobuf的二进制结构紧凑高效,适合网络传输和存储。 592 | 593 | ## 67.Shamir共享秘密的本质和使用流程 594 | 595 | Shamir共享秘密的本质和使用流程 596 | 597 | - Shamir共享秘密:由Adi Shamir提出,是一种秘密共享方案,将秘密分成多个部分(称为“份额”),只有收集到足够数量的份额才能恢复秘密。 598 | 599 | - 本质: 600 | 601 | - 基于多项式插值,将秘密编码为一个多项式的常数项。 602 | 603 | - 生成多个点(份额),只有收集到足够数量的点才能重建多项式并恢复秘密。 604 | 605 | 使用流程: 606 | 607 | - 初始化: 608 | 609 | - 选择一个素数p作为有限域。 610 | 611 | - 选择一个秘密s(常数项)。 612 | 613 | - 生成一个k-1次多项式:f(x) = s + a₁x + a₂x² + ... + aₖ₋₁x^(k-1)。 614 | 615 | - 生成份额: 616 | 617 | - 为每个参与者生成一个点(x, f(x))。 618 | 619 | - 恢复秘密: 620 | 621 | - 收集至少k个点,使用拉格朗日插值法重建多项式,恢复秘密s。 622 | 623 | - 总结:Shamir共享秘密是一种安全的分布式存储方案,适用于密钥管理和多方协作场景。 624 | 625 | ## 68.secp256k1和r1的区别,以及比特币和以太坊为何选择secp256k1 626 | 627 | - secp256k1: 628 | 629 | - 椭圆曲线方程:y² = x³ + 7。 630 | 631 | - 特点:高效、计算速度快,适合区块链场景。 632 | 633 | - 使用范围:比特币、以太坊等区块链系统。 634 | 635 | - secp256r1(也称为NIST P-256): 636 | 637 | - 椭圆曲线方程:y² = x³ - 3x + b。 638 | 639 | - 特点:参数由NIST标准化,安全性较高,但计算效率略低于secp256k1。 640 | 641 | - 使用范围:TLS、SSL等传统加密场景。 642 | 643 | - 区别: 644 | 645 | - 曲线参数不同:secp256k1的曲线参数简单,secp256r1的参数由NIST定义。 646 | 647 | - 性能:secp256k1在签名和验证速度上更快。 648 | 649 | - 安全性:两者均被认为安全,但secp256k1的简单性使其更受区块链青睐。 650 | 651 | - 比特币和以太坊选择secp256k1的原因: 652 | 653 | - 效率:secp256k1的计算速度更快,适合区块链的高频交易场景。 654 | 655 | - 透明性:secp256k1的曲线参数简单,避免了NIST可能存在的后门争议。 656 | 657 | - 社区共识:比特币率先采用secp256k1,以太坊为兼容性和一致性也选择该曲线。 658 | 659 | 660 | ## 69.回滚区块的时候,是怎么确认回滚哪个块 661 | 662 | ## 70.mpc 签名后的交易 signature 是明文,不需要加密嘛?不会被黑客盗走 663 | 664 | ## 71.如果 solana 那边区块节点挂了,出现了区块堆积,他们那边恢复以后,我们从最新区块接着扫,然后断掉的区块用另一个定时任务去扫,但是流水表(交易日报)怎么接上去 665 | 666 | ## 72.怎么解决重放攻击,有些链是没有链 ID,比如 xlm 链没有链ID 667 | 668 | ## 73.你们项目的归集是怎么做的? 那么大批量的归集(20-30万笔), 是一笔一笔的签名吗? 669 | 670 | ## 74.你们项目的充值提现有没有做过什么安全措施 671 | 672 | ## 75.你们签名机的热钱包和冷钱包是怎么进行数据隔离和权限隔离的? 673 | 674 | ## 76.合约内部交易是怎么解析的 675 | 676 | ## 77.用第三节点和自建节点的区别与联系 677 | 678 | ## 78.如何获取 Solana 智能合约的 IDL 679 | 680 | ## 79.uniswap上发一笔交易,过程发生了什么越详细越好 681 | 682 | ## 80.请详细说一下 Solana 交易的解析问题 683 | 684 | ## 81.交易所储备金证明系统怎么做的 685 | 686 | ## 82.大规模的批量提现 687 | 688 | ## 83.假充值怎么解决 689 | 690 | 假充值例子: 691 | - 伪造代币合约充值:部署假的代币合约,模仿ETH、USDT代币的名称和符号,但合约地址不一样。通过调用合约方法,直接修改目标钱包的余额显示。 692 | - 解决方式: 693 | - 钱包系统内有白名单地址,只有配置了白名单的合约,才能往交易所内充值; 694 | - 代币充值,钱包也是有个token表来配置,只有配置了的代币才能往里面冲。 695 | - 虚假转账记录:通过伪造交易数据,让钱包显示收到一笔转账,但这笔交易并未发生在链上。 696 | - 技术原理: 697 | - 利用钱包软件的漏洞,直接修改本地显示数据(较少见,需用户使用被篡改的钱包) 698 | - 或通过虚假节点返回伪造的区块链数据(针对未验证的 RPC 端点) 699 | - 解决方式:使用一个RPC节点,来执行一笔转账交易,但不上链,看看能否成功,用来表明该账户是不是有充值这么多金额 700 | 701 | 一般解决方式: 702 | - 通过区块链浏览器检查交易记录 703 | - 验证合约地址 704 | - 测试转账 705 | - 请求风控校验 706 | 707 | ## 84.交易所资产证明,证明自己有钱 708 | 709 | - https://github.com/the-web3/chaineye-binance-por 币安的资产证明的整个项目概述 710 | 711 | ## 85.私钥怎么和助记词对应,路径推导的协议怎么推导出来的 712 | 713 | 如果是使用12个助记词的话,首先生成128位随机熵,然后进行SHA-256哈希计算得到哈希值,取出哈希值的前四位,拼接到随机熵后面。之后取每十一位来转换成10进制索引,从2048个单词里得到相应的助记词。 714 | 然后使用PBKDF2基于密码的密钥派生函数,以助记词为入参,对助记词进行2028次哈希计算(HMAC-SHA512),得到512位(64字节)的Seed。 715 | 716 | 种子生成后,通过BIP32标准推导主私钥和主链码: 717 | 718 | 对种子使用HMAC-SHA512计算,得到主私钥(前32字节)和主链码(后32字节) 719 | 720 | ## 86.怎么算solana多少cu,一个账户占多少字节 721 | 722 | ## 87.Solana签名,怎么签 feepayer,两个签名怎么拼 723 | 724 | 签名规则: 725 | - feePayer必须是第一个签名者 726 | - 其他签名者按accountKeys中需要签名的顺序排列 727 | - 签名者对消息部分(Message)的哈希进行签名 728 | 问题场景: 729 | - 目标:feePayer使用私钥A签名,指令的发起者使用私钥B签名 730 | - 挑战: 731 | - 默认情况下,Transaction.sign会用单一私钥签名所有需要签名的账户 732 | - 需要分离签名过程,确保每个私钥只签名其对应的账户 733 | 手动拼接步骤: 734 | - 构造交易信息: 735 | - 使用@solana/web3.js创建交易并提取消息部分 736 | - 设置feePayer和其他签名账户 737 | - 序列化消息:获取交易的Message部分并序列化,用于签名 738 | - 分别签名: 739 | - 使用每个私钥对消息进行独立签名 740 | - 确保签名顺序与accountKeys中需要签名的账户顺序一致 741 | - 拼接交易:将签名数组和消息部分手动合成完整的交易字节数组 742 | - 验证和发送:将拼接后的交易提交到网络 743 | 744 | ``` 745 | import { Connection, Keypair, Transaction, SystemProgram, PublicKey } from "@solana/web3.js"; 746 | import { sign } from "@solana/web3.js/lib/ed25519"; // 手动签名工具 747 | 748 | const connection = new Connection("https://api.devnet.solana.com", "confirmed"); 749 | 750 | // 生成两个密钥对 751 | const feePayer = Keypair.generate(); // 支付费用的账户 752 | const sender = Keypair.generate(); // 转账发起者 753 | const recipient = Keypair.generate(); // 接收者 754 | 755 | async function createAndSignTransaction() { 756 | // 构造交易 757 | const tx = new Transaction(); 758 | const { blockhash } = await connection.getLatestBlockhash(); 759 | tx.recentBlockhash = blockhash; 760 | tx.feePayer = feePayer.publicKey; 761 | 762 | // 添加转账指令 763 | tx.add( 764 | SystemProgram.transfer({ 765 | fromPubkey: sender.publicKey, 766 | toPubkey: recipient.publicKey, 767 | lamports: 1000000, // 1 SOL 768 | }) 769 | ); 770 | 771 | // 编译消息(不签名) 772 | const message = tx.compileMessage(); 773 | const serializedMessage = message.serialize(); 774 | 775 | // 分别签名 776 | const feePayerSignature = sign(serializedMessage, feePayer.secretKey); // feePayer 签名 777 | const senderSignature = sign(serializedMessage, sender.secretKey); // sender 签名 778 | 779 | // 手动拼接交易 780 | const numSignatures = 2; // feePayer + sender 781 | const signatures = [ 782 | feePayerSignature, // 第一个签名是 feePayer 783 | senderSignature, // 第二个签名是 sender 784 | ]; 785 | 786 | // 创建完整的交易字节数组 787 | const txBytes = Buffer.concat([ 788 | Buffer.from([numSignatures]), // 签名数量 789 | ...signatures.map(sig => Buffer.from(sig)), // 签名数组 790 | Buffer.from(serializedMessage), // 消息部分 791 | ]); 792 | 793 | // 转换为 base58 编码(可选,用于 RPC) 794 | const base58 = require("bs58"); 795 | const encodedTx = base58.encode(txBytes); 796 | 797 | // 模拟交易(验证) 798 | const simulation = await connection.simulateTransaction( 799 | Transaction.from(txBytes), 800 | { commitment: "confirmed" } 801 | ); 802 | console.log("Simulation Result:", simulation.value); 803 | 804 | // 发送交易(需确保账户有足够 SOL) 805 | // const signature = await connection.sendRawTransaction(txBytes); 806 | // console.log("Transaction Signature:", signature); 807 | } 808 | 809 | createAndSignTransaction().catch(console.error); 810 | ``` 811 | 构造交易消息:创建交易并设置 feePayer 和指令。 812 | 序列化消息:提取交易的 Message 并序列化为字节数组。 813 | 分别签名:使用每个私钥对消息签名。 814 | 拼接交易:将签名和消息组合成完整交易。 815 | 验证和发送:通过 RPC 模拟或提交交易。 816 | 817 | Golang实现方式: 818 | ``` 819 | package main 820 | 821 | import ( 822 | "context" 823 | "fmt" 824 | "log" 825 | 826 | "github.com/gagliardetto/solana-go" 827 | "github.com/gagliardetto/solana-go/programs/system" 828 | "github.com/gagliardetto/solana-go/rpc" 829 | "github.com/gagliardetto/solana-go/text" 830 | ) 831 | 832 | func main() { 833 | // 初始化 RPC 客户端 834 | client := rpc.New("https://api.devnet.solana.com") 835 | 836 | // 生成密钥对 837 | feePayer, err := solana.NewRandomPrivateKey() 838 | if err != nil { 839 | log.Fatalf("Failed to generate feePayer key: %v", err) 840 | } 841 | sender, err := solana.NewRandomPrivateKey() 842 | if err != nil { 843 | log.Fatalf("Failed to generate sender key: %v", err) 844 | } 845 | recipient := solana.NewWallet().PublicKey() 846 | 847 | fmt.Printf("FeePayer: %s\n", feePayer.PublicKey()) 848 | fmt.Printf("Sender: %s\n", sender.PublicKey()) 849 | fmt.Printf("Recipient: %s\n", recipient) 850 | 851 | // 获取最近区块哈希 852 | ctx := context.Background() 853 | recent, err := client.GetLatestBlockhash(ctx, rpc.CommitmentFinalized) 854 | if err != nil { 855 | log.Fatalf("Failed to get recent blockhash: %v", err) 856 | } 857 | 858 | // 构造交易 859 | tx := solana.NewTransactionBuilder(). 860 | SetFeePayer(feePayer.PublicKey()). 861 | SetRecentBlockHash(recent.Value.Blockhash). 862 | AddInstruction( 863 | system.NewTransferInstruction( 864 | 1000000, // 1 SOL 865 | sender.PublicKey(), 866 | recipient, 867 | ).Build(), 868 | ) 869 | 870 | // 编译消息(不签名) 871 | message, err := tx.Build() 872 | if err != nil { 873 | log.Fatalf("Failed to build message: %v", err) 874 | } 875 | 876 | // 序列化消息 877 | serializedMessage, err := message.MarshalBinary() 878 | if err != nil { 879 | log.Fatalf("Failed to serialize message: %v", err) 880 | } 881 | 882 | // 分别签名 883 | feePayerSig, err := feePayer.Sign(serializedMessage) 884 | if err != nil { 885 | log.Fatalf("Failed to sign with feePayer: %v", err) 886 | } 887 | senderSig, err := sender.Sign(serializedMessage) 888 | if err != nil { 889 | log.Fatalf("Failed to sign with sender: %v", err) 890 | } 891 | 892 | // 拼接交易 893 | signatures := []solana.Signature{feePayerSig, senderSig} 894 | completeTx := &solana.Transaction{ 895 | Signatures: signatures, 896 | Message: *message, 897 | } 898 | 899 | // 序列化为字节数组(可选,用于手动检查) 900 | txBytes, err := completeTx.MarshalBinary() 901 | if err != nil { 902 | log.Fatalf("Failed to marshal transaction: %v", err) 903 | } 904 | 905 | // 模拟交易 906 | simResult, err := client.SimulateTransactionWithOpts( 907 | ctx, 908 | completeTx, 909 | &rpc.SimulateTransactionOpts{ 910 | SigVerify: false, // 不验证签名(测试用) 911 | Commitment: rpc.CommitmentFinalized, 912 | ReplaceRecentBlockhash: false, 913 | }, 914 | ) 915 | if err != nil { 916 | log.Fatalf("Failed to simulate transaction: %v", err) 917 | } 918 | 919 | // 输出结果 920 | if simResult.Value.Err != nil { 921 | fmt.Printf("Simulation failed: %v\n", simResult.Value.Err) 922 | } else { 923 | fmt.Printf("Simulation succeeded:\n") 924 | fmt.Printf("Units Consumed: %d\n", simResult.Value.UnitsConsumed) 925 | for _, log := range simResult.Value.Logs { 926 | fmt.Println(log) 927 | } 928 | } 929 | 930 | // 发送交易(需确保账户有资金) 931 | // sig, err := client.SendTransaction(ctx, completeTx) 932 | // if err != nil { 933 | // log.Fatalf("Failed to send transaction: %v", err) 934 | // } 935 | // fmt.Printf("Transaction Signature: %s\n", sig) 936 | } 937 | ``` 938 | 939 | ## 88.哈希环是不是一致性hash算法? 940 | 941 | 942 | ## 89.布隆过滤器,怎么避免误判? 943 | 944 | 945 | ## 90.工作中有没有遇到资金损失,或者系统故障,怎么解决的 946 | 947 | 948 | ## 91.提现成功率多少 949 | 950 | 951 | ## 92.每条链,每天多少笔 952 | 953 | 954 | ## 93.钱包对账怎么做的 955 | 956 | 957 | ## 93.BTC粉尘资金多少 958 | - P2PKH 546聪 959 | - P2SH 828聪 960 | - P2WPKH 270聪 961 | - P2WSH 417聪 962 | - P2TR 276聪 963 | 964 | ## 94.Solana匹配交易的时候,lookup account什么时候用 965 | 966 | 967 | ## 95.比特币usdt怎么实现的? 968 | 比特币原生不支持发行代币,USDT 是通过Omni Layer 协议在比特币之上“搭建了一层”,来实现“代币发行和转账”的。 969 | - blockchain-wallet/Omni/README.md at master · the-web3/blockchain-wallet 970 | - https://github.com/OmniLayer/spec 971 | 972 | ## 96.BTC你们怎么使用账本的,提现时使用哪种账本,怎么做 973 | - 提现时,通过最小化找零算法,来找一个金额相似的账本用来提现 974 | 975 | ## 97.不同layer2有些用的debug_traceTransaction,有些不支持,你们用的什么或者你知道接口名字吗 976 | 977 | - 支持debug_traceTransaction的链:Optimism、Arbitrum、Polygon PoS 978 | - 不支持的链: 979 | - zkSync:使用zks_getTransactionDetails接口 980 | 981 | ## 98.solana使用getblock接口,里面encoding参数json、jsonParsed有什么区别? 982 | 983 | 984 | ## 99.钱包导入助记词,有些钱包就把我不同地址资产识别出来了,怎么做的 985 | 986 | ## 100.一个私钥,在一个spl-token里面可以有几个 ata 账户 987 | 988 | ## 100.BTC getBlock接口 989 | 990 | ## 101.如何验证一笔交易是合法的。在RPC节点里面是否可以验证?如果可以的话,RPC节点是如何验证这个交易是合法的(具体验证哪些字段) 991 | 992 | ## 102.如何节省gas费?从数据结构设计的角度展开说说? 993 | 994 | ## 103.调用ec2的服务器被黑了咋办 995 | 996 | ## 104.温钱包,多签(你们温钱包怎么实现的) 997 | 998 | ## 105.你们这个签名机方案如果是业务层被端了,你们咋办 999 | 1000 | ## 106.讲一下cosmos的特点 1001 | 1002 | ## 107.说明下solana的ata账户 1003 | 1004 | ## 108.归集的时候问了下手续费的计算 1005 | 1006 | ## 109.token 归集的时候需要注意哪些 1007 | 1008 | ## 110.BTC 提现的时候,用哪个账本算法?引出问题:BTC提现的时候,不都是已经归集了吗?为啥还要考虑这个算法 1009 | 1010 | ## 111.有没有遇到过,提现的时候没有,还有一些用户资金还没有归集,那么你们是如何处理的呢? 1011 | 1012 | ## 112.你们钱包和风控的交互是咋么样的?请具体描述一下 1013 | 1014 | 1015 | 1016 | -------------------------------------------------------------------------------- /13-product/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Web3 产品经理面试题 2 | 3 | ## 第一部分:钱包与 Web3 数据平台 4 | 5 | #### 第一题:钱包的分类,根据私钥的管理方式进行分类 6 | - 交易所钱包: 7 | - 私钥管理管理:KMS,TEE,CloadHSM, Wallet.data 数据库文件;安全等级 CloadHSM > TEE > KMS > 数据库文件 8 | - 去中心化钱包(HD 钱包) 9 | - 私钥管理方式:本地设备里面 10 | - 硬件钱包(HD 钱包) 11 | - 私钥管理方式:硬件设备,离线 12 | - 智能合约钱包 13 | - EOA 地址,社交恢复钱包 14 | - 多签钱包 15 | - 多把密钥控制一个钱包 16 | - MPC 托管钱包 17 | - 密钥多方计算产生,GG18 经过 5 轮算法,GG20 是 5 轮算法阐述;密钥分片的形式,节点之间相互不知道具体的密钥分片 18 | 19 | #### 第二题:描述交易所钱包的功能模块 20 | - 地址分配:钱包事先生成一批地址,用户在业务注册平台,钱包分配地址给用户。 21 | - 充值:用户转钱到用户在交易所的地址,钱包扫块,风控也同时扫块;交易扫到之后通知业务层,业务层去风控校验充值信息,等到过了确认位之后再次通知业务层,业务层给用户入账 22 | - 提现:用户在交易所发起提现,业务层将交易发送到钱包和风控,钱包拿到交易之后,请求签名机钱包,在请求签名签名是要进行风控校验,校验通过之后签名交易,把交易签名返回来,钱包端构建完整的交易发送到区块链网络,交易上链之后,扫链程序扫到这笔交易之后通知业务层,业务层进行风控校验之后通知钱包提现成功 23 | - 归集:用户充值金额大于一定金额,进行归集,转到归集地址,归集地址发起交易发送到区块链网络,扫链扫到交易就算成功,逻辑类似提现; 24 | - 热转冷:热钱包金额大于一定金额,进行热转冷,流程和提现类似; 25 | - 冷转热:热钱包金额少于一定金额,需要冷钱包多签管理人员从冷钱划钱到热钱包,这里也行扫链维护交易,方便对账。 26 | - 回滚:因为链本身发生区块链重组或者区块链回滚,需要把区块回滚的交易进行处理 27 | - 对账:财务部,交易所上层业务部门,钱包部门,三个部门之前资产负债表,资产负债快照,每日交易日报,结算日报,周,月,半年度,年度。 28 | - 风险控制: 29 | - 底层数据处理流程 30 | - 交易抓取数据 31 | - 地址标签库 32 | - 数据清洗 33 | - 业务数据 34 | - 交易链路风控 35 | - 地址黑,灰,白检查 36 | - 对账风控 37 | 38 | #### 第三题:扫到交易的时候怎么去判定交易是充值,提现 39 | - 默认大家都知道,忽略 40 | 41 | #### 第四题:HD 钱包的功能 42 | - 地址生成 43 | - 收款 44 | - 转账 45 | - 闪兑 46 | - 发现(Dapp浏览器) 47 | - 行情 48 | - 资讯 49 | - 质押 50 | - NFT 51 | - 跨链 52 | - 法币出入金 53 | - 集成信用卡 54 | - 我的资产 55 | 56 | #### 第五题:地址生成的全流程 57 | 58 | - 地址生成:助记词-->MasterKey->n childKey->publicKey->address-> 信息和私钥和助记词编码加密入库 59 | - 导入助记词:导助记词-->MasterKey->n childKey->publicKey->address-> 信息和私钥和助记词编码加密入库 60 | - 导出助记词:助记词 code-解密解码>展示给用户 61 | - 导出私钥:获取私钥->解密>展示给用户 62 | - 导入私钥: 私钥->公钥->地址->信息和私钥加密入库 63 | 64 | #### 第六题:收款业务流程 65 | - 获取地址-->展示地址和二维码 66 | 67 | #### 第七题:转账业务流程 68 | - 用户输入转账信息(含收款地址和金额)-->选择 gas 的方式-->本地交易离线签名与构建 --> 广播交易 69 | 70 | #### 第八题:闪兑怎么做 71 | - 1inch 72 | - API VIP 账号开通流程 73 | 74 | #### 第九题:Dapp 接入流程 75 | 76 | - 钱包和 Dapp 通信模式 77 | - JS 桥接(Windows) 78 | - Hook 79 | - Websocker 80 | - 接入流程 81 | - Dapp url, 从后台请求 82 | - Js 桥接 83 | - 授权,Dapp 操作钱包资金 84 | 85 | 86 | #### 第十题:行情接入流程 87 | 88 | ``` 89 | okex------------| 90 | binance---------| 91 | bybit-----------|-----------| 92 | xxxdex----------| | 93 | |----- Price Aggragator----Api-->HD 钱包 94 | uniswap---------| | 95 | pancakeswap-----|-----------| 96 | xxxdex----------| 97 | ``` 98 | 99 | #### 第十题:质押接入流程 100 | 101 | - 链的质押特点 102 | 103 | - ETH 质押: 请看 The Web3 课程文档 104 | 105 | - SOL 质押: 106 | - 第一步:创建质押账户 107 | - 第二步:将质押的资金转入质押账户 108 | - 第三步:将质押的权限委托给验证者(P2P) 109 | 110 | - 第四步:解除质押: 111 | - 将质押账户编程 inactive 112 | - 等质押周期结束 113 | - 从质押账户里面取回质押,废弃账户 114 | 115 | - ADA 质押: 116 | - 第一步:把要质押的资金转到地址里面 117 | - 第二步:将质押的权限委托给验证者(P2P) 118 | - 第三步:操作取回质押等质押周期结束之后取回资金 119 | 120 | - XTZ 质押: 121 | - 第一步:创建质押账户转入资金质押 122 | - 第二步:取回质押,等到质押周期结束,提回资金 123 | 124 | - 质押接入 125 | 126 | #### 第十一题 ERC721 和 ERC115 的区别(NFT) 127 | 128 | 129 | - ERC721:不可分割 130 | - ERC115:NFT 可以以交易包的形式交易,可以分割 131 | 132 | 133 | #### 第十二题:跨链 134 | 135 | - OP 跨链桥底层逻辑 136 | 137 | - 请看 The Web3 课程文档 138 | 139 | - Polygon 跨链底层逻辑 140 | 141 | - 请看 The Web3 课程文档 142 | 143 | - DappLink 跨链桥底层逻辑 144 | 145 | - 请看 The Web3 课程文档 146 | 147 | ## 第二部分:以太坊生态产品 148 | 149 | 150 | 151 | ## 第三部分:Bitcoin 生态产品 152 | 153 | 154 | 155 | ## 第四部分:Cosmos 生态产品 156 | 157 | 158 | 159 | ## 第五部分:中心化交易所和量化 160 | 161 | 162 | 163 | 164 | ## 第六部分:Defi 产品 165 | 166 | 167 | 168 | ## 第七部分:代币经济学,DAO 和社区 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /14-database/NoSql.md: -------------------------------------------------------------------------------- 1 | # 非关系数据库面试题目 2 | -------------------------------------------------------------------------------- /14-database/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Mysql 和 Postgres 数据库面试题目 2 | 3 | 4 | 5 | ## 第一题:MySQL关键字执行顺序? 6 | 7 | 1. FROM子句:确定数据来源,包括JOIN的表。 8 | 2. ON:执行JOIN条件。 9 | 3. JOIN:如果有JOIN则进行JOIN操作。 10 | 4. WHERE子句:执行过滤条件。 11 | 5. GROUP BY子句:根据指定的列分组结果集合。 12 | 6. HAVING子句:执行分组过滤条件。 13 | 7. SELECT子句:选取指定的列。 14 | 8. DISTINCT子句:去除重复数据。 15 | 9. ORDER BY子句:最后对结果进行排序。 16 | 10. LIMIT子句:限制结果集的数量。 17 | 18 | 19 | ## 第二题:drop、delete与truncate分别在什么场景之下使用? 20 | 21 | drop table和truncate table相同: 22 | 23 | - 属于DDL 24 | - 不可回滚 25 | - 不可带where 26 | - 删除速度快 27 | 28 | 区别: 29 | 30 | + drop删除表内容和结构删除, 31 | 32 | delete from 33 | 34 | - 属于DML 35 | - 可回滚 36 | - 可带where 37 | - 表结构在,表内容要看where执行的情况 38 | - 删除速度慢,需要逐行删除 39 | 40 | 使用场景: 41 | 42 | - 不再需要一张表的时候,用drop 43 | - 想删除部分数据行时候,用delete,并且带上where子句 44 | - 保留表而删除所有数据的时候用truncate 45 | 46 | 47 | 48 | ## 第三题:InnoDB与MyISAM的区别是什么? 49 | 50 | - InnoDB支持事务,MyISAM不支持; 51 | 52 | - InnoDB支持外键,MyISAM不支持; 53 | 54 | - 而InnoDB支持行锁,MyISAM只支持表锁; 55 | 56 | - InnoDB聚集索引, 57 | 58 | - InnoDB中不保存表的行数,如select count(*) from table时,InnoDB需要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count(*)语句包含where条件时MyISAM也需要扫描整个表 59 | 60 | 61 | ## 第四题:MySQL索引类型? 62 | 63 | + **B-Tree 索引** 64 | 65 | B-Tree(平衡树)索引是 MySQL 中最常用的索引类型。它适用于全键值、键值范围和键前缀查找。B-Tree 索引适用于等值查询、范围查询和排序操作。 66 | 67 | ```sql 68 | CREATE INDEX idx_name ON table_name(column_name); 69 | ``` 70 | 71 | + **哈希索引(Hash Index)** 72 | 73 | 哈希索引基于哈希表实现,只适用于等值查询。哈希索引的查找速度非常快,但不适用于范围查询和排序操作。哈希索引通常由存储引擎自动创建和管理,例如 InnoDB 的内存表(Memory 存储引擎)支持哈希索引。 74 | 75 | ```sql 76 | CREATE TABLE table_name ( 77 | id INT PRIMARY KEY, 78 | column_name VARCHAR(255) 79 | ) ENGINE=MEMORY; 80 | ``` 81 | 82 | + **全文索引(Full-Text Index)** 83 | 84 | 全文索引用于全文搜索,适用于文本内容的搜索。全文索引可以对文本内容进行分词和匹配,支持自然语言搜索和布尔搜索。 85 | 86 | ```sql 87 | CREATE FULLTEXT INDEX idx_fulltext ON table_name(column_name); 88 | ``` 89 | 90 | + **空间索引(Spatial Index)** 91 | 92 | 空间索引用于地理空间数据的存储和查询。空间索引支持几何数据的存储和空间操作(例如距离计算、范围查询等)。 93 | 94 | ```sql 95 | CREATE SPATIAL INDEX idx_spatial ON table_name(column_name); 96 | ``` 97 | 98 | + **R-Tree 索引** 99 | 100 | R-Tree 索引是一种用于多维数据的空间索引结构,适用于范围查询和空间操作。R-Tree 索引通常用于地理信息系统(GIS)和空间数据库。 101 | 102 | ```sql 103 | CREATE TABLE table_name ( 104 | id INT PRIMARY KEY, 105 | geom GEOMETRY NOT NULL, 106 | SPATIAL INDEX(geom) 107 | ) ENGINE=InnoDB; 108 | ``` 109 | 110 | + **前缀索引** 111 | 112 | 只在所选列的值的前面几个字符上创建索引。可以减少索引所占用的磁盘空间,但可能会降低查询效率。 113 | 114 | ```sql 115 | alter table user add index index2(email(6)) 116 | ``` 117 | 118 | + **组合索引(Composite Index)** 119 | 120 | ​ 组合索引是指在多个列上创建的索引。组合索引可以提高多列查询的性能,但需要注意索引列的顺序。 121 | 122 | ```sql 123 | CREATE INDEX idx_composite ON table_name(column1, column2); 124 | ``` 125 | 126 | + **唯一索引(Unique Index)** 127 | 128 | 唯一索引确保索引列的值是唯一的。唯一索引可以防止重复值的插入,并且可以提高查询性能。 129 | 130 | ```sql 131 | CREATE UNIQUE INDEX idx_unique ON table_name(column_name); 132 | ``` 133 | 134 | + **主键索引(Primary Key Index)** 135 | 136 | 主键索引是一种特殊的唯一索引,用于唯一标识表中的每一行记录。主键索引通常是自增的整数列。 137 | 138 | ```sql 139 | CREATE TABLE table_name ( 140 | id INT PRIMARY KEY, 141 | column_name VARCHAR(255) 142 | ); 143 | ``` 144 | 145 | + **外键索引(Foreign Key Index)** 146 | 147 | 外键索引用于建立两个表之间的关系。外键索引确保引用表中的外键值存在于被引用表的主键或唯一键中。 148 | 149 | ```sql 150 | CREATE TABLE parent_table ( 151 | id INT PRIMARY KEY 152 | ); 153 | 154 | CREATE TABLE child_table ( 155 | id INT PRIMARY KEY, 156 | parent_id INT, 157 | FOREIGN KEY (parent_id) REFERENCES parent_table(id) 158 | ); 159 | ``` 160 | 161 | 162 | 163 | ## 第五题:InnoDB索引为什么使用B+树? 164 | 165 | 166 | 167 | ## 第六题:聚簇索引和非聚簇索引的区别? 168 | 169 | | 特性 | 聚簇索引 | 非聚簇索引 | 170 | | ---------------- | ------------------------ | ---------------------------- | 171 | | 物理存储顺序 | 决定数据行的物理存储顺序 | 与数据行分开存储 | 172 | | 唯一性 | 每个表只能有一个聚簇索引 | 每个表可以有多个非聚簇索引 | 173 | | 查询性能 | 范围查询和排序操作性能好 | 点查询性能好 | 174 | | 插入和更新性能 | 可能会影响插入和更新性能 | 通常比聚簇索引好 | 175 | | 叶子节点存储内容 | 存储实际的数据行 | 存储索引键和指向数据行的指针 | 176 | 177 | 聚簇索引决定了表中数据的物理存储顺序,每个表只能有一个聚簇索引,适用于范围查询和排序操作。非聚簇索引与数据行分开存储,每个表可以有多个非聚簇索引,适用于点查询。 178 | 179 | 180 | 181 | ## 第七题:MySQL中是否必然包含聚簇索引吗? 182 | 183 | 当使用InnoDB作为引擎时,如果定义了主键,那么InnoDB会使用主键作为聚簇索引,如果没有定义主键,那么会使用第一个非空的唯一索引(NOT NULL and UNIQUE INDEX)作为聚簇索引,如果既没有主键,也没有合适的非空索引,那么InnoDB会自动生成一个6字节隐藏列 _rowid作为聚簇索引。 184 | 185 | 186 | 187 | ## 第八题:覆盖索引和回表查询是什么? 188 | 189 | 190 | 191 | 192 | 193 | ## 第九题:mysql索引失效的情况? 194 | 195 | - **使用函数或表达式** 196 | 197 | 如果在查询中对索引列使用了函数或表达式,MySQL 可能无法使用该列上的索引。 198 | 199 | ```sql 200 | SELECT * FROM users WHERE YEAR(birthdate) = 1990; 201 | ``` 202 | 203 | - **隐式类型转换** 204 | 205 | 如果查询中的列类型与索引列类型不匹配,MySQL 可能会进行隐式类型转换,导致索引失效。 206 | 207 | ```sql 208 | SELECT * FROM users WHERE age = '30'; 209 | ``` 210 | 211 | 假设 `age` 列是整数类型,而查询中使用了字符串 `'30'`,MySQL 会进行隐式类型转换,导致索引失效。 212 | 213 | - **组合索引未遵循最左匹配原则** 214 | 215 | 216 | 217 | - **`LIKE`查询使用左通配符** 218 | 219 | 如果在 `LIKE` 查询中使用了左通配符(例如`_abc`, `%abc`),MySQL 可能无法使用索引。 220 | 221 | ```sql 222 | SELECT * FROM users WHERE name LIKE '%john%'; 223 | ``` 224 | 225 | 在这个查询中,`name` 列上的索引可能无法使用,因为查询使用了前置通配符 `%`。 226 | 227 | - **使用 `OR` 条件** 228 | 229 | 如果查询中使用了 `OR` 条件,并且 `OR` 条件中的列没有全部被索引覆盖,MySQL 可能无法使用索引。 230 | 231 | ```sql 232 | SELECT * FROM users WHERE age = 30 OR email = 'john@example.com'; 233 | ``` 234 | 235 | 假设 `age` 列上有索引,而 `email` 列上没有索引,MySQL 可能无法使用索引。 236 | 237 | - **使用 `!=`、`<>` 或 `NOT IN`** 238 | 239 | 如果查询中使用了 `!=`、`<>` 或 `NOT IN` 条件,MySQL 可能无法使用索引。 240 | 241 | ```sql 242 | SELECT * FROM users WHERE age != 30; 243 | ``` 244 | 245 | 在这个查询中,`age` 列上的索引可能无法使用。 246 | 247 | - **使用 `IS NULL` 或 `IS NOT NULL`** 248 | 249 | 如果查询中使用了 `IS NULL` 或 `IS NOT NULL` 条件,MySQL 可能无法使用索引。 250 | 251 | ```sql 252 | SELECT * FROM users WHERE email IS NULL; 253 | ``` 254 | 255 | 在这个查询中,`email` 列上的索引可能无法使用。 256 | 257 | - **使用 `ORDER BY` 或 `GROUP BY`** 258 | 259 | 如果 `ORDER BY` 或 `GROUP BY` 中的列没有被索引覆盖,MySQL 可能无法使用索引。 260 | 261 | ```sql 262 | SELECT * FROM users ORDER BY last_login; 263 | ``` 264 | 265 | 假设 `last_login` 列上没有索引,MySQL 可能无法使用索引进行排序。 266 | 267 | - **使用 `SELECT *`** 268 | 269 | 使用 `SELECT *` 可能会导致 MySQL 无法有效使用索引,因为返回的数据量过大,可能会导致索引失效。 270 | 271 | 272 | 273 | ## 第十题: 什么是最左匹配原则? 274 | 275 | 最左匹配原则(Leftmost Matching Principle)是指在使用组合索引时,查询条件中包含的列必须从组合索引的最左边开始,并且顺序一致,才能利用该索引。 276 | 277 | 278 | 279 | ## 第十一题:NOT IN和NOT EXIST的区别? 280 | 281 | 282 | 283 | 284 | 285 | ## 第十二题:事务的 ACID 是什么? 286 | 287 | **原子性(Atomicity)**: 事务是一个不可分割的工作单元,要么全部成功,要么全部失败。 288 | 289 | **实现**: MySQL 通过 `COMMIT` 和 `ROLLBACK` 语句来实现原子性。当事务成功完成时,使用 `COMMIT` 提交事务;如果发生错误,使用 `ROLLBACK` 回滚事务。 290 | 291 | **一致性(Consistency)**:假设一个事务将账户 A 的余额转移到账户 B。在事务执行前后,账户 A 和账户 B 的总余额必须保持一致。 292 | 293 | **实现**: MySQL 通过触发器、约束(如主键、外键、唯一约束)和存储过程等机制来确保一致性。 294 | 295 | **隔离性(Isolation)**:事务之间是相互隔离的,一个事务的执行不会影响其他事务的执行。每个事务都感觉不到其他事务的存在。 296 | 297 | 298 | 299 | **实现**: MySQL 提供了 300 | 301 | **持久性(Durability)**: 事务一旦提交,其结果将永久保存在数据库中,即使系统崩溃也不会丢失。 302 | 303 | **实现**: MySQL 通过日志机制(如 `redo log` 和 `undo log`)来确保持久性。`redo log` 记录了事务的操作,`undo log` 记录了事务的回滚信息。在系统崩溃后,MySQL 可以通过日志恢复事务。 304 | 305 | 306 | 307 | **脏读(Dirty Read)**: 308 | 一个事务读取到另一个尚未提交事务的修改。 309 | 310 | **不可重复读(Non-repeatable Read)**: 311 | 在同一个事务内,多次读取同一数据返回的结果有所不同。 312 | 313 | **幻读(Phantom Read)**: 314 | 一个事务在执行两次相同的查询时,因为另一个并发事务的插入或删除操作,导致两次查询返回的结果集不同。 315 | 316 | 317 | ## 第十三题:MySQL的隔离级别有哪些? 318 | 319 | 四种隔离级别(`READ UNCOMMITTED`、`READ COMMITTED`、`REPEATABLE READ`、`SERIALIZABLE`)来控制事务的隔离性。默认隔离级别是 `REPEATABLE READ`。 320 | 321 | - **READ UNCOMMITTED**: 允许脏读,即一个事务可以读取另一个事务未提交的数据。 322 | - **READ COMMITTED**: 不允许脏读,但允许不可重复读,即一个事务可以读取另一个事务已提交的数据。 323 | - **REPEATABLE READ**: 不允许脏读和不可重复读,但允许幻读,即一个事务可以读取到另一个事务插入的新数据。 324 | - **SERIALIZABLE**: 最高隔离级别,不允许脏读、不可重复读和幻读,所有事务按顺序执行。 325 | 326 | 327 | 328 | ## 第十四题:MySQL百万级数据分页要注意什么? 329 | 330 | 331 | 332 | ## 第十五题:简述MVCC机制 333 | 334 | 335 | 336 | ## 第十六题:间隙锁是什么 337 | 338 | 339 | 340 | ## 第十七题:MySQL底层架构体系及每层作用? 341 | 342 | 343 | 344 | 345 | ## 第十八题:什么是红黑树? 346 | 347 | 348 | 349 | ## 第十九题:什么是 B 树? 350 | 351 | 352 | 353 | ## 第二十题:什么是 B+ 树? 354 | 355 | 356 | 357 | ## 第二十一题:MySQL索引底层实现为什么要用 B+ 树结构? 358 | 359 | 360 | 361 | 362 | ## 第二十二题: 363 | 364 | 365 | 366 | ## 第二十三题: 367 | 368 | 369 | 370 | ## 第二十四题: 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | -------------------------------------------------------------------------------- /14-database/Redis.md: -------------------------------------------------------------------------------- 1 | # Redis 面试题目 2 | -------------------------------------------------------------------------------- /15-java/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Java 面试题目 2 | -------------------------------------------------------------------------------- /16-bitcoin/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Bitcoin 面试题目 2 | 3 | ## 第一题: 简述 UTXO 模型 4 | 5 | 6 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 7 | 8 | ## 第二题:比特钱包有哪些地址格式,请简要说明 9 | 10 | 11 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 12 | 13 | ## 第三题: Pay-to-Taproot介绍与优势 14 | 15 | Pay-to-Taproot(P2TR)是一种ScriptPubKey,它将比特币锁定在一个脚本上,允许用户向 Schnorr 公钥或各种其他脚本的 Merkle 根支付。表面上看,一个P2TR输出将比特币锁定在一个施诺尔公钥上,我们假设为Q。然而,这个公钥Q实际上是一个公钥P和一个公钥M的总和,M是由其他ScriptPubKeys列表的Merkle根计算出来的。P2TR 输出中的比特币可以通过发布公钥 P 的签名或满足 Merkle 树中包含的脚本之一来花费,前者称为密钥路径,后者则是脚本路径。虽然P2TR的输出可能有许多种方式,但只有被使用的那一种会被公开,这样可以为其他未使用的替代方案保持隐私。此外,由于Schnorr密钥聚合特性,公钥P本身可以是一个聚合密钥,公钥P作为一个聚合密钥或单一密钥的状态永远不会被透露,因为所有的P2TR输出都是彼此相似的,这样将破坏许多链分析启发式方法,增强用户的隐私。 16 | 17 | ### 1.其他支付方式 18 | 19 | - P2PKH:Pay-to-Public-Key-Hash(P2PKH)是一种ScriptPubKey,它将比特币锁定在一个公钥的哈希(比特币地址)上。例如,Alice想在P2PKH交易中向Bob发送1个BTC,Bob向Alice提供他钱包中的一个地址,然后Bob的地址会被包括在交易中。当Bob试图花费他收到的比特币时,他必须用对应于公钥的私钥来签署交易,公钥的哈希值与Alice交易中提供的哈希值一致。 20 | 21 | - P2WPKH:Pay-to-Witness-Public-Key-Hash (P2WPKH) 是一种 ScriptPubKey,用于将比特币锁定到 SegWit 地址。 P2WPKH 交易在大多数方面类似于 P2PKH 交易;它仍然将比特币锁定到公钥的哈希值。主要区别在于 P2WPKH 使用 SegWit。这意味着所有输入的 ScriptSig(解锁比特币的脚本)被移出交易主体并进入见证部分,并称为脚本见证。这些数据仍然记录在区块链上,但数据产生的费用会低于常规数据,使得 SegWit 交易比常规交易便宜。 22 | 23 | - P2SH:Pay-to-Script-Hash (P2SH) 是一种 ScriptPubKey, 主要用于多重签名钱包,制作输出脚本逻辑,在接受交易之前检查多重签名。例如,如果 Alice 在 P2SH 交易中向 Bob 发送 1 BTC,她会将花费比特币所需脚本的哈希值包含在交易中。此脚本可能需要 Bob 的私钥和/或许多其他人的签名。当 Bob 想要花费他从 Alice 那里收到的比特币时,他会重建 Alice 用来发送比特币的脚本哈希,并使用脚本所需的任何私钥对交易进行签名。P2SH 非常灵活,因为它允许用户构建任意脚本。此外,交易的发送者不需要知道他们发送到什么脚本类型。在上面的示例中,Bob 可以线下构建他想要的脚本,并且只向 Alice 发送该脚本的哈希值,从而为 Bob 保留更多隐私。 24 | 25 | P2WSH:Pay-to-Witness-Script-Hash (P2WSH) 是一种在大多数方面类似于 P2SH 交易的交易类型,除了它使用 SegWit。与 P2SH 交易一样,P2WSH 交易将比特币锁定到脚本的哈希值。为了花费这个比特币,花费者必须出示称为 RedeemScript 的脚本和任何必需的签名。在技术层面上,P2WSH 实际上描述了用于将比特币锁定到 SegWit 脚本哈希的 ScriptPubKey。 26 | 27 | ### 2.P2TR优点 28 | 29 | 通过比较不同类型的签名大小,可以看出在单一签名上使用P2TR是要比同等的P2WPKH要大一点的,但仔细观察会发现,对单一签名的钱包用户和整个网络来说,使用P2TR有很多好处: 30 | 31 | [![DappLink](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/images/2.png)](https://www.dapplink.xyz/zh) 32 | 33 | 34 | - 花费更便宜:在投入层面上,花费一个单一签名的P2TR UTXO比花费一个P2WPKH UTXO要少15%左右。像上表这样过于简单的分析隐藏了一个细节,即花费者不能选择他们被要求支付的地址,所以如果你留在P2WPKH上,而其他人都升级到P2TR,你的2进2出交易的实际典型大小将是232.5vbytes,而所有P2TR交易仍然只有211.5vbytes。 35 | 36 | - 隐私性:虽然早期采用者改用新的脚本格式时,会失去一些隐私,但改用taproot的用户也会立即得到隐私性的提升。你的交易将能够看起来与从事新的LN通道、更有效的DLCs、安全的多重签名、各种巧妙的钱包备份恢复方案或其他一百种开创性发展的人没有区别。现在使用P2TR进行单签名,也允许你的钱包在以后升级到多签名、tapscripts、LN支持或其他功能,而不影响你现有用户的隐私。无论是旧版本还是新版本的软件收到 UTXO 都没有关系——两个 UTXO 在链上看起来都是一样的。 37 | 38 | - 对硬件签名设备来说更方便:自重新发现费用超额支付攻击以来,一些硬件签署设备拒绝签署交易,除非该交易中花费的每个UTXO都有元数据,其中包含产生该UTXO的整个交易的重要部分的副本。而Taproot 消除了费用超额支付攻击的潜在漏洞,因此可以显着提高硬件签名者的性能。 39 | 40 | - 更多的可预测性:P2PKH和P2WPKH UTXO的ECDSA签名可以有不同的大小,由于钱包需要在创建签名之前选择交易的费率,大多数钱包只是假设最坏情况下的签名大小,因此接受较小的签名时将略微多付一些费用。而对于P2TR,签名的大小是事先知道的,允许钱包可以选择一个精确的feerate。 41 | 42 | - 帮助完整的节点:比特币系统的整体安全性取决于大部分比特币用户使用自己的节点验证每笔确认的交易,也包括验证您钱包创建的交易。Taproot的schnorr签名可以有效地进行批量验证,在同步区块的过程中,节点验证签名时需要消耗的CPU周期减少了约1/2。就算你拒绝了上述的所有优点,也要考虑一下使用taproot去帮助运行完整节点的人。 43 | 支持P2TR 44 | 45 | 对于已经支持接收和花费v0 segwit P2WPKH输出的钱包来说,升级到v1 segwit P2TR进行单一签名应该很容易,以下是主要的步骤: 46 | 47 | - 使用新的BIP32密钥推导路径:强烈建议为P2TR公钥使用一个新的推导路径(例如由BIP86定义的),如果你在ECDSA和schnorr签名中使用相同的密钥,可能会被攻击。 48 | - 通过哈希值来调整你的公钥:虽然技术上不需要单签名,特别是当你的所有密钥都来自随机选择的BIP32种子时,BIP341建议将你的密钥提交到一个不可消耗的scripthash树。这就像使用椭圆曲线加法运算一样简单,将你的公钥与该密钥的哈希值的曲线点相加。遵守这个建议的好处就是如果你以后要增加对无脚本多签名的支持,或者增加对tr()描述符的支持,你将能够使用同样的代码。 49 | - 创建你的地址并对其进行监控:使用bech32m来创建你的地址。支付将被发送到scriptPubKey OP_1 。你可以使用用于扫描 v0 隔离见证地址(如 P2WPKH)的任何方法来扫描支付脚本的交易。 50 | - 创建一个支出交易:taproot的所有非见证字段都和P2WPKH的一样,所以你不需要担心交易序列化的变化。 51 | - 创建一个签名信息:这是对支出交易的数据的承诺。大部分数据与你为P2WPKH交易所签署的数据相同,但字段的顺序被改变,还有一些额外的东西被签署。实现这一点只是一个序列化和散列各种数据的问题,所以编写代码应该很容易。 52 | - 签署签名信息的哈希值:现在已经有许多不同的方法用来创建Schnorr签名了。因此当前最好的方法不是 "推出你自己的方法",而是使用你信任的、经过严格审查的库中的功能。但是,如果你由于某种原因不能这样做,BIP340提供了一种算法,如果你已经有了制作ECDSA签名的基础,那么这种算法应该很容易实现。当你有了你的签名,把它放在输入的见证数据中,然后发送你的支出交易。 53 | 54 | ### 3.总结 55 | ​ Pay-to-Taproot (P2TR)输出是版本为 1 的SegWit 输出,以后所有的 Taproot 交易都是 SegWit 交易,因此对于开发者,优先尝试一下P2TR是必要的。在taproot在709,632区块激活之前,你就可以使用testnet、公共默认标志或Bitcoin Core的私有regtest模式测试你的代码。 56 | 57 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 58 | 59 | 60 | ## 第四题:简述比特币的 POW 共识算法 61 | 62 | 63 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 64 | 65 | 66 | ## 第五题:比特币有哪些分叉链,产生的原因是什么 67 | 68 | 69 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 70 | 71 | ## 第六题:简述 BRC20 的原理 72 | 73 | 74 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 75 | 76 | ## 第七题:请说明一下什么是 RGB 协议 77 | 78 | 79 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 80 | 81 | ## 第八题:举例说明比特币脚本编程的过程 82 | 83 | 84 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 85 | 86 | ## 第九题:Taproot 是如何进行隐私保护的 87 | 88 | 89 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 90 | 91 | #### 比特币网络当前的隐私问题 92 | 93 | 在比特币网络中,个人的交易记录或是商家的现金流可以被任何人追踪到。进一步来说,对于比特币网络常见的多重签名交易类型,用于合作完成一笔交易,但是在现行的比特网络中,当交易输出满足执行解锁条件时,它就会显示整个脚本以及它所包含的所有数据,网络参与者可以通过使用脚本的初始哈希值,在区块链上轻松审计。这些问题的出现是因为比特币的设计本身没有考虑到这一层次的隐私保护,而隐私保护是区块链现在亟待解决的问题。 94 | 95 | #### Schnorr聚合签名保护多方的参与 96 | 97 | ##### 作为比特币网络的签名算法 98 | 99 | ECDSA的全称是椭圆曲线数字签名算法。它在比特币中的作用我们的并不陌生,我们在比特币网络中的每一次签名都用到了 ECDSA 技术。比如说,用户A想发送一笔交易将比特币转账给用户B,必须要让矿工确认只有用户A拥有该UTXO的私钥,即拥有这笔资产的处置权。因此用户A需要用 ECDSA 生成一个独一无二且无法被修改的数字签名,以证明其拥有私钥,同时确认交易部分的具体金额。自比特币诞生以来,该算法很好地胜任了这份生成签名的关键性工作,维持了比特币网络的安全,但是ECDSA缺乏正式的安全证明,并依赖于额外的的假设。Schnorr和ECDSA同样基于离散对数问题,但是Schnorr签名算法有一个正式的数学证明能够证明其安全性,且用到了更少的假设,因此,从安全性角度考虑,Schnorr签名算法接替ECDSA算法也就不足为奇了。 100 | 101 | ##### 支持聚合签名,实现隐私保护 102 | 103 | ![the-web3](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/images/3.png) 104 | 105 | 此外,Schnorr最大的优点在于实现了聚合签名,因为Schnorr具备线性特征,这就允许多个用户的公钥 通过线性计算聚合成一个公钥,并能生成与之对应的聚合签名。聚合意味着合并为一个,因此味着当多方参与时,Schnorr聚合签名可以将多个签名数据压缩合并成单个聚合签名了,验证者通过所有签名相关的数据和公钥组成的列表对单个聚合签名进行验证,若验证通过,其效果等同于对所有相关签名进行独立验证且全部通过。 106 | 107 | 以2-3多签为例,⽬前⽐特币多签的锁定脚本需要3个公钥地址,这部分会被压缩为脚本,所以升级之后⼤⼩⽆变化,但是解锁脚本需要2个公钥与2个签名,在升级为Schnorr之后,只需要⼀个"公钥和"与"签名和"。对于更通⽤的n-m多签,⽬前⽐特币多签的解锁脚本需要n个公钥与n个签名,Schnorr签名依然只需要⼀个"公钥和"与⼀个"签名和”。也就是说签名⼈越多,Schnorr签名的空间利⽤率越⾼。 108 | 109 | 将多个参与者的签名聚合成一个签名,这使得多签名交易看起来像是常规的P2PKH交易,保护了多重签名参与者的隐私。对比之下,ECDSA 本身是不支持多重签名,比特币现在是通过 P2SH 脚本来处理,但是 P2SH 类的脚本会向网络暴露多签交易的存在并披露所有的签名者。因此,使用 Schnorr 签名,增强交易的隐私性,并节省解锁脚本内因多签带来的空间占用,节约宝贵的链上空间,实现变相扩容。 110 | 111 | ​ 112 | #### MAST对交易脚本的隐私保护 113 | 114 | ![MAST](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/images/4.png) 115 | 116 | MAST(Merkle Abstract Syntax Tree)由抽象语法树和Merkle树发展而来,AST背后的技术允许我们将一个脚本分割成互斥的子集,而merkle树允许我们在不披露整个脚本的情况下验证单独的脚本子集属于一个完整的脚本。 117 | 118 | MAST使用Merkle树对脚本的互斥分支进行编码,这使复杂脚本条件可以通过隐藏未执行的分支脚本来提高隐私。具体表现为,用户支出时,只需披露相关脚本以及从该脚本通向默克树根的路径,用一个默克尔证明就能为执行的脚本提供证明。 119 | 120 | ![op_code](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/images/5.png) 121 | 122 | 以上面Alice的财产处理脚本为例子,上面指定的脚本不仅包括Alice的公钥(需要验证私钥的签名),还包括Bob和Charlie的公钥和一些条件逻辑,如超时。在当前的BTC网络中,上述的所有数据和脚本被花费都都会记录在链上,因此每个人都可以跟踪到这笔UTXO的所有信息,这对于Alice、Bob、Charlie的隐私来说是不好的消息。 123 | 124 | 引入MAST后,可以看到MAST树对该脚本的表示如下。 125 | 126 | ![mast-eg](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/images/6.png) 127 | 128 | 将其分解为两个子脚本,一个脚本是Alice能够随时花费这笔比特币,另一个脚本是三个月后Alice的比特币还没有使用完,Bob和Charlie就可以考虑如何处理这笔比特币了。在实际情况中,只会选择一个分支进行执行,而通过披露的单个分支,无法确定Alice其他的子脚本有做什么动作,从而保护了Alice的隐私。 129 | 130 | #### Taproot升级下的用户的隐私 131 | 132 | Taproot本身是一次软分叉升级,在比特币客户端升级后还会兼容老的客户端,这对于比特币本身的发展和社区的凝聚都是强有力的。Taproot通过隐藏交易的全部脚本,同时使复杂交易与其他交易难以区分,增强了比特币网络参与者的隐私性。对不使用复杂交易脚本的普通用户来说,这次Taproot升级是接近无感知的;但对开发者来说,钱包和服务必须升级对应的功能。随着越来越多的用户利用 Taproot 的功能,其对效率和隐私的积极影响被放大。 133 | 134 | 总言之,相较于现行的比特币网络,区块链分析能够对单个地址进行追踪,taproot为比特币网络纳入了一定的隐私功能,用户的隐私得到保护,也会惠及比特币本身,促进比特币网络的发展,为比特币的未来带来更多可能性。 135 | 136 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 137 | 138 | 139 | ## 第十题:简述 Schnorr盲签名 140 | 141 | #### Schnorr盲签名方案 142 | 143 | 顾名思义,Schnorr 盲签名是签名者不知道他们签署了什么的签名。 可能很难想象这有什么用处,但在处理`不经意服务器(Oblivious Server)`时,它是一个很好的工具,而这在比特币和整个互联网的未来发挥着重要作用。 144 | 145 | `不经意服务器`指的是一些支持可信计算或数据存储的服务器,用户可能会对其服务进行付费,但服务器却不需要知道它操作的所有信息,这类信息包括用户的身份、哪些方正在相互交互、正在存储哪些数据、正在执行的什么计算等等内容,因为这个过程是不经意的。一个不经意的服务器应该是一个可靠的计算服务,除了计算什么都不做。 146 | 147 | 将这个场景迁移到比特币网络中,如果采用盲签名,节点只负责运算,而无法知晓正在计算的内容是什么,最终节点打包的区块并不会透露用户的任何隐私,这为用户的匿名性提供了极大保护。用户只需为节点(矿工)支付交易的手续费,就能享受比特币网络提供的隐私性和安全性。 148 | 149 | Schnorr盲签名方案有四个步骤: 150 | 151 | + 签名者提供一个随机数 152 | 153 | + 接收者提出一个使用随机数构造的盲挑战(它不显示有关签名内容的信息) 154 | 155 | + 签名者使用步骤 1 中的随机数提供此挑战的正常 schnorr 签名 156 | 157 | + 接收者解开这个签名的盲部分,从而产生一个正常的、有效的 Schnorr 签名,它看起来与签名者提供的签名完全无关,因为 R 和 s 值都由随机数调整。 158 | 159 | #### Schnorr盲签名是如何工作的 160 | 161 | 在创建盲签名的过程中,具有公钥 `X` 的签名者和接收者之间的通信类似于Schnorr 的签名协议: 162 | 163 | 1. 签名者生成一个随机的 `k` 并将随机数 `R = k*G` 发送给接收者。 164 | 2. 接收者响应一个挑战 `c`。 165 | 3. 签名者回复生成的盲签名 `s = k + c*x`,其中 `x` 是签名者的私钥。 166 | 167 | 在正常的 Schnorr 协议中,接收者旨在选择 c 作为他们想要签名的某些消息 m 的哈希 `H(X, R, m)`。 但是我们使用盲签名的目标是允许接收者以某种方式调整 R 和 s 以获得签名 `(R', s')`,其中调整值是随机数,因此该签名看起来与原始的 `(R, s)` 完全无关。 168 | 169 | ​ 因此,如果我们想完成这个方案,最直接的做法就是让接收者生成随机数 `α`和`t`,并让 `X' = X + t*G`, `R' = R + α*G`, X'和R'是调整后的公钥和随机数。因此它们生成的挑战将是 `c = H(X', R', m)`,生成的盲签名为 `s = k + H(X',R',m) * x` ,然后需要调整为有效 schnorr 签名。 170 | 171 | ![s'](https://cdn.jsdelivr.net/gh/rjman-ljm/resources@master/assets/1629345358465-1629345358463.png) 172 | 173 | 这样,我们就最终得到了使用密钥 X'签署的消息 m 的有效 Schnorr 签名 `(R',s')`。 174 | 175 | 然而,这个方案有一个致命的缺陷。 如果签名者试图查看它已签署的内容(例如通过查看比特币交易的所有签名)并且遇到使用公钥 X' 签署消息 m得到的签名 (R', s') ,它将无法识别这些值中的任何一个,这看似达到了匿名的目的 ,但它将能够计算 H(X', R', m) 并将该值与它在步骤 2 中收到的挑战进行比较,这破坏了签名者的匿名性。 176 | 177 | 为了解决这个问题,可以生成一个新的随机数`β` ,并用它来调整挑战 `c = H(X', R', m) + β`,调整后的签名就变成了 178 | 179 | ![s''](https://cdn.jsdelivr.net/gh/rjman-ljm/resources@master/assets/1629352879562-1629352879560.png) 180 | 181 | 并且,我们必须对随机数添加额外的调整以保持一致,调整后得到 `R'=R + α∗G + β∗X`。现在得到最终的盲签名 `(R', s')`,其中所有这些值对于签名者来说都是随机的,并且挑战哈希也是随机的。 182 | 183 | #### Schnorr盲签名的安全性 184 | 185 | Schnorr 盲签名方案对于所提出的各种攻击来说,实际上并不安全,只是规定签名者在很小一段时间内中止和重试,才能确保使其安全(但也会导致不太有效的签名方案)。正因为如此,通常只有在需要Schnorr签名时使用 Schnorr 盲签名才是有意义的(例如,如果被签名的事物是未来的比特币交易)。对于盲签名的大多数链下用例,使用其他盲签名方案可能更有意义。 如BIP340提及的,确切地说,Schnorr 签名承诺了一个非常简单的盲签名方案,但它是不安全的,因为它容易受到 Wagner 的攻击。一种已知的缓解措施是让签名者以一定的概率中止签名会话,并且可以在非标准密码假设下证明由此产生的方案是安全的。 186 | 187 | #### 盲CoinSwap服务器 188 | 189 | 盲签名的一个有趣用例是 CoinSwap 服务器,CoinSwap 是一对不可链接的交易,通常有两方以原子方式相互支付几乎相等的金额。使用盲签名,可以有一个盲 CoinSwap 服务,用户可以支付少量费用与服务器执行 CoinSwap,而服务器无法跟踪哪个付款对应哪个收款。 190 | 191 | 就像常规 Schnorr 签名一样,盲签名支持预先提交的随机数方案。具体来说,一旦服务器给出了它的随机数,并且客户端生成了它的调整,客户端就能够计算 `s*G`,其中 s 来自已知消息的盲签名,这可以实现这些签名的原子购买,这是通过使用 `s*G` 作为适配器签名中的适配器点来实现的。该签名将用户的付款签署给服 192 | 193 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | -------------------------------------------------------------------------------- /17-cosmos/ReadMe.md: -------------------------------------------------------------------------------- 1 | # Cosmos 面试题目 2 | -------------------------------------------------------------------------------- /18-cryptography/ReadMe.md: -------------------------------------------------------------------------------- 1 | # 密码学 2 | -------------------------------------------------------------------------------- /19-open/ReadMe.md: -------------------------------------------------------------------------------- 1 | # 开放性面试题目 2 | 3 | 4 | ## 第一题:你在过去的工作过程中遇到过哪些问题,你是怎么发现并解决的 5 | 6 | 7 | ## 第二题:12. 你的项目和其他项目相比有什么亮点,请详细说一下 8 | 9 | 10 | ## 第三题:13. 你最印象深刻的难题是什么?怎么解决的?越详细越好 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 0xchaineye 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 区块链链面试指南 2 | 3 | 4 | 5 | 本教程由 The Web3 社区出品, DappLink 赞助 6 | 7 | 8 | [![DappLink](https://raw.githubusercontent.com/eniac-x-labs/.github/main/profile/dapplink.jpeg)](https://www.dapplink.xyz/zh) 9 | 10 | 11 | 面试了很多区块链工程师,感觉大家都无法抓住面试的要点,本教程主要是整理一些区块链中的面试思路和面试题目 12 | 13 | 14 | ### 教程目录 15 | 16 | #### 1. [计算基础](https://github.com/0xchaineye/chaineye-blockchain-interview/blob/main/01-computer-basics/readme.md) 17 | #### 2. [网络编程](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/01-computer-basics/readme.md) 18 | #### 3. [数据结构与算法](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/02-data-structures-and-algorithms/readme.md) 19 | #### 4. [NodeJs](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/03-nodejs/readme.md) 20 | #### 5. [共识算法](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/04-consensus-algorithm/readme.md) 21 | #### 6. [以太坊](https://github.com/0xchaineye/chaineye-blockchain-interview/blob/main/05-ethereum/readme.md) 22 | #### 7. [solidity](https://github.com/0xchaineye/chaineye-blockchain-interview/blob/main/06-solidity/readme.md) 23 | #### 8. [golang](https://github.com/0xchaineye/chaineye-blockchain-interview/tree/main/07-golang#readme) 24 | #### 9. [rust](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/08-rust/readme.md) 25 | #### 10. [layer2](https://github.com/the-web3/chaineye-blockchain-interview/tree/main/09-layer2) 26 | #### 11. [零知识证明](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/10-zkp/readme.md) 27 | #### 12. [cosmos](https://github.com/the-web3/chaineye-blockchain-interview/tree/main/11-cosmos#readme) 28 | #### 13. [密码学](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/18-cryptography/ReadMe.md) 29 | #### 14. [钱包](https://github.com/the-web3/chaineye-blockchain-interview/blob/main/12-wallet/README.md) 30 | 31 | ### The Web3 社区简介 32 | The Web3 是一个专注 Web3 技术解决方案设计与开发、技术教程设计与开发、Web3 项目投研分析和 Web3 项目孵化,旨在将开发者,创业者,投资者和项目方联系在一起的社区。 33 | 34 | #### The web3 业务范围 35 | 36 | - 技术服务:提供交易所钱包,HD 钱包,硬件钱包,MPC 托管钱包,Dapps, 质押协议,L1,L2 ,L3 公链,数据可用层(DA)和中心化交易所技术开发服务。 37 | - 技术培训:提供个人技术成长和企业技术培训服务 38 | - 开发者活动承接:各种线下线上黑客松和开发者 meetup 活动承接 39 | - 除此之外,我们还和 "磐石安全实验室" 深入合作,开展去中心化安全审计服务 40 | 41 | ### The Web3 社区官方链接 42 | - github: https://github.com/the-web3 43 | - X: https://twitter.com/0xtheweb3cn 44 | - telegram: https://t.me/+pmoh3D4uTAFjNWM1 45 | - discord: https://discord.gg/muhuXRsK 46 | - the web3 官网:https://thewebthree.xyz/ 47 | - the web3 技术服务网站:https://web.thewebthree.xyz/ 48 | 49 | 50 | -------------------------------------------------------------------------------- /images/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/chaineye-blockchain-interview/c1baf63669e6e32295b9914780372e08d2c42561/images/2.png -------------------------------------------------------------------------------- /images/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/chaineye-blockchain-interview/c1baf63669e6e32295b9914780372e08d2c42561/images/3.png -------------------------------------------------------------------------------- /images/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/chaineye-blockchain-interview/c1baf63669e6e32295b9914780372e08d2c42561/images/4.png -------------------------------------------------------------------------------- /images/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/chaineye-blockchain-interview/c1baf63669e6e32295b9914780372e08d2c42561/images/5.png -------------------------------------------------------------------------------- /images/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/chaineye-blockchain-interview/c1baf63669e6e32295b9914780372e08d2c42561/images/6.png -------------------------------------------------------------------------------- /images/pic_chang.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/the-web3/chaineye-blockchain-interview/c1baf63669e6e32295b9914780372e08d2c42561/images/pic_chang.png --------------------------------------------------------------------------------