├── 1.md
├── 10.md
├── 11.md
├── 12.md
├── 13.md
├── 14.md
├── 15.md
├── 2.md
├── 3.md
├── 4.md
├── 5.md
├── 6.md
├── 7.md
├── 8.md
├── 9.md
├── img
├── active.png
├── filecoin-1.png
├── filecoin_arch.png
├── ipfsandfilecoin.png
├── payment.png
├── post.png
├── retrieval_protocol.png
├── sle.png
├── sle2.png
├── storage_protocol.png
└── wikiissue1.png
└── readme.md
/1.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之一:filecoin概念
2 |
3 |
4 | > 作者:杨尉(waynewyang),转载请注明出处
5 |
6 |
7 | ## 目录
8 | - [1 filecoin概念](#filecoin概念)
9 | - [1.1 filecoin的定义](#filecoin的定义)
10 | - [1.2 filecoin的设计目的](#filecoin的设计目的)
11 | - [1.3 filecoin与IPFS的关系](#filecoin与ipfs的关系)
12 | - [1.3.1 filecoin与IPFS属性对比](#filecoin与ipfs属性对比)
13 | - [1.3.2 IPFS的对标对象](#ipfs的对标对象)
14 | - [1.3.3 filecoin的对标对象](#filecoin的对标对象)
15 | - [1.4 filecoin网络中的角色](#filecoin网络中的角色)
16 | - [1.4.1 矿工收益方式类比理解](#矿工收益方式类比理解)
17 | - [1.4.2 存储矿工](#存储矿工)
18 | - [1.4.3 检索矿工](#检索矿工)
19 | - [1.4.4 客户(检索客户和存储客户)选择filecoin的理由](#检索客户和存储客户)
20 |
21 | ## 1 filecoin概念
22 | ### 1.1 filecoin的定义
23 | - Filecoin是一个去中心化的存储网络(**DSN**),是一个**云存储的自由交易市场**,通过Filecoin**项目**来实现其**协议**。矿工通过提供数据存储或检索来获得**token**(也称为“filecoin”)。相反,客户向矿工支付token以存储或分发数据并进行检索。
24 |
25 | - Filecoin的多重含义:
26 | - 网络
27 | - 市场
28 | - 项目
29 | - 协议
30 | - Token
31 |
32 | - [回到目录](#目录)
33 |
34 |
35 | ### 1.2 filecoin的设计目的
36 | - filecoin设计符合**激励相容**,每一个参与者的最有利可图的选择(包括目标客户,矿工,投资者和开发人员)将是采取行动提高网络服务质量,这也是他们的最优策略。
37 |
38 | - 以超高竞争力的价格可靠地存储文件(低成本、高效率)
39 |
40 | - 客户可以调整其存储策略以满足他们的需求,在冗余,检索速度和成本之间创建自定义平衡。全球的Filecoin存储和检索市场使供应商竞争以最优惠的价格为客户提供灵活的选择
41 |
42 | - [回到目录](#目录)
43 |
44 |
45 | ### 1.3 filecoin与IPFS的关系
46 |
47 | #### 1.3.1 filecoin与IPFS属性对比
48 | |类别| IPFS | Filecoin |
49 | |:-------:|:--------|:--------|
50 | |功能 |基于内容寻址的分布式存储基础设施|IPFS网络之上的激励层,提供一个云存储领域的自由交易市场|
51 | |对标对象|HTTP|大型集中式孤岛存储提供商,如国外的aws、国内的aliyun等|
52 | |存储权限|对有所有权的IPFS节点具备存储权限|1 除对有所有权的IPFS节点具备存储权限外
2 还可以通过支付的方式,在其供应商的节点之上具备存储权限|
53 | |读取权限|ALL(只要知道内容cid)|ALL(只要知道内容cid)|
54 | |架构设计|**另行文章补充分析**|原则上需要无缝对接到IPFS
1Filecoin将IPLD用于区块链数据结构
2 Filecoin节点使用libp2p建立彼此的安全连接
3 节点和Filecoin块传播之间的消息传递使用libp2p pubsub|
55 | |使用场景|1 存储自己的节点数据,分享数据等,类似BT
2 基于IPFS或其中部分组件构建企业自己的分布式云存储架构、区块链架构等|1 成为filecoin矿工,提供分布式检索及存储服务
2 成为filecoin客户,支付费用享受filecoin网络的检索及存储服务
3 基于filecoin,开发第三方管理系统|
56 |
57 | - IPFS现在和将来都可以免费下载,运行和使用,并且将独立于Filecoin运行。一旦Filecoin正式网络启动,IPFS节点还可以免费或利润地在Filecoin检索市场上提供其文件的检索。
58 |
59 | - [回到目录](#目录)
60 |
61 |
62 | #### 1.3.2 IPFS的对标对象
63 | |特点| HTTP | IPFS |
64 | |:-------:|:-------:|:-------:|
65 | |**寻址方式** |**位置寻址**
一维寻址,低效、脆弱|**内容寻址**
多维寻址,高效、稳定|
66 | |效率|低效|高效|
67 | |稳定性|脆弱|稳定|
68 | |开放性|封闭、垄断|开放、共享|
69 |
70 | - [回到目录](#目录)
71 |
72 | #### 1.3.3 filecoin的对标对象
73 | |特点| 传统云存储提供商(大型集中式孤岛存储网络) | Filecoin |
74 | |:-------:|:--------|:-------:|
75 | |网络模式 |集中式 |DSN|
76 | |加入门槛 |高,从硬件底层基础设施、一直到软件、服务的提供,小企业很难插足 |低、自由交易市场,Filecoin做好基础设施|
77 | |宏观视野:闲置存储空间 |高|低|
78 | |价格 |昂贵,垄断、可人为保持高水平|便宜,自由竞争市场|
79 | |安全性 |差,破坏隐私
1 云存储上可查看用户隐私
2 甚至许多密码鉴权信息都没有隐私可言
3 单个提供商的故障影响大|强
1 无第三方或者中心机构,文件加密安全得到保障
2 单个云提供商的故障小|
80 | |利益分配群体 |巨头 |All|
81 |
82 | - [回到目录](#目录)
83 |
84 | ### 1.4 filecoin网络中的角色
85 | |角色| 说明 |主要影响因素|
86 | |:-------:|:--------|:--------|
87 | |存储矿工 |存储矿工通过为客户存储数据来获得Filecoin;获得区块奖励和交易费用的概率与矿工对Filecoin网络的存储量成正比 |存储容量|
88 | |检索矿工 |检索矿工的带宽和交易的出价/响应时间(即延迟和与客户的接近度)将决定其在网络上关闭检索交易的能力 |带宽|
89 | |检索客户|支付filecoin获取检索服务||
90 | |存储客户|支付filecoin获取存储服务||
91 |
92 | - [回到目录](#目录)
93 |
94 | ### 1.4.1 矿工收益方式类比理解
95 | - 类比**filecoin为一家股份公司,类比存储矿工为股东(股份出资人)**
96 |
97 | |收益来源| 类比分析 |
98 | |:-------:|:--------|
99 | |提供存储服务|存储矿工收益来自两部分
1 工资(提供存储并收取服务费用)
2 按照出资比例分红(区块奖励就是按照有效存储占比来实现的) |
100 | |提供检索服务|检索矿工是offchain的,不参与挖矿,收益来自
1 工资(提供检索并收取服务费用) |
101 |
102 | - [回到目录](#目录)
103 |
104 | ### 1.4.2 存储矿工
105 | 存储两类数据,**存储整个区块链所需的总存储量将远低于矿工为交易存储的密封数据**。
106 | - 密封客户的存储数据
107 | - blockchain数据的数据的副本
108 |
109 | - [回到目录](#目录)
110 |
111 | ### 1.4.3 检索矿工
112 | - 提供检索的途径
113 | - 可以存储热门数据(非存储矿工),以便更优质提供服务
114 | - 自己同时做存储矿工,或者从存储矿工处获取
115 | - 不限于从filecoin网络获取,可以从免费的IPFS网络获取
116 | - 检索效率的保证
117 | - 检索矿工是不运行在blockchain中的,是off blockchain的。
118 | - 全球分布式
119 |
120 | - [回到目录](#目录)
121 |
122 | ### 1.4.4 客户(检索客户和存储客户)选择filecoin的理由
123 | - 企业客户愿意使用filecoin来支付数据存储和检索的理由
124 | - filecoin是一套激励相容的系统,filecoin的设计目标保证了每个参与者(包括客户,矿工,投资者和开发人员)的最有利可图的选择或者说是最优策略是采取行动来提高网络的服务质量。具备技术先进性。
125 | - 数据更为安全
126 | - 抵押机制促使矿工提供稳定安全服务,预计会出现声誉系统。矿工需要自行保证系统内的稳定性。
127 | - 即便提供商出现故障,**filecoin网络可以在多个存储提供商之间进行额外的修复。**
128 | - 客户可以根据数据安全等级选择副本数量。
129 |
130 | - 价格更为廉价
131 | - 内容寻址的本质决定了其全局冗余度低。
132 | - filecoin作为全球性的分布式存储系统,可以做全球性去重,从而降低整个网络存储成本。
133 |
134 | - 个人客户选择使用filecoin的理由
135 | - 预计filecoin将提供允许一方支付另一方来检索数据的结构
136 | - 包括web 2.0网站的主要内容分发模型,在该模型中,网站所有者为基础设施服务付费,以免费向其用户提供数据,然后以其他方式通过内容获利。
137 | - filecoin的设计目标,让用户和内容创作者能够探索各种新的内容分发和经济模型。
138 | - 例如版权问题的解决
139 |
140 | - [回到目录](#目录)
141 |
--------------------------------------------------------------------------------
/10.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之十:filecoin源码分析之支撑包分析(2/2)
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 | ## 目录
6 | - 10 filecoin源码分析之支撑包分析(2/2)
7 | - 10.1 repo
8 | - 10.2 proofs和sectorbuilder
9 | - 10.3 type
10 | - 10.4 abi
11 | - 10.5 pubsub
12 |
13 |
14 | > 本章续上一章的支撑包介绍,主要为便于后面章节的源码理解
15 |
16 | ## 10.1 repo
17 |
18 | - 提供功能
19 | - 实例化fs资源或者mem资源
20 | - 提供读取、设置API地址方法
21 | - 提供存储已被校验区块的方法
22 | - 提供阶段密封数据存储方法
23 | - 提供密封完成数据存储方法
24 | - 提供读取配置方法
25 | - 提供通用数据存储方法
26 | - 提供交易数据存储方法
27 | - 提供钱包信息存储方法
28 | - 提供存储密钥方法
29 | - 提供快照配置存储方法
30 | - 提供版本号读取方法
31 |
32 | ```
33 | ▼ package
34 | repo
35 |
36 | ▶ imports
37 |
38 | ▼ constants
39 | // 当前为1,可以cat ~/.filecoin/version确认
40 | +Version : uint
41 |
42 | ▼+Datastore : interface
43 | [embedded]
44 | // 包含datastore的read、write、batch
45 | +datastore.Batching
46 |
47 | // Repo接口分别由fsrepo及memrepo实现
48 | ▼+Repo : interface
49 | [methods]
50 | // 读取API地址
51 | +APIAddr() : string, error
52 | // 存储已被校验过的区块数据
53 | +ChainDatastore() : Datastore
54 | // 关闭
55 | +Close() : error
56 | // 读取配置,对应上一章中的config
57 | +Config() : *config.Config
58 | // 存储通用数据
59 | +Datastore() : Datastore
60 | // 交易数据存储
61 | +DealsDatastore() : Datastore
62 | // 存储密钥相关
63 | +Keystore() : keystore.Keystore
64 | // 存储倒数第二个配置
65 | +ReplaceConfig(cfg *config.Config) : error
66 | // 存储密封扇区
67 | +SealedDir() : string
68 | // 设置API地址
69 | +SetAPIAddr(string) : error
70 | // 存储分段密封扇区
71 | +StagingDir() : string
72 | // 读取版本号
73 | +Version() : uint
74 | // 存储钱包信息
75 | +WalletDatastore() : Datastore
76 | ```
77 |
78 | ```
79 | location: repo/fsrepo.go
80 |
81 | ▼ package
82 | repo
83 |
84 | ▼ constants
85 | // api文件
86 | +APIFile
87 | // chain目录:chain
88 | -chainDatastorePrefix
89 | // 配置文件名称,对应上一章中的config
90 | -configFilename
91 | // 交易目录:deals
92 | -dealsDatastorePrefix
93 | // 资源目录锁文件:repo.lock
94 | -lockFile
95 | // 快照文件前缀名 snapshot
96 | -snapshotFilenamePrefix
97 | // 快照目录;配置快照
98 | -snapshotStorePrefix
99 | // 临时配置文件名称
100 | -tempConfigFilename
101 | // version文件名称
102 | -versionFilename
103 | // 钱包目录名称wallet
104 | -walletDatastorePrefix
105 |
106 | ▼ variables
107 | -log
108 |
109 | ▼+FSRepo : struct
110 | [fields]
111 | -cfg : *config.Config
112 | -chainDs : Datastore
113 | -dealsDs : Datastore
114 | -ds : Datastore
115 | -keystore : keystore.Keystore
116 | -lk : sync.RWMutex
117 | -lockfile : io.Closer
118 | // 资源目录路径
119 | -path : string
120 | // 资源目录版本
121 | -version : uint
122 | -walletDs : Datastore
123 | [methods]
124 | +APIAddr() : string, error
125 | +ChainDatastore() : Datastore
126 | +Close() : error
127 | +Config() : *config.Config
128 | +Datastore() : Datastore
129 | +DealsDatastore() : Datastore
130 | +Keystore() : keystore.Keystore
131 | +ReplaceConfig(cfg *config.Config) : error
132 | +SealedDir() : string
133 | +SetAPIAddr(maddr string) : error
134 | // 快照存储
135 | +SnapshotConfig(cfg *config.Config) : error
136 | +StagingDir() : string
137 | +Version() : uint
138 | +WalletDatastore() : Datastore
139 | -loadConfig() : error
140 | -loadFromDisk() : error
141 | -loadVersion() : uint, error
142 | -openChainDatastore() : error
143 | -openDatastore() : error
144 | -openDealsDatastore() : error
145 | -openKeystore() : error
146 | -openWalletDatastore() : error
147 | -removeAPIFile() : error
148 | -removeFile(path string) : error
149 | [functions]
150 | // 打开已被初始化过的资源目录
151 | +OpenFSRepo(p string) : *FSRepo, error
152 |
153 | ▼+NoRepoError : struct
154 | [fields]
155 | +Path : string
156 | [methods]
157 | +Error() : string
158 |
159 | ▼ functions
160 | // 从文件中读取api file
161 | +APIAddrFromFile(apiFilePath string) : string, error
162 | // 初始化资源目录
163 | +InitFSRepo(p string, cfg *config.Config) : error
164 | -checkWritable(dir string) : error
165 | -fileExists(file string) : bool
166 | -genSnapshotFileName() : string
167 | -initConfig(p string, cfg *config.Config) : error
168 | -initVersion(p string, version uint) : error
169 | -isInitialized(p string) : bool, error
170 | ```
171 |
172 | ```
173 | ▼ package
174 | repo
175 |
176 | ▼ imports
177 |
178 | ▼+MemRepo : struct
179 | [fields]
180 | +C : *config.Config
181 | +Chain : Datastore
182 | +D : Datastore
183 | +DealsDs : Datastore
184 | +Ks : keystore.Keystore
185 | +W : Datastore
186 | -apiAddress : string
187 | -lk : sync.RWMutex
188 | -sealedDir : string
189 | -stagingDir : string
190 | -version : uint
191 | [methods]
192 | +APIAddr() : string, error
193 | +ChainDatastore() : Datastore
194 | +CleanupSectorDirs()
195 | +Close() : error
196 | +Config() : *config.Config
197 | +Datastore() : Datastore
198 | +DealsDatastore() : Datastore
199 | +Keystore() : keystore.Keystore
200 | +ReplaceConfig(cfg *config.Config) : error
201 | +SealedDir() : string
202 | +SetAPIAddr(addr string) : error
203 | +StagingDir() : string
204 | +Version() : uint
205 | +WalletDatastore() : Datastore
206 | [functions]
207 | // 实例化内存资源接口,会调用NewInMemoryRepoWithSectorDirectories
208 | +NewInMemoryRepo() : *MemRepo
209 | // 实例化内存资源接口,指定阶段密封和最终密封目录
210 | +NewInMemoryRepoWithSectorDirectories(staging, sealedDir string) : *MemRepo
211 | ```
212 |
213 | ## 10.2 proofs和sectorbuilder
214 |
215 | - proofs提供功能
216 | - 校验时空证明的方法
217 | - 校验密封证明的方法
218 | - 更细节的注释见如下代码笔者增加的注释
219 | - rustverifier实现具体的方法
220 |
221 | ```
222 | location: proofs/types.go
223 |
224 | ▼ package
225 | proofs
226 |
227 | ▼ constants
228 | // merkle根长度
229 | +CommitmentBytesLen : uint
230 | // 时空证明挑战参数长度:32bytes
231 | +PoStChallengeSeedBytesLen : uint
232 | // 密封复制证明长度:384bytes
233 | +SealBytesLen : uint
234 | // 时空证明长度:192bytes
235 | +SnarkBytesLen : uint
236 |
237 | // 原始数据的merkle根,由PoRep输出
238 | +CommD : []byte
239 |
240 | // 副本数据的merkle根,由PoRep输出
241 | +CommR : []byte
242 |
243 | // 中间层的merkle根,由PoRep输出
244 | +CommRStar : []byte
245 |
246 | // 挑战随机参数,32bytes,256bits,PoSt的输入
247 | +PoStChallengeSeed : []byte
248 |
249 | // 时空证明输出,192bytes
250 | +PoStProof : []byte
251 |
252 | // 密封复制证明,384bytes
253 | +SealProof : []byte
254 | ```
255 |
256 | ```
257 | location: proofs/interface.go
258 |
259 | ▼ package
260 | proofs
261 |
262 | ▼ constants
263 | +Live
264 | +Test
265 |
266 | +SectorStoreType : int
267 |
268 | // 校验时空证明校验请求
269 | ▼+VerifyPoSTRequest : struct
270 | [fields]
271 | // 挑战参数
272 | +ChallengeSeed : PoStChallengeSeed
273 | +CommRs : []CommR
274 | +Faults : []uint64
275 | +Proof : PoStProof
276 | +StoreType : SectorStoreType
277 |
278 | ▼+VerifyPoSTResponse : struct
279 | [fields]
280 | +IsValid : bool
281 |
282 | // 向特定矿工&特定扇区发起密封校验请求
283 | ▼+VerifySealRequest : struct
284 | [fields]
285 | // 来自于密封的返回参数
286 | +CommD : CommD
287 | +CommR : CommR
288 | +CommRStar : CommRStar
289 | +Proof : SealProof
290 | // 矿工标识
291 | +ProverID : [31]byte
292 | // 扇区ID
293 | +SectorID : [31]byte
294 | // 用于控制密封校验效率
295 | +StoreType : SectorStoreType
296 |
297 | ▼+VerifySealResponse : struct
298 | [fields]
299 | +IsValid : bool
300 |
301 | ▼+Verifier : interface
302 | [methods]
303 | // 校验时空证明
304 | +VerifyPoST(VerifyPoSTRequest) : VerifyPoSTResponse, error
305 | // 校验密封证明
306 | +VerifySeal(VerifySealRequest) : VerifySealResponse, error
307 | ```
308 |
309 | ```
310 | location: proofs/rustverifier.go
311 |
312 | ▼ package
313 | proofs
314 |
315 | ▶ imports
316 |
317 | ▼ variables
318 | -log
319 |
320 | // RustVerifier 实现VerifyPoST与VerifySeal接口
321 | ▼+RustVerifier : struct
322 | [methods]
323 | +VerifyPoST(req VerifyPoSTRequest) : VerifyPoSTResponse, error
324 | +VerifySeal(req VerifySealRequest) : VerifySealResponse, error
325 |
326 | ▼ functions
327 | +CSectorStoreType(cfg SectorStoreType) : *C.ConfiguredStore, error
328 | -cUint64s(src []uint64) : *C.uint64_t, C.size_t
329 | -elapsed(what string) : func()
330 | ```
331 |
332 | - sectorbuilder
333 | - 提供向unsealed扇区写入pieces的方法
334 | - 提供生成时空证明的方法
335 | - 提供从特定扇区读取特定pieces的方法
336 | - 提供密封完成通知的方法
337 | - 提供批量密封所有未完成的分段扇区
338 | - 与rust-fil-proof交互,更深入的逻辑需要参见rust
339 |
340 | ```
341 | location: proofs/sectorbuilder/interface.go
342 |
343 | package sectorbuilder
344 |
345 | ▶ imports
346 |
347 | // 生成生成时空证明请求
348 | ▼+GeneratePoSTRequest : struct
349 | [fields]
350 | +ChallengeSeed : proofs.PoStChallengeSeed
351 | +CommRs : []proofs.CommR
352 |
353 | // 生成生成时空证明响应
354 | ▼+GeneratePoSTResponse : struct
355 | [fields]
356 | +Faults : []uint64
357 | +Proof : proofs.PoStProof
358 |
359 | ▼+PieceInfo : struct
360 | [fields]
361 | +Ref : cid.Cid
362 | +Size : uint64
363 |
364 | // 密封元数据
365 | ▼+SealedSectorMetadata : struct
366 | [fields]
367 | +CommD : proofs.CommD
368 | // 副本哈希后续将被删除
369 | +CommR : proofs.CommR
370 | +CommRStar : proofs.CommRStar
371 | // Pieces后续将被删除
372 | +Pieces : []*PieceInfo
373 | +Proof : proofs.SealProof
374 | +SectorID : uint64
375 |
376 | // 密封结果
377 | ▼+SectorSealResult : struct
378 | [fields]
379 | +SealingErr : error
380 | +SealingResult : *SealedSectorMetadata
381 | +SectorID : uint64
382 |
383 | // SectorBuilder提供相关功能
384 | // 1 写入、密封pieces至扇区
385 | // 2 unseal、读取pieces
386 | ▼+SectorBuilder : interface
387 | [methods]
388 | // 向unsealed扇区写入pieces
389 | +AddPiece(ctx context.Context, pi *PieceInfo) : uint64, error
390 | +Close() : error
391 | // 生成时空证明
392 | +GeneratePoST(GeneratePoSTRequest) : GeneratePoSTResponse, error
393 | +GetMaxUserBytesPerStagedSector() : uint64, error
394 | // 从扇区中读取特定pieces
395 | +ReadPieceFromSealedSector(pieceCid cid.Cid) : io.Reader, error
396 | // 密封所有未完成的分段扇区
397 | +SealAllStagedSectors(ctx context.Context) : error
398 | // 密封完成的通知
399 | +SectorSealResults() : chan SectorSealResult
400 |
401 | ▼ functions
402 | -init()
403 | ```
404 |
405 | ```
406 | location: proofs/sectorbuilder/poller.go
407 |
408 | // 当pieces加入后,会进行FFI调用,定时执行密封
409 | const SealedSectorPollingInterval = 1 * time.Second
410 | ```
411 |
412 |
413 | ## 10.3 type
414 |
415 | 如下对一些主要结构进行简析
416 |
417 | - AttoFIL(10*-18 FIL)
418 | - 提供AttoFIL的算数运算方法
419 | - 提供AttoFIL的逻辑运算方法
420 |
421 | - Block
422 | - 区块结构
423 |
424 | ```
425 | ▼+Block : struct
426 | [fields]
427 | +Height : Uint64
428 | +MessageReceipts : []*MessageReceipt
429 | +Messages : []*SignedMessage
430 | +Miner : address.Address
431 | +Nonce : Uint64
432 | +ParentWeight : Uint64
433 | +Parents : SortedCidSet
434 | +Proof : proofs.PoStProof
435 | +StateRoot : cid.Cid
436 | +Ticket : Signature
437 | -cachedBytes : []byte
438 | -cachedCid : cid.Cid
439 | [methods]
440 | +Cid() : cid.Cid
441 | +Equals(other *Block) : bool
442 | +IsParentOf(c Block) : bool
443 | +Score() : uint64
444 | +String() : string
445 | +ToNode() : node.Node
446 | [functions]
447 | +DecodeBlock(b []byte) : *Block, error
448 | ```
449 |
450 | - BlockHeight
451 | - 区块高度相关操作方法
452 |
453 | ```
454 | ▼+BlockHeight : struct
455 | [fields]
456 | -val : *big.Int
457 | [methods]
458 | +Add(y *BlockHeight) : *BlockHeight
459 | +AsBigInt() : *big.Int
460 | +Bytes() : []byte
461 | +Equal(y *BlockHeight) : bool
462 | +GreaterEqual(y *BlockHeight) : bool
463 | +GreaterThan(y *BlockHeight) : bool
464 | +LessEqual(y *BlockHeight) : bool
465 | +LessThan(y *BlockHeight) : bool
466 | +String() : string
467 | +Sub(y *BlockHeight) : *BlockHeight
468 | [functions]
469 | +NewBlockHeight(x uint64) : *BlockHeight
470 | +NewBlockHeightFromBytes(buf []byte) : *BlockHeight
471 | +NewBlockHeightFromString(s string, base int) : *BlockHeight, bool
472 | ```
473 |
474 | - BytesAmount (*big.Int)
475 | - 提供相关的算数逻辑运算
476 |
477 | - ChannelID(支付通道结构体)
478 |
479 | ```
480 | ▼+ChannelID : struct
481 | [fields]
482 | -val : *big.Int
483 | [methods]
484 | +Bytes() : []byte
485 | +Equal(y *ChannelID) : bool
486 | +Inc() : *ChannelID
487 | +KeyString() : string
488 | +String() : string
489 | [functions]
490 | +NewChannelID(x uint64) : *ChannelID
491 | +NewChannelIDFromBytes(buf []byte) : *ChannelID
492 | +NewChannelIDFromString(s string, base int) : *ChannelID, bool
493 | ```
494 |
495 | - 一些变量定义
496 | - 创建各类actor对象
497 |
498 | ```
499 | func init() {
500 | AccountActorCodeObj = dag.NewRawNode([]byte("accountactor"))
501 | AccountActorCodeCid = AccountActorCodeObj.Cid()
502 | StorageMarketActorCodeObj = dag.NewRawNode([]byte("storagemarket"))
503 | StorageMarketActorCodeCid = StorageMarketActorCodeObj.Cid()
504 | PaymentBrokerActorCodeObj = dag.NewRawNode([]byte("paymentbroker"))
505 | PaymentBrokerActorCodeCid = PaymentBrokerActorCodeObj.Cid()
506 | MinerActorCodeObj = dag.NewRawNode([]byte("mineractor"))
507 | MinerActorCodeCid = MinerActorCodeObj.Cid()
508 | BootstrapMinerActorCodeObj = dag.NewRawNode([]byte("bootstrapmineractor"))
509 | BootstrapMinerActorCodeCid = BootstrapMinerActorCodeObj.Cid()
510 | }
511 | ```
512 |
513 | - Message相关
514 | - 消息结构及方法
515 | - filecoin网络的交易由一些列的Message组成
516 |
517 | ```
518 | ▼+Message : struct
519 | [fields]
520 | +From : address.Address
521 | +Method : string
522 | +Nonce : Uint64
523 | +Params : []byte
524 | +To : address.Address
525 | +Value : *AttoFIL
526 | [methods]
527 | +Cid() : cid.Cid, error
528 | +Marshal() : []byte, error
529 | +String() : string
530 | +Unmarshal(b []byte) : error
531 | [functions]
532 | +NewMessage(from, to address.Address, nonce uint64, value *AttoFIL, method string, params []byte) : *Message
533 | ```
534 |
535 | ```
536 | ▼+MessageReceipt : struct
537 | [fields]
538 | +ExitCode : uint8
539 | +GasAttoFIL : *AttoFIL
540 | +Return : [][]byte
541 | ```
542 |
543 | ```
544 | ▼+MeteredMessage : struct
545 | [fields]
546 | +GasLimit : GasUnits
547 | +GasPrice : AttoFIL
548 | [embedded]
549 | +Message : Message
550 | [methods]
551 | +Marshal() : []byte, error
552 | +Unmarshal(b []byte) : error
553 | [functions]
554 | +NewMeteredMessage(msg Message, gasPrice AttoFIL, gasLimit GasUnits) : *MeteredMessage
555 | ```
556 |
557 | ```
558 | ▼+SignedMessage : struct
559 | [fields]
560 | +Signature : Signature
561 | [embedded]
562 | +MeteredMessage : MeteredMessage
563 | [methods]
564 | +Cid() : cid.Cid, error
565 | +Marshal() : []byte, error
566 | +RecoverAddress(r Recoverer) : address.Address, error
567 | +String() : string
568 | +Unmarshal(b []byte) : error
569 | +VerifySignature() : bool
570 | [functions]
571 | +NewSignedMessage(msg Message, s Signer, gasPrice AttoFIL, gasLimit GasUnits) : *SignedMessage, error
572 | ```
573 |
574 | - TipSet
575 | - 区块集合
576 |
577 | ```
578 | +Tip : Block
579 |
580 | ▼+TipSet : map[cid.Cid]*Tip
581 | [methods]
582 | +AddBlock(b *Block) : error
583 | +Clone() : TipSet
584 | +Equals(ts2 TipSet) : bool
585 | +Height() : uint64, error
586 | +MinTicket() : Signature, error
587 | +ParentWeight() : uint64, error
588 | +Parents() : SortedCidSet, error
589 | +String() : string
590 | +ToSlice() : []*Block
591 | +ToSortedCidSet() : SortedCidSet
592 | ```
593 |
594 | ## 10.4 abi
595 | - abi
596 | - 对filecoin中的各类数据定义数据类型
597 | - 提供abi编解码操作方法
598 |
599 |
600 | ## 10.5 pubsub
601 |
602 | - 提供功能
603 | - 提供订阅实例化以及订阅方法
604 | - 提供发布实例化以及发布方法
605 |
606 | ```
607 | ▼ package
608 | pubsub
609 |
610 | ▶ imports
611 |
612 | ▼+Subscriber : struct
613 | [fields]
614 | -pubsub : *libp2p.PubSub
615 | [methods]
616 | +Subscribe(topic string) : Subscription, error
617 | [functions]
618 | +NewSubscriber(sub *libp2p.PubSub) : *Subscriber
619 |
620 | ▼-subscriptionWrapper : struct
621 | [embedded]
622 | +*libp2p.Subscription : *libp2p.Subscription
623 | [methods]
624 | +Next(ctx context.Context) : Message, error
625 |
626 | ▼+Message : interface
627 | [methods]
628 | +GetData() : []byte
629 | +GetFrom() : peer.ID
630 |
631 | ▼+Subscription : interface
632 | [methods]
633 | +Cancel()
634 | +Next(ctx context.Context) : Message, error
635 | +Topic() : string
636 | ```
637 |
638 | ```
639 | ▼ package
640 | pubsub
641 |
642 | ▶ imports
643 |
644 | ▼+Publisher : struct
645 | [fields]
646 | -pubsub : *pubsub.PubSub
647 | [methods]
648 | +Publish(topic string, data []byte) : error
649 | [functions]
650 | +NewPublisher(sub *pubsub.PubSub) : *Publisher
651 | ```
652 |
--------------------------------------------------------------------------------
/11.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之十一:filecoin源码分析之内部接口层api包分析
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 11.filecoin源码分析之内部接口层api包分析
8 | - 11.1 api
9 | - 11.2 actor
10 | - 11.3 address
11 | - 11.4 client
12 | - 11.5 config
13 | - 11.6 daemon
14 | - 11.7 dag
15 | - 11.8 id
16 | - 11.11.log
17 | - 11.10 miner
18 | - 11.11 mining
19 | - 11.12 ping
20 | - 11.13 retrieval_client
21 | - 11.14 swarm
22 |
23 | > api包提供内部接口,供协议层、command/REST使用
24 |
25 | > 较大程度依赖node包
26 |
27 | ## 11.1 api
28 | ### 11.1.1 api的接口定义
29 |
30 | > 如下所示,包含了一系列子接口
31 |
32 | ```
33 | type API interface {
34 | Actor() Actor
35 | Address() Address
36 | Client() Client
37 | Daemon() Daemon
38 | Dag() Dag
39 | ID() ID
40 | Log() Log
41 | Miner() Miner
42 | Mining() Mining
43 | Paych() Paych
44 | Ping() Ping
45 | RetrievalClient() RetrievalClient
46 | Swarm() Swarm
47 | Version() Version
48 | }
49 | ```
50 |
51 | ### 11.1.2 api的接口实现
52 |
53 | ```
54 | ▼ package
55 | impl
56 |
57 | ▶ imports
58 |
59 | // nodeAPI来实现其接口定义
60 | ▼-nodeAPI : struct
61 | [fields]
62 | // 合约
63 | -actor : *nodeActor
64 | // 地址
65 | -address : *nodeAddress
66 | // 客户端
67 | -client : *nodeClient
68 | // daemon
69 | -daemon : *nodeDaemon
70 | // dag
71 | -dag : *nodeDag
72 | // 节点ID
73 | -id : *nodeID
74 | // 日志
75 | -log : *nodeLog
76 | // 日志
77 | -logger : logging.EventLogger
78 | // 矿工
79 | -miner : *nodeMiner
80 | // 挖矿
81 | -mining : *nodeMining
82 | // 节点
83 | -node : *node.Node
84 | // 支付通道
85 | -paych : *nodePaych
86 | // ping
87 | -ping : *nodePing
88 | // 检索客户端
89 | -retrievalClient : *nodeRetrievalClient
90 | // swarm
91 | -swarm : *nodeSwarm
92 | // 版本
93 | -version : *nodeVersion
94 |
95 | [methods]
96 | // 如下为实现API接口
97 | +Actor() : api.Actor
98 | +Address() : api.Address
99 | +Client() : api.Client
100 | +Daemon() : api.Daemon
101 | +Dag() : api.Dag
102 | +ID() : api.ID
103 | +Log() : api.Log
104 | +Miner() : api.Miner
105 | +Mining() : api.Mining
106 | +Paych() : api.Paych
107 | +Ping() : api.Ping
108 | +RetrievalClient() : api.RetrievalClient
109 | +Swarm() : api.Swarm
110 | +Version() : api.Version
111 |
112 | ▼ functions
113 | // 实例化API
114 | // 1 获取高层API porcelainAPI 指针,miner与paych有用到
115 | // 2 调用各子系统的实例化函数逐一实例化
116 | +New(node *node.Node) : api.API
117 | ```
118 |
119 | ## 11.2 actor
120 | ### 11.2.1 actor的接口定义
121 |
122 | ```
123 | ▼ package
124 | api
125 |
126 | ▶ imports
127 |
128 | ▼+ActorView : struct
129 | [fields]
130 | // actor类型
131 | +ActorType : string
132 | // actor地址
133 | +Address : string
134 | // actor余额
135 | +Balance : *types.AttoFIL
136 | // actor代码-CID
137 | +Code : cid.Cid
138 | // 导出符号集合
139 | +Exports : ReadableExports
140 | // 表征actor实例的状态
141 | +Head : cid.Cid
142 | // 消息计数器,仅为account actors与外部发生交互的时候计算
143 | +Nonce : uint64
144 |
145 | // 导出符号集合
146 | +ReadableExports : map[string]*ReadableFunctionSignature
147 |
148 | ▼+ReadableFunctionSignature : struct
149 | [fields]
150 | // 参数
151 | +Params : []string
152 | // 返回
153 | +Return : []string
154 |
155 | ▼+Actor : interface
156 | // 目前接口只有查看功能,返回合约的具体信息
157 | [methods]
158 | +Ls(ctx context.Context) : []*ActorView, error
159 | ```
160 | ### 11.2.2 actor的接口实现
161 |
162 | ```
163 | ▼ package
164 | impl
165 |
166 | ▶ imports
167 |
168 | // 使用nodeActor来实现Actor接口
169 | ▼-nodeActor : struct
170 | [fields]
171 | -api : *nodeAPI
172 | [methods]
173 | // 调用ls方法实现查询功能
174 | +Ls(ctx context.Context) : []*api.ActorView, error
175 | [functions]
176 | // 实例化nodeActor,由api实现代码中调用
177 | -newNodeActor(api *nodeAPI) : *nodeActor
178 |
179 | ▼ functions
180 | // 获取合约类型
181 | // 1 account actor
182 | // 2 存储市场actor
183 | // 3 支付通道actor
184 | // 4 矿工actor
185 | // 4 BootstrapMiner actor
186 | -getActorType(actType exec.ExecutableActor) : string
187 | // 查询合约状态
188 | -ls(ctx context.Context, fcn *node.Node, actorGetter state.GetAllActorsFunc) : []*api.ActorView, error
189 | -makeActorView(act *actor.Actor, addr string, actType exec.ExecutableActor) : *api.ActorView
190 | -makeReadable(f *exec.FunctionSignature) : *api.ReadableFunctionSignature
191 | -presentExports(e exec.Exports) : api.ReadableExports
192 | ```
193 |
194 | ## 11.3 address
195 | - 提供功能
196 | - 地址显示方法
197 | - 地址查找方法
198 | - 创建地址方法
199 | - 导出地址方法
200 | - 导入地址方法
201 |
202 | ```
203 | ▼ package
204 | api
205 |
206 | ▶ imports
207 |
208 | ▼+Address : interface
209 | [methods]
210 | +Addrs() : Addrs
211 | +Export(ctx context.Context, addrs []address.Address) : []*types.KeyInfo, error
212 | +Import(ctx context.Context, f files.File) : []address.Address, error
213 |
214 | ▼+Addrs : interface
215 | [methods]
216 | +Lookup(ctx context.Context, addr address.Address) : peer.ID, error
217 | +Ls(ctx context.Context) : []address.Address, error
218 | +New(ctx context.Context) : address.Address, error
219 | ```
220 |
221 | ## 11.4 client
222 | - 提供如下功能
223 | - 查询piece数据(DAG格式)
224 | - 导入数据(相当于ipfs add)
225 | - 列出所有订单
226 | - 支付
227 | - 发起存储交易
228 | - 查询存储交易
229 |
230 | ```
231 | ▼+Ask : struct
232 | [fields]
233 | +Error : error
234 | +Expiry : *types.BlockHeight
235 | +ID : uint64
236 | +Miner : address.Address
237 | +Price : *types.AttoFIL
238 |
239 | ▼+Client : interface
240 | [methods]
241 | +Cat(ctx context.Context, c cid.Cid) : uio.DagReader, error
242 | +ImportData(ctx context.Context, data io.Reader) : ipld.Node, error
243 | +ListAsks(ctx context.Context) : chan Ask, error
244 | +Payments(ctx context.Context, dealCid cid.Cid) : []*paymentbroker.PaymentVoucher, error
245 | +ProposeStorageDeal(ctx context.Context, data cid.Cid, miner address.Address, ask uint64, duration uint64, allowDuplicates bool) : *storage.DealResponse, error
246 | +QueryStorageDeal(ctx context.Context, prop cid.Cid) : *storage.DealResponse, error
247 | ```
248 |
249 | ## 11.5 config
250 | - 提供功能
251 | - Get配置
252 | - Set配置
253 |
254 | ## 11.6 daemon
255 | - 提供功能
256 | - 启动进程相关
257 | - 具体的业务启动逻辑会调用到node包
258 |
259 |
260 | ```
261 | ▼ package
262 | api
263 |
264 | ▶ imports
265 |
266 | ▼+DaemonInitConfig : struct
267 | [fields]
268 | // 如果配置,定期检查并密封staged扇区
269 | +AutoSealIntervalSeconds : uint
270 | +DefaultAddress : address.Address
271 | // 指定网络
272 | +DevnetNightly : bool
273 | +DevnetTest : bool
274 | +DevnetUser : bool
275 | // 创世文件
276 | +GenesisFile : string
277 | +PeerKeyFile : string
278 | // repo目录
279 | +RepoDir : string
280 | // 指定矿工
281 | +WithMiner : address.Address
282 |
283 | +DaemonInitOpt : func(*DaemonInitConfig)
284 |
285 | ▼+Daemon : interface
286 | [methods]
287 | +Init(ctx context.Context, opts ...DaemonInitOpt) : error
288 | +Start(ctx context.Context) : error
289 | +Stop(ctx context.Context) : error
290 | ```
291 |
292 | ## 11.7 dag
293 | - 提供功能
294 | - dag查询功能
295 | - 类似ipfs block get
296 |
297 | ## 11.8 id
298 | - 提供功能
299 | - ID详细信息
300 | - 如多地址、协议版本、导出公钥等
301 |
302 | ```
303 | ▼+IDDetails : struct
304 | [fields]
305 | +Addresses : []ma.Multiaddr
306 | +AgentVersion : string
307 | +ID : peer.ID
308 | +ProtocolVersion : string
309 | +PublicKey : []byte
310 | [methods]
311 | +MarshalJSON() : []byte, error
312 | +UnmarshalJSON(data []byte) : error
313 |
314 | ▼+ID : interface
315 | [methods]
316 | +Details() : *IDDetails, error
317 |
318 | ▼ functions
319 | -decode(idd map[string]*json.RawMessage, key string, dest interface{}) : error
320 | ```
321 |
322 | ## 11.11.log
323 | - 提供日志功能
324 |
325 | ```
326 | ▼+Log : interface
327 | [methods]
328 | +Tail(ctx context.Context) : io.Reader
329 | ```
330 |
331 | ## 11.10 miner
332 | - 创建矿工
333 |
334 | ```
335 | ▼+Miner : interface
336 | [methods]
337 | +Create(ctx context.Context, fromAddr address.Address, gasPrice types.AttoFIL, gasLimit types.GasUnits, pledge uint64, pid peer.ID, collateral *types.AttoFIL) : address.Address, error
338 | ```
339 |
340 | ## 11.11 mining
341 | - 挖矿控制
342 | - 启动
343 | - 停止
344 |
345 | ```
346 | ▼+Mining : interface
347 | [methods]
348 | +Once(ctx context.Context) : *types.Block, error
349 | +Start(ctx context.Context) : error
350 | +Stop(ctx context.Context) : error
351 | ```
352 |
353 | ## 11.12 ping
354 | - 提供ping接口
355 |
356 | ```
357 | ▼+PingResult : struct
358 | [fields]
359 | +Success : bool
360 | +Text : string
361 | +Time : time.Duration
362 |
363 | ▼+Ping : interface
364 | [methods]
365 | +Ping(ctx context.Context, pid peer.ID, count uint, delay time.Duration) : chan *PingResult, error
366 | ```
367 |
368 | ## 11.13 retrieval_client
369 | - 提供检索接口
370 |
371 | ```
372 | ▼+RetrievalClient : interface
373 | [methods]
374 | +RetrievePiece(ctx context.Context, pieceCID cid.Cid, minerAddr address.Address) : io.ReadCloser, error
375 | ```
376 |
377 | ## 11.14 swarm
378 | - 提供节点连接功能
379 | - 显示连接节点
380 | - 连接节点
381 | - 查找节点
382 |
383 | ```
384 | ▼+SwarmConnInfo : struct
385 | [fields]
386 | +Addr : string
387 | +Latency : string
388 | +Muxer : string
389 | +Peer : string
390 | +Streams : []SwarmStreamInfo
391 | [methods]
392 | +Len() : int
393 | +Less(i, j int) : bool
394 | +Swap(i, j int)
395 |
396 | ▼+SwarmConnInfos : struct
397 | [fields]
398 | +Peers : []SwarmConnInfo
399 | [methods]
400 | +Len() : int
401 | +Less(i, j int) : bool
402 | +Swap(i, j int)
403 |
404 | ▼+SwarmConnectResult : struct
405 | [fields]
406 | +Peer : string
407 | +Success : bool
408 |
409 | ▼+SwarmStreamInfo : struct
410 | [fields]
411 | +Protocol : string
412 |
413 | ▼+Swarm : interface
414 | [methods]
415 | +Connect(ctx context.Context, addrs []string) : []SwarmConnectResult, error
416 | +FindPeer(ctx context.Context, peerID peer.ID) : peerstore.PeerInfo, error
417 | +Peers(ctx context.Context, verbose, latency, streams bool) : *SwarmConnInfos, error
418 | ```
419 |
--------------------------------------------------------------------------------
/12.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之十二:filecoin源码分析之内部接口层plumbing&porcelain接口
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 12.filecoin源码分析之内部接口层plumbing&porcelain接口
8 | - 12.1 说明
9 | - 12.2 plumbing&porcelain模式简述
10 | - 12.3 plumbing底层接口
11 | - 12.4 porcelain高层接口
12 |
13 | ## 12.1 说明
14 | - 目前官方正在将api包解耦,往plumbing、porcelain中迁移
15 | - 缘由: 原来的api包,依赖于node包,而node包应该属于api之上的,这导致代码耦合性大
16 | - node作为一个上帝对象,被api包依赖,对架构扩展性,其他类型节点扩展开发不利
17 | - 就在笔者写这篇文章的同时,官方应该还在继续迁移,后面api包会逐步都迁移完
18 |
19 | - porcelain主要依赖于plumbing接口
20 |
21 | - 上一章所述的api包将会被废除
22 |
23 | ## 12.2 plumbing&porcelain模式简述
24 | - 该模式是借鉴git的思路,提供两种接口,porcelain偏高层面对用户更加友好方便;plumbing偏底层,友好度弱于porcelain
25 | - porcelain是英文瓷器的意思,类似洗手盆之类;plumbing是水管装置的意思,类似下水管,用户当然直接用洗手盆省心,不用管水管的事情
26 | - 用户级更偏向用porcelain,协议级更偏向使用plumbing,
27 |
28 | ## 12.3 plumbing底层接口
29 | - 说明
30 | - plumbing底层接口是为实现协议以及面向网络的必须最小实现
31 | - 更应用级别的调用更多将会调用到porcelain高层接口
32 |
33 | - 提供的具体功能接口
34 | - 区块状态读取
35 | - 配置信息
36 | - 日志
37 | - 消息池操作
38 | - 消息预览,Gas计算
39 | - 消息查询
40 | - 消息发送
41 | - 消息等待
42 | - 网络操作
43 | - Chain状态获取(actor信息)
44 | - 钱包底层操作
45 |
46 | - 具体的方法如下
47 |
48 | ```
49 | ▼ package
50 | plumbing
51 |
52 | ▶ imports
53 |
54 | ▼+API : struct
55 | [fields]
56 | -chain : chain.ReadStore
57 | -config : *cfg.Config
58 | -logger : logging.EventLogger
59 | -msgPool : *core.MessagePool
60 | -msgPreviewer : *msg.Previewer
61 | -msgQueryer : *msg.Queryer
62 | -msgSender : *msg.Sender
63 | -msgWaiter : *msg.Waiter
64 | -network : *ntwk.Network
65 | -sigGetter : *mthdsig.Getter
66 | -wallet : *wallet.Wallet
67 | [methods]
68 | +ActorGet(ctx context.Context, addr address.Address) : *actor.Actor, error
69 | +ActorGetSignature(ctx context.Context, actorAddr address.Address, method string) : *exec.FunctionSignature, error
70 | +BlockGet(ctx context.Context, id cid.Cid) : *types.Block, error
71 | +ChainHead(ctx context.Context) : types.TipSet
72 | +ChainLs(ctx context.Context) : chan interface{}
73 | +ConfigGet(dottedPath string) : interface{}, error
74 | +ConfigSet(dottedPath string, paramJSON string) : error
75 | +MessagePoolGet(cid cid.Cid) : *types.SignedMessage, bool
76 | +MessagePoolPending() : []*types.SignedMessage
77 | +MessagePoolRemove(cid cid.Cid)
78 | +MessagePreview(ctx context.Context, from, to address.Address, method string, params ...interface{}) : types.GasUnits, error
79 | +MessageQuery(ctx context.Context, optFrom, to address.Address, method string, params ...interface{}) : [][]byte, *exec.FunctionSignature, error
80 | +MessageSend(ctx context.Context, from, to address.Address, value *types.AttoFIL, gasPrice types.AttoFIL, gasLimit types.GasUnits, method string, params ...interface{}) : cid.Cid, error
81 | +MessageWait(ctx context.Context, msgCid cid.Cid, cb func(*types.Block, *types.SignedMessage, *types.MessageReceipt) error) : error
82 | +NetworkFindProvidersAsync(ctx context.Context, key cid.Cid, count int) : chan pstore.PeerInfo
83 | +NetworkGetPeerID() : peer.ID
84 | +PubSubPublish(topic string, data []byte) : error
85 | +PubSubSubscribe(topic string) : pubsub.Subscription, error
86 | +SignBytes(data []byte, addr address.Address) : types.Signature, error
87 | +WalletAddresses() : []address.Address
88 | +WalletFind(address address.Address) : wallet.Backend, error
89 | +WalletNewAddress() : address.Address, error
90 | [functions]
91 | +New(deps *APIDeps) : *API
92 | ```
93 |
94 | ## 12.4 porcelain高层接口
95 | - 说明
96 | - porcelain主要依赖plumbing实现。
97 | - 主要是面向用户级操作
98 |
99 | - 提供功能
100 | - 获取区块高度
101 | - 建立支付通道/多支付通道
102 | - 获取默认地址
103 | - 消息池等待未被打包进区块的消息
104 | - 采用默认地址发送消息
105 | - 获取指定矿工报价单
106 | - 获取矿工Owner地址
107 | - 获取矿工节点ID
108 | - 创建矿工,预览Gas消耗
109 | - 矿工报价,预览Gas消耗
110 | - 矿工报价
111 | - 获取签名支付凭证
112 | - 钱包余额查询
113 |
114 | ```
115 | ▼ package
116 | porcelain
117 |
118 | ▶ imports
119 |
120 | ▼+API : struct
121 | [embedded]
122 | +*plumbing.API : *plumbing.API
123 | [methods]
124 | +ChainBlockHeight(ctx context.Context) : *types.BlockHeight, error
125 | +CreatePayments(ctx context.Context, config CreatePaymentsParams) : *CreatePaymentsReturn, error
126 | +GetAndMaybeSetDefaultSenderAddress() : address.Address, error
127 | +MessagePoolWait(ctx context.Context, messageCount uint) : []*types.SignedMessage, error
128 | +MessageSendWithDefaultAddress(ctx context.Context, from, to address.Address, value *types.AttoFIL, gasPrice types.AttoFIL, gasLimit types.GasUnits, method string, params ...interface{}) : cid.Cid, error
129 | +MinerGetAsk(ctx context.Context, minerAddr address.Address, askID uint64) : minerActor.Ask, error
130 | +MinerGetOwnerAddress(ctx context.Context, minerAddr address.Address) : address.Address, error
131 | +MinerGetPeerID(ctx context.Context, minerAddr address.Address) : peer.ID, error
132 | +MinerPreviewCreate(ctx context.Context, fromAddr address.Address, pledge uint64, pid peer.ID, collateral *types.AttoFIL) : types.GasUnits, error
133 | +MinerPreviewSetPrice(ctx context.Context, from address.Address, miner address.Address, price *types.AttoFIL, expiry *big.Int) : types.GasUnits, error
134 | +MinerSetPrice(ctx context.Context, from address.Address, miner address.Address, gasPrice types.AttoFIL, gasLimit types.GasUnits, price *types.AttoFIL, expiry *big.Int) : MinerSetPriceResponse, error
135 | +PaymentChannelLs(ctx context.Context, fromAddr address.Address, payerAddr address.Address) : map[string]*paymentbroker.PaymentChannel, error
136 | +PaymentChannelVoucher(ctx context.Context, fromAddr address.Address, channel *types.ChannelID, amount *types.AttoFIL, validAt *types.BlockHeight) : *paymentbroker.PaymentVoucher, error
137 | +WalletBalance(ctx context.Context, address address.Address) : *types.AttoFIL, error
138 | [functions]
139 | +New(plumbing *plumbing.API) : *API
140 | ```
141 |
142 |
--------------------------------------------------------------------------------
/13.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之十三:filecoin源码分析之服务层actor及vm
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 13.filecoin源码分析之服务层actor及vm
8 | - 13.1 说明
9 | - 13.2 exec(actor及vm的接口定义)
10 | - 13.3 actor的类型及源码分析
11 | - 13.3.1 基础actor包
12 | - 13.3.2 storagemarket actor
13 | - 13.3.3 miner actor
14 | - 13.3.4 paymentbroker actor
15 | - 13.3.5 account actor
16 | - 13.4 vm(虚拟机运行环境)
17 | - 13.5 state包(actor状态)
18 |
19 | ## 13.1 说明
20 | > 分析源代码版本:master 2c87fd59(2019.3.7)
21 |
22 | > 回头看第三章开发网使用中创建矿工,提交订单,支付等操作实际上都是actor的新增及状态改变
23 |
24 | > 当前的实现vm还不具备通用abi数据的解释执行能力,未达到真正智能合约水平
25 |
26 | ## 13.2 exec(actor及vm的接口定义)
27 | - 说明
28 | - 提供可执行actor的最小接口要求 ExecutableActor (由actor及具体actor包实现)
29 | - 提供actor键值存取接口定义 Lookup (由actor包实现)
30 | - 提供状态临时存储的接口定义 Storage (由vm.Storage实现)
31 | - actor的执行环境接口定义 VMContext (由vm.context实现)
32 |
33 | - 具体源码注释如下
34 |
35 | ```
36 | ▼ package
37 | exec
38 |
39 | ▶ imports
40 |
41 | ▼ constants
42 | +ErrDanglingPointer
43 | +ErrDecode
44 | +ErrInsufficientGas
45 | +ErrStaleHead
46 |
47 | ▼ variables
48 | +Errors
49 |
50 | ▼+Error : string
51 | [methods]
52 | +Error() : string
53 |
54 | +ExportedFunc : func(ctx VMContext) []byte, uint8, error
55 |
56 | // actor符号集合
57 | ▼+Exports : map[string]*FunctionSignature
58 | [methods]
59 | // 判断是否存在特定方法
60 | +Has(method string) : bool
61 |
62 | // 对于单个函数的符号表
63 | // todo中的事情:需要转换为非go类型
64 | ▼+FunctionSignature : struct
65 | [fields]
66 | +Params : []abi.Type
67 | +Return : []abi.Type
68 |
69 | // 可执行合约接口,这是每一类型的合约必须实现的最小接口
70 | // 包括account,miner,storagemarket,paymentbroker
71 | ▼+ExecutableActor : interface
72 | [methods]
73 | +Exports() : Exports
74 | +InitializeState(storage Storage, initializerData interface{}) : error
75 |
76 | // 由actor.lookup实现键值存储 (actor/storage.go)
77 | ▼+Lookup : interface
78 | [methods]
79 | +Commit(ctx context.Context) : cid.Cid, error
80 | +Delete(ctx context.Context, k string) : error
81 | +Find(ctx context.Context, k string) : interface{}, error
82 | +IsEmpty() : bool
83 | +Set(ctx context.Context, k string, v interface{}) : error
84 | +Values(ctx context.Context) : []*hamt.KV, error
85 |
86 | // 由vm.Storage实现
87 | // 解决持久化的问题,有副本防止回滚机制
88 | // 具体实现还有Flush持久化到datastore功能
89 | ▼+Storage : interface
90 | [methods]
91 | // 提交最新actor Head
92 | +Commit(cid.Cid, cid.Cid) : error
93 | // 如下都为内存中操作
94 | +Get(cid.Cid) : []byte, error
95 | +Head() : cid.Cid
96 | +Put(interface{}) : cid.Cid, error
97 |
98 | // actor的abi执行环境接口,由vm.context实现
99 | ▼+VMContext : interface
100 | [methods]
101 | // 创建新的合约地址
102 | +AddressForNewActor() : address.Address, error
103 | // 查询区块高度
104 | +BlockHeight() : *types.BlockHeight
105 | // Gas收费
106 | +Charge(cost types.GasUnits) : error
107 | // 创建合约
108 | +CreateNewActor(addr address.Address, code cid.Cid, initalizationParams interface{}) : error
109 | // 判断是否为account类型的Actor
110 | +IsFromAccountActor() : bool
111 | // 合约中交易信息
112 | +Message() : *types.Message
113 | // 执行合约函数
114 | +Send(to address.Address, method string, value *types.AttoFIL, params []interface{}) : [][]byte, uint8, error
115 | +Storage() : Storage
116 | // 当Storage接口完成会删除如下两项
117 | +ReadStorage() : []byte, error
118 | +WriteStorage(interface{}) : error
119 | ```
120 |
121 | ## 13.3 actor的类型及源码分析
122 | - actor包定义及实现了基础actor,此外filecoin还定义了四种内置的actor类型
123 | - 存储市场actor,此类actor整个网络只有一个实例,用于创建存储矿工、更新功率表、获取总存储容量
124 | - Miner actor,此类actor整个网络只有多个实例(随用户数增加而增加),用于执行矿工相关的操作
125 | - paymentbroker actor,此类actor整个网络只有一个实例,用于创建支付通道以及支付相关信息
126 | - account actor,账户actor,此类actor整个网络只有多个实例(随用户数增加而增加),只能用于基本的转账操作
127 |
128 | ### 13.3.1 基础actor包
129 | - 说明
130 | - 定义了actor的基础结构,其中code如果使用内置的如上四种actor,他们的值都是固定的
131 | - 提供了actor的基础操作方法
132 | - 见笔者在代码中的注释
133 |
134 | ```
135 | location: actor/actor.go
136 |
137 | ▼ package
138 | actor
139 |
140 | // Actor可以理解为合约或者账户,转账操作要检查code cid合法性
141 | ▼+Actor : struct
142 | [fields]
143 | //余额
144 | +Balance : *types.AttoFIL
145 |
146 | // 合约代码的cid,vm具体执行其对应的代码
147 | // 1 具体代码的cid
148 | // 2 在go语言实现的四种特定合约,这个字段是常量,比如account,miner,storagemarket,paymentbroker
149 | +Code : cid.Cid
150 |
151 | // 合约状态的最新状态
152 | +Head : cid.Cid
153 |
154 | // 防止重放攻击而设置的参数
155 | +Nonce : types.Uint64
156 |
157 | [methods]
158 | // 计算actor的cid
159 | +Cid() : cid.Cid, error
160 |
161 | // 打印合约信息
162 | +Format(f fmt.State, c rune)
163 |
164 | // 增加Nonce+1方法
165 | +IncNonce()
166 |
167 | // 编码
168 | +Marshal() : []byte, error
169 |
170 | // 解码
171 | +Unmarshal(b []byte) : error
172 | [functions]
173 | +NewActor(code cid.Cid, balance *types.AttoFIL) : *Actor
174 |
175 | ▼ functions
176 | // 只有account类型的actor使用
177 | +NextNonce(actor *Actor) : uint64, error
178 | -init()
179 | ```
180 |
181 | ```
182 | location: actor/export.go
183 |
184 | ▼ functions
185 | // 返回某个actor的方法执行函数
186 | +MakeTypedExport(actor exec.ExecutableActor, method string) : exec.ExportedFunc
187 |
188 | // 序列化成字节切片
189 | +MarshalValue(val interface{}) : []byte, error
190 | ```
191 |
192 | ### 13.3.2 storagemarket actor
193 | - 主要功能
194 | - 创建存储矿工
195 | - 获取总存储量
196 | - 更新功率
197 |
198 | ```
199 | ▼ package
200 | storagemarket
201 |
202 | ▶ imports
203 |
204 | ▼ constants
205 | +ErrInsufficientCollateral
206 | +ErrPledgeTooLow
207 | +ErrUnknownMiner
208 |
209 | ▼ variables
210 | +Errors
211 | +MinimumCollateralPerSector
212 | +MinimumPledge
213 | -storageMarketExports
214 |
215 | ▼+Actor : struct
216 | [methods]
217 | // 创建存储矿工
218 | // 会调用到miner actor的创建
219 | +CreateMiner(vmctx exec.VMContext, pledge *big.Int, publicKey []byte, pid peer.ID) : address.Address, uint8, error
220 | +Exports() : exec.Exports
221 | // 获取总存储
222 | +GetTotalStorage(vmctx exec.VMContext) : *big.Int, uint8, error
223 | +InitializeState(storage exec.Storage, _ interface{}) : error
224 | // 更新功率
225 | +UpdatePower(vmctx exec.VMContext, delta *big.Int) : uint8, error
226 |
227 | ▼+State : struct
228 | [fields]
229 | // miners合集的cid
230 | +Miners : cid.Cid
231 | +TotalCommittedStorage : *big.Int
232 |
233 | ▼ functions
234 | +MinimumCollateral(sectors *big.Int) : *types.AttoFIL
235 | // 实例化存储市场
236 | +NewActor() : *actor.Actor, error
237 | -init()
238 | ```
239 |
240 | ### 13.3.3 miner actor
241 | - 提供功能
242 | - 有基本转账功能
243 | - 提供如下功能
244 | - filecoin网络中存在多个Miner Actor
245 |
246 | ```
247 | ▼ package
248 | miner
249 |
250 | ▶ imports
251 |
252 | ▼ constants
253 | +ErrAskNotFound
254 | +ErrCallerUnauthorized
255 | +ErrInsufficientPledge
256 | +ErrInvalidPoSt
257 | +ErrInvalidSealProof
258 | +ErrInvalidSector
259 | +ErrPublicKeyTooBig
260 | +ErrSectorCommitted
261 | +ErrStoragemarketCallFailed
262 | +MaximumPublicKeySize
263 |
264 | ▼ variables
265 | +Errors
266 | +GracePeriodBlocks
267 | +ProvingPeriodBlocks
268 | -minerExports
269 |
270 | ▼+Actor : struct
271 | [fields]
272 | +Bootstrap : bool
273 | [methods]
274 | // 增加订单
275 | +AddAsk(ctx exec.VMContext, price *types.AttoFIL, expiry *big.Int) : *big.Int, uint8, error
276 | // 抵押承诺
277 | +CommitSector(ctx exec.VMContext, sectorID uint64, commD, commR, commRStar, proof []byte) : uint8, error
278 | +Exports() : exec.Exports
279 | // 获取存储矿工相关信息
280 | +GetAsk(ctx exec.VMContext, askid *big.Int) : []byte, uint8, error
281 | +GetAsks(ctx exec.VMContext) : []uint64, uint8, error
282 | +GetKey(ctx exec.VMContext) : []byte, uint8, error
283 | +GetLastUsedSectorID(ctx exec.VMContext) : uint64, uint8, error
284 | +GetOwner(ctx exec.VMContext) : address.Address, uint8, error
285 | +GetPeerID(ctx exec.VMContext) : peer.ID, uint8, error
286 | +GetPledge(ctx exec.VMContext) : *big.Int, uint8, error
287 | +GetPower(ctx exec.VMContext) : *big.Int, uint8, error
288 | +GetProvingPeriodStart(ctx exec.VMContext) : *types.BlockHeight, uint8, error
289 | +GetSectorCommitments(ctx exec.VMContext) : map[string]types.Commitments, uint8, error
290 | +InitializeState(storage exec.Storage, initializerData interface{}) : error
291 | // 提交时空证明
292 | +SubmitPoSt(ctx exec.VMContext, postProofs []proofs.PoStProof) : uint8, error
293 | // 更新节点Id
294 | +UpdatePeerID(ctx exec.VMContext, pid peer.ID) : uint8, error
295 |
296 | // 报价单:价格,时长,序号
297 | ▼+Ask : struct
298 | [fields]
299 | +Expiry : *types.BlockHeight
300 | +ID : *big.Int
301 | +Price : *types.AttoFIL
302 |
303 | // 矿工Actor状态
304 | ▼+State : struct
305 | [fields]
306 | +Asks : []*Ask
307 | +Collateral : *types.AttoFIL
308 | +LastPoSt : *types.BlockHeight
309 | +LastUsedSectorID : uint64
310 | +NextAskID : *big.Int
311 | +Owner : address.Address
312 | +PeerID : peer.ID
313 | +PledgeSectors : *big.Int
314 | +Power : *big.Int
315 | +ProvingPeriodStart : *types.BlockHeight
316 | +PublicKey : []byte
317 | +SectorCommitments : map[string]types.Commitments
318 | [functions]
319 | +NewState(owner address.Address, key []byte, pledge *big.Int, pid peer.ID, collateral *types.AttoFIL) : *State
320 |
321 | ▼ functions
322 | +NewActor() : *actor.Actor
323 | -init()
324 | ```
325 | ### 13.3.4 paymentbroker actor
326 | - 说明
327 | - 全网只有一个paymentbroker
328 | - 几个概念的关系简图
329 |
330 | 
331 |
332 | - 源码分析注释
333 | ```
334 | ▼ package
335 | paymentbroker
336 |
337 | ▼+Actor : struct
338 | [methods]
339 | // 关闭支付通道
340 | +Close(vmctx exec.VMContext, payer address.Address, chid *types.ChannelID, amt *types.AttoFIL, validAt *types.BlockHeight, sig []byte) : uint8, error
341 | // 创建支付通道
342 | +CreateChannel(vmctx exec.VMContext, target address.Address, eol *types.BlockHeight) : *types.ChannelID, uint8, error
343 | +Exports() : exec.Exports
344 | // 增加资金
345 | +Extend(vmctx exec.VMContext, chid *types.ChannelID, eol *types.BlockHeight) : uint8, error
346 | +InitializeState(storage exec.Storage, initializerData interface{}) : error
347 | // 查询某个支付者的信息
348 | +Ls(vmctx exec.VMContext, payer address.Address) : []byte, uint8, error
349 | // 撤回资金
350 | +Reclaim(vmctx exec.VMContext, chid *types.ChannelID) : uint8, error
351 | // 赎回(或者收款)资金
352 | +Redeem(vmctx exec.VMContext, payer address.Address, chid *types.ChannelID, amt *types.AttoFIL, validAt *types.BlockHeight, sig []byte) : uint8, error
353 | // 收据,指明在特定区块高度之前都是有效的
354 | +Voucher(vmctx exec.VMContext, chid *types.ChannelID, amount *types.AttoFIL, validAt *types.BlockHeight) : []byte, uint8, error
355 |
356 | ▼+PaymentChannel : struct
357 | [fields]
358 | // 支付通道内金额
359 | +Amount : *types.AttoFIL
360 | // 已被赎回金额
361 | +AmountRedeemed : *types.AttoFIL
362 | +Eol : *types.BlockHeight
363 | // 收款人地址
364 | +Target : address.Address
365 |
366 | ▼ functions
367 | // 收据的签名及校验
368 | +SignVoucher(channelID *types.ChannelID, amount *types.AttoFIL, validAt *types.BlockHeight, addr address.Address, signer types.Signer) : types.Signature, error
369 | +VerifyVoucherSignature(payer address.Address, chid *types.ChannelID, amt *types.AttoFIL, validAt *types.BlockHeight, sig []byte) : bool
370 | -createVoucherSignatureData(channelID *types.ChannelID, amount *types.AttoFIL, validAt *types.BlockHeight) : []byte
371 | -findByChannelLookup(ctx context.Context, storage exec.Storage, byPayer exec.Lookup, payer address.Address) : exec.Lookup, error
372 | -init()
373 | -reclaim(ctx context.Context, vmctx exec.VMContext, byChannelID exec.Lookup, payer address.Address, chid *types.ChannelID, channel *PaymentChannel) : error
374 | -updateChannel(ctx exec.VMContext, target address.Address, channel *PaymentChannel, amt *types.AttoFIL, validAt *types.BlockHeight) : error
375 | -withPayerChannels(ctx context.Context, storage exec.Storage, payer address.Address, f func(exec.Lookup) error) : error
376 | -withPayerChannelsForReading(ctx context.Context, storage exec.Storage, payer address.Address, f func(exec.Lookup) error) : error
377 | ```
378 |
379 | ### 13.3.5 account actor
380 | - 说明
381 | - 纯账户,记录nonce
382 | - 只有转帐功能
383 | - filecoin网络中存在多个account Actor
384 |
385 | ```
386 | ▼ package
387 | account
388 |
389 | ▶ imports
390 |
391 | ▼ variables
392 | -accountExports
393 |
394 | ▼+Actor : struct
395 | [methods]
396 | +Exports() : exec.Exports
397 | +InitializeState(_ exec.Storage, _ interface{}) : error
398 |
399 | ▼ functions
400 | // 实例化account actor 集成actor包中Actor所实现的所有方法
401 | +NewActor(balance *types.AttoFIL) : *actor.Actor, error
402 | // 将其他actor类型转为account,保留余额
403 | +UpgradeActor(act *actor.Actor) : error
404 | ```
405 |
406 | # 13.4 vm(虚拟机运行环境)
407 |
408 | - 虚拟机执行函数
409 |
410 | ```
411 | ▼ package
412 | vm
413 |
414 | ▶ imports
415 |
416 | ▼-sendDeps : struct
417 | [fields]
418 | -transfer : func(*actor.Actor, *actor.Actor, *types.AttoFIL) error
419 |
420 | ▼ functions
421 | // 执行合约
422 | +Send(ctx context.Context, vmCtx *Context) : [][]byte, uint8, error
423 | // 转账
424 | +Transfer(fromActor, toActor *actor.Actor, value *types.AttoFIL) : error
425 | -send(ctx context.Context, deps sendDeps, vmCtx *Context) : [][]byte, uint8, error
426 | ```
427 | - vm环境实现
428 |
429 | ```
430 | ▼+Context : struct
431 | [fields]
432 | -ancestors : []types.TipSet
433 | -blockHeight : *types.BlockHeight
434 | -deps : *deps
435 | -from : *actor.Actor
436 | -gasTracker : *GasTracker
437 | -lookBack : int
438 | -message : *types.Message
439 | -state : *state.CachedTree
440 | -storageMap : StorageMap
441 | -to : *actor.Actor
442 | [methods]
443 | // 实现上述VMContext 接口,注释见上
444 | +AddressForNewActor() : address.Address, error
445 | +BlockHeight() : *types.BlockHeight
446 | +Charge(cost types.GasUnits) : error
447 | +CreateNewActor(addr address.Address, code cid.Cid, initializerData interface{}) : error
448 | +GasUnits() : types.GasUnits
449 | +IsFromAccountActor() : bool
450 | +Message() : *types.Message
451 | +Rand(sampleHeight *types.BlockHeight) : []byte, error
452 | +ReadStorage() : []byte, error
453 | +Send(to address.Address, method string, value *types.AttoFIL, params []interface{}) : [][]byte, uint8, error
454 | +Storage() : exec.Storage
455 | +WriteStorage(memory interface{}) : error
456 | [functions]
457 | +NewVMContext(params NewContextParams) : *Context
458 |
459 | ▼+NewContextParams : struct
460 | [fields]
461 | +Ancestors : []types.TipSet
462 | +BlockHeight : *types.BlockHeight
463 | +From : *actor.Actor
464 | +GasTracker : *GasTracker
465 | +LookBack : int
466 | +Message : *types.Message
467 | +State : *state.CachedTree
468 | +StorageMap : StorageMap
469 | +To : *actor.Actor
470 |
471 | ▼-deps : struct
472 | [fields]
473 | +EncodeValues : func([]*abi.Value) []byte, error
474 | +GetOrCreateActor : func(context.Context, address.Address, func() *actor.Actor, error) *actor.Actor, error
475 | +Send : func(context.Context, *Context) [][]byte, uint8, error
476 | +ToValues : func([]interface{}) []*abi.Value, error
477 |
478 | ▼ deps* : ctype
479 | [functions]
480 | -makeDeps(st *state.CachedTree) : *deps
481 |
482 | ▼ functions
483 | -computeActorAddress(creator address.Address, nonce uint64) : address.Address, error
484 | ```
485 |
486 | - 合约状态存储
487 |
488 | ```
489 | ▼+Storage : struct
490 | [fields]
491 | -actor : *actor.Actor
492 | -blockstore : blockstore.Blockstore
493 | -chunks : map[cid.Cid]ipld.Node
494 | [methods]
495 | +Commit(newCid cid.Cid, oldCid cid.Cid) : error
496 | +Flush() : error
497 | +Get(cid cid.Cid) : []byte, error
498 | +Head() : cid.Cid
499 | +Prune() : error
500 | +Put(v interface{}) : cid.Cid, error
501 | -liveDescendantIds(id cid.Cid) : *cid.Set, error
502 | [functions]
503 | +NewStorage(bs blockstore.Blockstore, act *actor.Actor) : Storage
504 |
505 | ▼-storageMap : struct
506 | [fields]
507 | -blockstore : blockstore.Blockstore
508 | -storageMap : map[address.Address]Storage
509 | [methods]
510 | +Flush() : error
511 | +NewStorage(addr address.Address, actor *actor.Actor) : Storage
512 |
513 | ▼+StorageMap : interface
514 | [methods]
515 | +Flush() : error
516 | +NewStorage(addr address.Address, actor *actor.Actor) : Storage
517 |
518 | ▼ functions
519 | +NewStorageMap(bs blockstore.Blockstore) : StorageMap
520 | ```
521 |
522 | ## 13.5 state包(actor状态)
523 | - 表征actor的状态
524 |
525 |
--------------------------------------------------------------------------------
/14.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之十四:filecoin源码分析之服务层链同步、共识协议及挖矿
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 14.filecoin源码分析之服务层链同步、共识协议及挖矿
8 | - 14.1 chain
9 | - 14.1.1 基础结构
10 | - 14.1.2 链同步
11 | - 14.1.3 链存储
12 | - 14.2 consensus
13 | - 14.3 mining
14 | - 14.3.1 挖矿的主要逻辑
15 | - 14.3.2 其他细节源码简析
16 |
17 | > 分析基于的源码版本:go-filecoin master a0598a54(2019年3月9日)
18 |
19 | ## 14.1 chain同步
20 |
21 | ### 14.1.1 基础结构
22 |
23 | - TipIndex 定义定义了tipset的基础结构及方法
24 |
25 | ```
26 | ▼ package
27 | chain
28 |
29 | ▼+TipIndex : struct
30 | [fields]
31 | -mu : sync.Mutex
32 | // 根据id来获取tipset及其状态根
33 | -tsasByID : tsasByTipSetID
34 | // 根据父块来获取tipset及其状态根
35 | -tsasByParentsAndHeight : map[string]tsasByTipSetID
36 |
37 | [methods]
38 | // 根据id来获取tipset及其状态根
39 | +Get(tsKey string) : *TipSetAndState, error
40 | // 根据父块来获取tipset及其状态根
41 | +GetByParentsAndHeight(pKey string, h uint64) : []*TipSetAndState, error
42 | // 根据Id判断是否有此tipset
43 | +Has(tsKey string) : bool
44 | // 根据父块判断是否有此tipset
45 | +HasByParentsAndHeight(pKey string, h uint64) : bool
46 | // 设置tipset和状态根
47 | +Put(tsas *TipSetAndState) : error
48 | [functions]
49 | +NewTipIndex() : *TipIndex
50 |
51 | ▼+TipSetAndState : struct
52 | [fields]
53 | // tipset
54 | +TipSet : types.TipSet
55 | // 相当于区块的root cid
56 | +TipSetStateRoot : cid.Cid
57 | ```
58 |
59 | ### 14.1.2 链同步
60 |
61 | - chain同步的接口定义
62 |
63 | ```
64 | location: chain/syncer.go
65 |
66 | ▼ package
67 | chain
68 |
69 | ▼+Syncer : interface
70 | [methods]
71 | // 处理新区块的接口定义
72 | +HandleNewBlocks(ctx context.Context, blkCids []cid.Cid) : error
73 |
74 | 具体接口实现在location: chain/defalut_syncer.go中
75 | ```
76 |
77 | - 特殊情况的错误
78 |
79 | ```
80 | location: chain/reorg.go
81 |
82 | // 如果当前区块头不包含在最新的区块头之上时候,会报此错误
83 | ▼ functions
84 | +IsReorg(curHead types.TipSet, newChain []types.TipSet) : bool
85 | ```
86 |
87 | ### 14.1.3 链存储
88 |
89 | - 其中
90 | - Readstore是一个通用接口
91 | - Store的设计基本是给ChainSync使用的
92 |
93 | ```
94 | location: chain/store.go
95 |
96 | ▼ package
97 | chain
98 |
99 | ▼ constants
100 | // 用于发布新的区块头的主题"new-head"
101 | +NewHeadTopic
102 |
103 | ▼ variables
104 | // 创世块的key
105 | +GenesisKey
106 |
107 | ▼+ReadStore : interface
108 | [methods]
109 | // 获取历史区块,通过channel实现
110 | +BlockHistory(ctx context.Context, tips types.TipSet) : chan interface{}
111 | // 获取创世区块cid
112 | +GenesisCid() : cid.Cid
113 | // 通过cid获取具体的block
114 | +GetBlock(ctx context.Context, id cid.Cid) : *types.Block, error
115 | // 通过cid获取具体的block
116 | +GetTipSetAndState(ctx context.Context, tsKey string) : *TipSetAndState, error
117 | // 获取最新区块
118 | +Head() : types.TipSet
119 | // 最新区块变更事件
120 | +HeadEvents() : *pubsub.PubSub
121 | // 最新合约状态
122 | +LatestState(ctx context.Context) : state.Tree, error
123 | // 加载chain
124 | +Load(ctx context.Context) : error
125 | // 停止
126 | +Stop()
127 |
128 | // 这个接口只是chain同步使用
129 | ▼+Store : interface
130 | [embedded]
131 | +ReadStore
132 | [methods]
133 | +GetBlocks(ctx context.Context, ids types.SortedCidSet) : []*types.Block, error
134 | +GetTipSetAndStatesByParentsAndHeight(ctx context.Context, pTsKey string, h uint64) : []*TipSetAndState, error
135 | +HasAllBlocks(ctx context.Context, cs []cid.Cid) : bool
136 | +HasBlock(ctx context.Context, c cid.Cid) : bool
137 | +HasTipSetAndState(ctx context.Context, tsKey string) : bool
138 | +HasTipSetAndStatesWithParentsAndHeight(ctx context.Context, pTsKey string, h uint64) : bool
139 | // 存储并更新最新区块信息
140 | +PutTipSetAndState(ctx context.Context, tsas *TipSetAndState) : error
141 | +SetHead(ctx context.Context, s types.TipSet) : error
142 | ```
143 |
144 | ## 14.2 consensus
145 | - 主要功能
146 | - 提供创建选票方法,验证中奖选票方法,确定最终的tipset
147 | - 将合法的tipset消息取出,生效actor状态
148 |
149 | ```
150 | ▼ package
151 | consensus
152 |
153 | ▶ imports
154 |
155 | ▼ constants
156 | +ECPrM : uint64
157 | +ECV : uint64
158 | +LookBackParameter
159 |
160 | ▼ variables
161 | +AncestorRoundsNeeded
162 | +ErrInvalidBase
163 | +ErrStateRootMismatch
164 | +ErrUnorderedTipSets
165 | -log
166 | -ticketDomain : *big.Int
167 |
168 | // Expected实现EC共识
169 | ▼+Expected : struct
170 | [fields]
171 | // 全局功率表
172 | +PwrTableView : PowerTableView
173 | -bstore : blockstore.Blockstore
174 | -cstore : *hamt.CborIpldStore
175 | -genesisCid : cid.Cid
176 | -processor : Processor
177 | -verifier : proofs.Verifier
178 | [methods]
179 | // 比较两个tipset的权重
180 | +IsHeavier(ctx context.Context, a, b types.TipSet, aSt, bSt state.Tree) : bool, error
181 | // 建立新的tipset
182 | +NewValidTipSet(ctx context.Context, blks []*types.Block) : types.TipSet, error
183 | // 运行状态转换
184 | // 1 新区块到来的时候出发状态转换(chain sync逻辑)
185 | // 2 进入后判断tipset的有效性,包括验证选票是否中奖
186 | // 3 逐一执行消息,切换状态
187 | +RunStateTransition(ctx context.Context, ts types.TipSet, ancestors []types.TipSet, pSt state.Tree) : state.Tree, error
188 | // 计算tipset权重
189 | +Weight(ctx context.Context, ts types.TipSet, pSt state.Tree) : uint64, error
190 | -runMessages(ctx context.Context, st state.Tree, vms vm.StorageMap, ts types.TipSet, ancestors []types.TipSet) : state.Tree, error
191 | -validateBlockStructure(ctx context.Context, b *types.Block) : error
192 | -validateMining(ctx context.Context, st state.Tree, ts types.TipSet, parentTs types.TipSet) : error
193 |
194 | ▼+Processor : interface
195 | // 会被RunStateTransition间接掉用,进行状态切换(生效挖矿成功的tipset消息)
196 | [methods]
197 | // 从tipset中逐一取出block处理
198 | +ProcessBlock(ctx context.Context, st state.Tree, vms vm.StorageMap, blk *types.Block, ancestors []types.TipSet) : []*ApplicationResult, error
199 | +ProcessTipSet(ctx context.Context, st state.Tree, vms vm.StorageMap, ts types.TipSet, ancestors []types.TipSet) : *ProcessTipSetResponse, error
200 |
201 | ▼ functions
202 | // 与白皮书描述一致,按照存储功率出块,用以判断是否中奖
203 | +CompareTicketPower(ticket types.Signature, minerPower uint64, totalPower uint64) : bool
204 | // 产生随机挑战种子,针对时空证明
205 | +CreateChallengeSeed(parents types.TipSet, nullBlkCount uint64) : proofs.PoStChallengeSeed, error
206 | // 生成选票
207 | // 用上一个区块的时空证明+矿工地址(目前直接用的矿工地址,issue1054讨论中) 生成256bit哈希
208 | +CreateTicket(proof proofs.PoStProof, minerAddr address.Address) : []byte
209 | // 判断是否中奖,调用CompareTicketPower
210 | +IsWinningTicket(ctx context.Context, bs blockstore.Blockstore, ptv PowerTableView, st state.Tree, ticket types.Signature, miner address.Address) : bool, error
211 | // 实例化Expected
212 | +NewExpected(cs *hamt.CborIpldStore, bs blockstore.Blockstore, processor Processor, pt PowerTableView, gCid cid.Cid, verifier proofs.Verifier) : Protocol
213 | -init()
214 |
215 | ```
216 |
217 | ## 14.3 mining
218 |
219 | ### 14.3.1 挖矿的主要逻辑
220 |
221 | - 1 不能将空块最为基准块
222 | - 2 基于上一个Tipset信息(如果上一个为空块,必须找到空块之前高度最高的Tipset,并记录中间空块数据)和空块数母生成合法的时空证明挑战参数
223 | - 3 生成时空证明
224 | - 4 时空证明成功,调用共识协议创建奖票
225 | - 5 如果奖票中奖,将未打包的消息打包区块
226 |
227 | ```
228 | location: mining/working
229 |
230 | //这里是挖矿逻辑的真正入口
231 |
232 | // Mine implements the DefaultWorkers main mining function..
233 | // The returned bool indicates if this miner created a new block or not.
234 | func (w *DefaultWorker) Mine(ctx context.Context, base types.TipSet, nullBlkCount int, outCh chan<- Output) bool {
235 | log.Info("Worker.Mine")
236 | ctx = log.Start(ctx, "Worker.Mine")
237 | defer log.Finish(ctx)
238 | // 不能将空块作为基准块挖矿
239 | if len(base) == 0 {
240 | log.Warning("Worker.Mine returning because it can't mine on an empty tipset")
241 | outCh <- Output{Err: errors.New("bad input tipset with no blocks sent to Mine()")}
242 | return false
243 | }
244 |
245 | st, err := w.getStateTree(ctx, base)
246 | if err != nil {
247 | log.Errorf("Worker.Mine couldn't get state tree for tipset: %s", err.Error())
248 | outCh <- Output{Err: err}
249 | return false
250 | }
251 |
252 | log.Debugf("Mining on tipset: %s, with %d null blocks.", base.String(), nullBlkCount)
253 | if ctx.Err() != nil {
254 | log.Warningf("Worker.Mine returning with ctx error %s", ctx.Err().Error())
255 | return false
256 | }
257 |
258 | // 基于上一个基准Tipset以及空块数目生成Post随机挑战参数
259 | challenge, err := consensus.CreateChallengeSeed(base, uint64(nullBlkCount))
260 | if err != nil {
261 | outCh <- Output{Err: err}
262 | return false
263 | }
264 |
265 | // 生成时空证明
266 | prCh := createProof(challenge, w.createPoSTFunc)
267 |
268 | var proof proofs.PoStProof
269 | var ticket []byte
270 | select {
271 | case <-ctx.Done():
272 | log.Infof("Mining run on base %s with %d null blocks canceled.", base.String(), nullBlkCount)
273 | return false
274 | case prChRead, more := <-prCh:
275 | if !more {
276 | log.Errorf("Worker.Mine got zero value from channel prChRead")
277 | return false
278 | }
279 | copy(proof[:], prChRead[:])
280 | // 时空证明成功,调用共识协议创建奖票
281 | ticket = consensus.CreateTicket(proof, w.minerAddr)
282 | }
283 |
284 | // TODO: Test the interplay of isWinningTicket() and createPoSTFunc()
285 | // https://github.com/filecoin-project/go-filecoin/issues/1791
286 | // 调用共识协议确认是否中奖
287 | weHaveAWinner, err := consensus.IsWinningTicket(ctx, w.blockstore, w.powerTable, st, ticket, w.minerAddr)
288 |
289 | if err != nil {
290 | log.Errorf("Worker.Mine couldn't compute ticket: %s", err.Error())
291 | outCh <- Output{Err: err}
292 | return false
293 | }
294 |
295 | if weHaveAWinner {
296 | // 如果中奖将打包消息,生成区块
297 | next, err := w.Generate(ctx, base, ticket, proof, uint64(nullBlkCount))
298 | if err == nil {
299 | log.SetTag(ctx, "block", next)
300 | log.Debugf("Worker.Mine generates new winning block! %s", next.Cid().String())
301 | }
302 | outCh <- NewOutput(next, err)
303 | return true
304 | }
305 |
306 | return false
307 | }
308 | ```
309 |
310 | ### 14.3.2 其他细节源码简析
311 |
312 | - 消息队列(交易消息集)的处理
313 |
314 | ```
315 | location: mining/mqueue.go
316 |
317 | ▼ package
318 | mining
319 |
320 | ▶ imports
321 |
322 | ▼+MessageQueue : struct
323 | [fields]
324 | -senderQueues : queueHeap
325 | [methods]
326 | // 取出消息切片,即多条消息
327 | +Drain() : []*types.SignedMessage
328 | +Empty() : bool
329 | // 从队列取出一条消息
330 | +Pop() : *types.SignedMessage, bool
331 | [functions]
332 | // 实例化消息队列
333 | +NewMessageQueue(msgs []*types.SignedMessage) : MessageQueue
334 |
335 | -nonceQueue : []*types.SignedMessage
336 |
337 | // 一些队列的基本操作
338 | // 1 长度、push、pop功能
339 | // 2 Less主要是比较两条交易中的Gas价格,大家可以回头看看type中的消息定义,这里不赘述了
340 | // 3 为什么要提供Less接口,留给大家思索一下,熟悉以太坊的可能一眼就看出了
341 | ▼-queueHeap : []nonceQueue
342 | [methods]
343 | +Len() : int
344 | +Less(i, j int) : bool
345 | +Pop() : interface{}
346 | +Push(x interface{})
347 | +Swap(i, j int)
348 | ```
349 |
350 | - 调度器
351 | - 入口
352 | - node实例会调用NewScheduler创建相关实例并启动挖矿
353 |
354 | ```
355 | ▼ package
356 | mining
357 |
358 | ▶ imports
359 |
360 | ▼ constants
361 | +MineDelayConversionFactor
362 |
363 | ▼-timingScheduler : struct
364 | [fields]
365 | -isStarted : bool
366 | -mineDelay : time.Duration
367 | // 查找权重最高的Tipset
368 | -pollHeadFunc : func() types.TipSet
369 | // 底层的挖矿逻辑,在下面会分析Worker
370 | -worker : Worker
371 | [methods]
372 | // 判断是否启动挖矿
373 | +IsStarted() : bool
374 | // 启动挖矿
375 | +Start(miningCtx context.Context) : chan Output, *sync.WaitGroup
376 |
377 | ▼+Scheduler : interface
378 | [methods]
379 | +IsStarted() : bool
380 | +Start(miningCtx context.Context) : chan Output, *sync.WaitGroup
381 |
382 | ▼ functions
383 | +MineOnce(ctx context.Context, w Worker, md time.Duration, ts types.TipSet) : Output, error
384 | // 实例化timingScheduler
385 | +NewScheduler(w Worker, md time.Duration, f func() types.TipSet) : Scheduler
386 | -nextNullBlkCount(prevNullBlkCount int, prevBase, currBase types.TipSet) : int
387 | ```
388 |
389 | - 打包区块
390 | - 具体见如下注释,可对应此查阅源码。
391 |
392 | ```
393 | location: mining/block_generate.go
394 |
395 | ▼ package
396 | mining
397 |
398 | ▶ imports
399 |
400 | ▼ DefaultWorker* : ctype
401 | [methods]
402 | // 1 如果节点没有产生过有效存储,无法参与挖矿
403 | // 2 计算区块高度= 基准Tipset高度+空块数目
404 | // 3 取出未打包消息,调用vm执行,生成收据,并更新状态
405 | // 4 打包区块信息,返回
406 | +Generate(ctx context.Context, baseTipSet types.TipSet, ticket types.Signature, proof proofs.PoStProof, nullBlockCount uint64) : *types.Block, error
407 | ```
408 |
409 |
--------------------------------------------------------------------------------
/15.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之十五:filecoin源码分析之节点运行逻辑
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 15.filecoin源码分析之节点运行逻辑
8 | - 15.1 前提
9 | - 15.2 filecoin节点运行逻辑简析
10 | - 15.2.1 基本数据结构
11 | - 15.2.2 创建filecoin节点实例
12 | - 15.2.3 启动及停止filecoin节点
13 | - 15.2.4 启动及停止挖矿
14 | - 15.3 阶段性分析结束说明
15 |
16 |
17 | > 分析基于的源码版本:go-filecoin master a0598a54(2019年3月9日)
18 |
19 | ## 15.1 前提
20 |
21 | - 我们在前面的章节已经经过了三个阶段的梳理分析
22 | - 概念阶段,包括概念、通用语言理解、开发网络使用
23 | - 顶层架构与概念的结合理解
24 | - 具体源码的简析,包括协议层、支撑包、内部api层、服务层
25 |
26 | - 源码部分的command部分比较容易理解,就不单独文章赘述了,基本与内部api层都可以对应起来
27 |
28 | - 现在再来看节点的运行逻辑应该会更加清晰了
29 |
30 | ## 15.2 filecoin节点运行逻辑简析
31 |
32 | ### 15.2.1 基本数据结构
33 |
34 | ```
35 | ▼ package
36 | node
37 |
38 | ▶ imports
39 |
40 | ▼ variables
41 | +ErrNoMinerAddress
42 | -filecoinDHTProtocol : dhtprotocol.ID
43 | -log
44 |
45 | // 创建具体的filecoin节点实例
46 | ▼+Config : struct
47 | [fields]
48 | // 设置区块时间
49 | +BlockTime : time.Duration
50 | // 配置节点是否转发
51 | +IsRelay : bool
52 | // libp2p选项
53 | +Libp2pOpts : []libp2p.Option
54 | // 在离线模式下,会关闭libp2p
55 | +OfflineMode : bool
56 | // 配置资源
57 | +Repo : repo.Repo
58 | // 配置区块奖励方法
59 | +Rewarder : consensus.BlockRewarder
60 | // 配置节点时空证明校验函数
61 | +Verifier : proofs.Verifier
62 | [methods]
63 | // 创建node实例
64 | +Build(ctx context.Context) : *Node, error
65 | -buildHost(ctx context.Context, makeDHT func(host host.Host) routing.IpfsRouting, error) : host.Host, error
66 |
67 | +ConfigOpt : func(*Config) error
68 |
69 | ▼+Node : struct
70 | [fields]
71 | // 确认最新区块,本地持久化并广播
72 | +AddNewlyMinedBlock : newBlockFunc
73 | // 订阅主题"/fil/blocks"
74 | +BlockSub : pubsub.Subscription
75 | // 块服务接口
76 | +Blockstore : bstore.Blockstore
77 | // 维持相关节点连接
78 | +Bootstrapper : *net.Bootstrapper
79 | // 读取区块信息
80 | +ChainReader : chain.ReadStore
81 | // 同时协议
82 | +Consensus : consensus.Protocol
83 | // 块交换,节点间的数据交换
84 | +Exchange : exchange.Interface
85 | // new-head 主题
86 | +HeaviestTipSetCh : chan interface{}
87 | // 新区块处理请求
88 | +HeaviestTipSetHandled : func()
89 | // hello服务
90 | +HelloSvc : *hello.Handler
91 | // 消息订阅
92 | +MessageSub : pubsub.Subscription
93 | // 挖矿调度
94 | +MiningScheduler : mining.Scheduler
95 | // 消息池操作
96 | +MsgPool : *core.MessagePool
97 | // 离线模式
98 | +OfflineMode : bool
99 | +OnlineStore : *hamt.CborIpldStore
100 | // 对应libp2p中的host
101 | +PeerHost : host.Host
102 | // libp2p中的ping service
103 | +Ping : *ping.PingService
104 | // 高层api
105 | +PorcelainAPI : *porcelain.API
106 | // 功率表
107 | +PowerTable : consensus.PowerTableView
108 | // 配置资源
109 | +Repo : repo.Repo
110 | // 检索客户端
111 | +RetrievalClient : *retrieval.Client
112 | // 检索矿工
113 | +RetrievalMiner : *retrieval.Miner
114 | // 路由,libp2p
115 | +Router : routing.IpfsRouting
116 | // 存储矿工
117 | +StorageMiner : *storage.Miner
118 | // 存储客户
119 | +StorageMinerClient : *storage.Client
120 | // 链同步
121 | +Syncer : chain.Syncer
122 | // 钱包管理
123 | +Wallet : *wallet.Wallet
124 | -blockTime : time.Duration
125 | -blockservice : bserv.BlockService
126 | -cancelMining : context.CancelFunc
127 | -cancelSubscriptionsCtx : context.CancelFunc
128 | -cborStore : *hamt.CborIpldStore
129 | -host : host.Host
130 | -lookup : lookup.PeerLookupService
131 | -mining
132 | -miningCtx : context.Context
133 | -miningDoneWg : *sync.WaitGroup
134 | -sectorBuilder : sectorbuilder.SectorBuilder
135 |
136 | [methods]
137 | +BlockHeight() : *types.BlockHeight, error
138 | +BlockService() : bserv.BlockService
139 | +CborStore() : *hamt.CborIpldStore
140 | +ChainReadStore() : chain.ReadStore
141 | // 创建矿工方法
142 | +CreateMiner(ctx context.Context, accountAddr address.Address, gasPrice types.AttoFIL, gasLimit types.GasUnits, pledge uint64, pid libp2ppeer.ID, collateral *types.AttoFIL) : *address.Address, error
143 | +GetBlockTime() : time.Duration
144 | +Host() : host.Host
145 | // 节点查找方法
146 | +Lookup() : lookup.PeerLookupService
147 | +MiningSignerAddress() : address.Address
148 | +MiningTimes() : time.Duration, time.Duration
149 | // 创建新的account地址,钱包地址
150 | +NewAddress() : address.Address, error
151 | +SectorBuilder() : sectorbuilder.SectorBuilder
152 | +SetBlockTime(blockTime time.Duration)
153 | // 启动节点
154 | +Start(ctx context.Context) : error
155 | // 启动挖矿
156 | +StartMining(ctx context.Context) : error
157 | // 停止节点
158 | +Stop(ctx context.Context)
159 | // 停止挖矿
160 | +StopMining(ctx context.Context)
161 | -addNewlyMinedBlock(ctx context.Context, b *types.Block)
162 | -cancelSubscriptions()
163 | -getLastUsedSectorID(ctx context.Context, minerAddr address.Address) : uint64, error
164 | -getMinerActorPubKey() : []byte, error
165 | -handleNewHeaviestTipSet(ctx context.Context, head types.TipSet)
166 | -handleNewMiningOutput(miningOutCh chan mining.Output)
167 | -handleSubscription(ctx context.Context, f pubSubProcessorFunc, fname string, s pubsub.Subscription, sname string)
168 | -isMining() : bool
169 | -miningAddress() : address.Address, error
170 | -miningOwnerAddress(ctx context.Context, miningAddr address.Address) : address.Address, error
171 | -saveMinerConfig(minerAddr address.Address, signerAddr address.Address) : error
172 | -setIsMining(isMining bool)
173 | -setupHeartbeatServices(ctx context.Context) : error
174 | -setupMining(ctx context.Context) : error
175 | [functions]
176 | // 调用Build创建node实例
177 | +New(ctx context.Context, opts ...ConfigOpt) : *Node, error
178 |
179 | ▼-blankValidator : struct
180 | [methods]
181 | +Select(_ string, _ [][]byte) : int, error
182 | +Validate(_ string, _ []byte) : error
183 |
184 | -newBlockFunc : func(context.Context, *types.Block)
185 |
186 | -pubSubProcessorFunc : func(ctx context.Context, msg pubsub.Message) error
187 |
188 | ▼ functions
189 | +BlockTime(blockTime time.Duration) : ConfigOpt
190 | +IsRelay() : ConfigOpt
191 | +Libp2pOptions(opts ...libp2p.Option) : ConfigOpt
192 | +OfflineMode(offlineMode bool) : ConfigOpt
193 | +RewarderConfigOption(rewarder consensus.BlockRewarder) : ConfigOpt
194 | +StartMining(ctx context.Context, node *Node) : error
195 | +VerifierConfigOption(verifier proofs.Verifier) : ConfigOpt
196 | -initSectorBuilderForNode(ctx context.Context, node *Node, sectorStoreType proofs.SectorStoreType) : sectorbuilder.SectorBuilder, error
197 | -initStorageMinerForNode(ctx context.Context, node *Node) : *storage.Miner, error
198 | -readGenesisCid(ds datastore.Datastore) : cid.Cid, error
199 | ```
200 |
201 | ### 15.2.2 创建filecoin节点实例
202 |
203 | - 实例化filecoin节点,简析见如下添加的注释
204 |
205 | ```
206 | // Build instantiates a filecoin Node from the settings specified in the config.
207 | func (nc *Config) Build(ctx context.Context) (*Node, error) {
208 | // 创建内存资源实例
209 | if nc.Repo == nil {
210 | nc.Repo = repo.NewInMemoryRepo()
211 | }
212 |
213 | // 创建块服务实例
214 | bs := bstore.NewBlockstore(nc.Repo.Datastore())
215 |
216 | validator := blankValidator{}
217 |
218 | var peerHost host.Host
219 | var router routing.IpfsRouting
220 |
221 | // 带宽统计实例,加入libp2popts
222 | bandwidthTracker := p2pmetrics.NewBandwidthCounter()
223 | nc.Libp2pOpts = append(nc.Libp2pOpts, libp2p.BandwidthReporter(bandwidthTracker))
224 |
225 | // 非离线模式才启用libp2p
226 | if !nc.OfflineMode {
227 | makeDHT := func(h host.Host) (routing.IpfsRouting, error) {
228 | r, err := dht.New(
229 | ctx,
230 | h,
231 | dhtopts.Datastore(nc.Repo.Datastore()),
232 | dhtopts.NamespacedValidator("v", validator),
233 | dhtopts.Protocols(filecoinDHTProtocol),
234 | )
235 | if err != nil {
236 | return nil, errors.Wrap(err, "failed to setup routing")
237 | }
238 | router = r
239 | return r, err
240 | }
241 |
242 | var err error
243 | // 实例化非离线模式libp2p host
244 | peerHost, err = nc.buildHost(ctx, makeDHT)
245 | if err != nil {
246 | return nil, err
247 | }
248 | } else {
249 | // 离线模式处理
250 | router = offroute.NewOfflineRouter(nc.Repo.Datastore(), validator)
251 | peerHost = rhost.Wrap(noopLibP2PHost{}, router)
252 | }
253 |
254 | // ping服务实例
255 | // set up pinger
256 | pinger := ping.NewPingService(peerHost)
257 |
258 | // bitswap实例
259 | // set up bitswap
260 | nwork := bsnet.NewFromIpfsHost(peerHost, router)
261 | //nwork := bsnet.NewFromIpfsHost(innerHost, router)
262 | bswap := bitswap.New(ctx, nwork, bs)
263 | bservice := bserv.New(bs, bswap)
264 |
265 | cstOnline := hamt.CborIpldStore{Blocks: bservice}
266 | cstOffline := hamt.CborIpldStore{Blocks: bserv.New(bs, offline.Exchange(bs))}
267 | // 获取创世块cid
268 | genCid, err := readGenesisCid(nc.Repo.Datastore())
269 | if err != nil {
270 | return nil, err
271 | }
272 |
273 | // chain.Store实例以及功率表
274 | var chainStore chain.Store = chain.NewDefaultStore(nc.Repo.ChainDatastore(), &cstOffline, genCid)
275 | powerTable := &consensus.MarketView{}
276 |
277 | // 共识协议processor实例
278 | var processor consensus.Processor
279 | if nc.Rewarder == nil {
280 | processor = consensus.NewDefaultProcessor()
281 | } else {
282 | processor = consensus.NewConfiguredProcessor(consensus.NewDefaultMessageValidator(), nc.Rewarder)
283 | }
284 |
285 | // 共识协议实例
286 | var nodeConsensus consensus.Protocol
287 | if nc.Verifier == nil {
288 | nodeConsensus = consensus.NewExpected(&cstOffline, bs, processor, powerTable, genCid, &proofs.RustVerifier{})
289 | } else {
290 | nodeConsensus = consensus.NewExpected(&cstOffline, bs, processor, powerTable, genCid, nc.Verifier)
291 | }
292 |
293 | // 链同步,链读取,消息池实例
294 | // only the syncer gets the storage which is online connected
295 | chainSyncer := chain.NewDefaultSyncer(&cstOnline, &cstOffline, nodeConsensus, chainStore)
296 | chainReader, ok := chainStore.(chain.ReadStore)
297 | if !ok {
298 | return nil, errors.New("failed to cast chain.Store to chain.ReadStore")
299 | }
300 | msgPool := core.NewMessagePool()
301 |
302 | // Set up libp2p pubsub
303 | fsub, err := libp2pps.NewFloodSub(ctx, peerHost)
304 | if err != nil {
305 | return nil, errors.Wrap(err, "failed to set up pubsub")
306 | }
307 |
308 | // 钱包服务实例
309 | backend, err := wallet.NewDSBackend(nc.Repo.WalletDatastore())
310 | if err != nil {
311 | return nil, errors.Wrap(err, "failed to set up wallet backend")
312 | }
313 | fcWallet := wallet.New(backend)
314 |
315 | // 实例化高层api
316 | PorcelainAPI := porcelain.New(plumbing.New(&plumbing.APIDeps{
317 | Chain: chainReader,
318 | Config: cfg.NewConfig(nc.Repo),
319 | Deals: strgdls.New(nc.Repo.DealsDatastore()),
320 | MsgPool: msgPool,
321 | MsgPreviewer: msg.NewPreviewer(fcWallet, chainReader, &cstOffline, bs),
322 | MsgQueryer: msg.NewQueryer(nc.Repo, fcWallet, chainReader, &cstOffline, bs),
323 | MsgSender: msg.NewSender(fcWallet, chainReader, msgPool, consensus.NewOutboundMessageValidator(), fsub.Publish),
324 | MsgWaiter: msg.NewWaiter(chainReader, bs, &cstOffline),
325 | Network: net.New(peerHost, pubsub.NewPublisher(fsub), pubsub.NewSubscriber(fsub), net.NewRouter(router), bandwidthTracker),
326 | SigGetter: mthdsig.NewGetter(chainReader),
327 | Wallet: fcWallet,
328 | }))
329 |
330 | // 实例化node
331 | nd := &Node{
332 | blockservice: bservice,
333 | Blockstore: bs,
334 | cborStore: &cstOffline,
335 | OnlineStore: &cstOnline,
336 | Consensus: nodeConsensus,
337 | ChainReader: chainReader,
338 | Syncer: chainSyncer,
339 | PowerTable: powerTable,
340 | PorcelainAPI: PorcelainAPI,
341 | Exchange: bswap,
342 | host: peerHost,
343 | MsgPool: msgPool,
344 | OfflineMode: nc.OfflineMode,
345 | PeerHost: peerHost,
346 | Ping: pinger,
347 | Repo: nc.Repo,
348 | Wallet: fcWallet,
349 | blockTime: nc.BlockTime,
350 | Router: router,
351 | }
352 |
353 | // Bootstrapping network peers.
354 | periodStr := nd.Repo.Config().Bootstrap.Period
355 | period, err := time.ParseDuration(periodStr)
356 | if err != nil {
357 | return nil, errors.Wrapf(err, "couldn't parse bootstrap period %s", periodStr)
358 | }
359 |
360 | // 实例化Bootstrapper,指定node的该方法
361 | // Bootstrapper maintains connections to some subset of addresses
362 | ba := nd.Repo.Config().Bootstrap.Addresses
363 | bpi, err := net.PeerAddrsToPeerInfos(ba)
364 | if err != nil {
365 | return nil, errors.Wrapf(err, "couldn't parse bootstrap addresses [%s]", ba)
366 | }
367 | minPeerThreshold := nd.Repo.Config().Bootstrap.MinPeerThreshold
368 | nd.Bootstrapper = net.NewBootstrapper(bpi, nd.Host(), nd.Host().Network(), nd.Router, minPeerThreshold, period)
369 |
370 | // 实例化链查找服务,指定node的该方法
371 | // On-chain lookup service
372 | defaultAddressGetter := func() (address.Address, error) {
373 | return nd.PorcelainAPI.GetAndMaybeSetDefaultSenderAddress()
374 | }
375 | nd.lookup = lookup.NewChainLookupService(nd.ChainReader, defaultAddressGetter, bs)
376 |
377 | return nd, nil
378 | }
379 | ```
380 |
381 | ### 15.2.3 启动及停止filecoin节点
382 |
383 | - 启动filecoin节点的流程概览
384 |
385 | ```
386 | // Start boots up the node.
387 | func (node *Node) Start(ctx context.Context) error {
388 | // 加载本地chain信息
389 | if err := node.ChainReader.Load(ctx); err != nil {
390 | return err
391 | }
392 |
393 | // 如果存在存储矿工,配置挖矿功能
394 | // Only set these up if there is a miner configured.
395 | if _, err := node.miningAddress(); err == nil {
396 | if err := node.setupMining(ctx); err != nil {
397 | log.Errorf("setup mining failed: %v", err)
398 | return err
399 | }
400 | }
401 |
402 | // 设置链同步回调函数
403 | // Start up 'hello' handshake service
404 | syncCallBack := func(pid libp2ppeer.ID, cids []cid.Cid, height uint64) {
405 | // TODO it is possible the syncer interface should be modified to
406 | // make use of the additional context not used here (from addr + height).
407 | // To keep things simple for now this info is not used.
408 | err := node.Syncer.HandleNewBlocks(context.Background(), cids)
409 | if err != nil {
410 | log.Infof("error handling blocks: %s", types.NewSortedCidSet(cids...).String())
411 | }
412 | }
413 | // 实例化hello握手协议
414 | node.HelloSvc = hello.New(node.Host(), node.ChainReader.GenesisCid(), syncCallBack, node.ChainReader.Head)
415 |
416 | // 实例化存储矿工协议
417 | cni := storage.NewClientNodeImpl(dag.NewDAGService(node.BlockService()), node.Host(), node.GetBlockTime())
418 | var err error
419 | node.StorageMinerClient, err = storage.NewClient(cni, node.PorcelainAPI)
420 | if err != nil {
421 | return errors.Wrap(err, "Could not make new storage client")
422 | }
423 |
424 | // 实例化检索客户及检索矿工协议
425 | node.RetrievalClient = retrieval.NewClient(node)
426 | node.RetrievalMiner = retrieval.NewMiner(node)
427 |
428 | // 订阅区块通知
429 | // subscribe to block notifications
430 | blkSub, err := node.PorcelainAPI.PubSubSubscribe(BlockTopic)
431 | if err != nil {
432 | return errors.Wrap(err, "failed to subscribe to blocks topic")
433 | }
434 | node.BlockSub = blkSub
435 |
436 | // 订阅消息通知
437 | // subscribe to message notifications
438 | msgSub, err := node.PorcelainAPI.PubSubSubscribe(msg.Topic)
439 | if err != nil {
440 | return errors.Wrap(err, "failed to subscribe to message topic")
441 | }
442 | node.MessageSub = msgSub
443 |
444 | cctx, cancel := context.WithCancel(context.Background())
445 | node.cancelSubscriptionsCtx = cancel
446 |
447 | // 启用新线程订阅区块及消息主题,设置handle回调
448 | go node.handleSubscription(cctx, node.processBlock, "processBlock", node.BlockSub, "BlockSub")
449 | go node.handleSubscription(cctx, node.processMessage, "processMessage", node.MessageSub, "MessageSub")
450 |
451 | // 启用新线程处理新的tipset事件
452 | node.HeaviestTipSetHandled = func() {}
453 | node.HeaviestTipSetCh = node.ChainReader.HeadEvents().Sub(chain.NewHeadTopic)
454 | go node.handleNewHeaviestTipSet(cctx, node.ChainReader.Head())
455 |
456 | // 非离线模式启动bootstapper服务
457 | if !node.OfflineMode {
458 | node.Bootstrapper.Start(context.Background())
459 | }
460 |
461 | // 启动心跳服务
462 | if err := node.setupHeartbeatServices(ctx); err != nil {
463 | return errors.Wrap(err, "failed to start heartbeat services")
464 | }
465 |
466 | return nil
467 | }
468 | ```
469 |
470 | - 停止filecoin节点的流程概览
471 |
472 | > 释放资源,停止相关服务
473 |
474 | ```
475 | // Stop initiates the shutdown of the node.
476 | func (node *Node) Stop(ctx context.Context) {
477 | node.ChainReader.HeadEvents().Unsub(node.HeaviestTipSetCh)
478 | // 停止挖矿
479 | node.StopMining(ctx)
480 |
481 | // 取消订阅
482 | node.cancelSubscriptions()
483 | // 停止链读取服务
484 | node.ChainReader.Stop()
485 |
486 | // 停止密封服务
487 | if node.SectorBuilder() != nil {
488 | if err := node.SectorBuilder().Close(); err != nil {
489 | fmt.Printf("error closing sector builder: %s\n", err)
490 | }
491 | node.sectorBuilder = nil
492 | }
493 |
494 | // 关闭host实例
495 | if err := node.Host().Close(); err != nil {
496 | fmt.Printf("error closing host: %s\n", err)
497 | }
498 |
499 | // 关闭资源实例
500 | if err := node.Repo.Close(); err != nil {
501 | fmt.Printf("error closing repo: %s\n", err)
502 | }
503 |
504 | // 关闭bootstqpper实例
505 | node.Bootstrapper.Stop()
506 |
507 | fmt.Println("stopping filecoin :(")
508 | }
509 | ```
510 |
511 |
512 | ### 15.2.4 启动及停止挖矿
513 |
514 | - 启动挖矿
515 |
516 | ```
517 | // StartMining causes the node to start feeding blocks to the mining worker and initializes
518 | // the SectorBuilder for the mining address.
519 | func (node *Node) StartMining(ctx context.Context) error {
520 | // 如果在挖矿中,退出
521 | if node.isMining() {
522 | return errors.New("Node is already mining")
523 | }
524 | // 获取矿工地址
525 | minerAddr, err := node.miningAddress()
526 | if err != nil {
527 | return errors.Wrap(err, "failed to get mining address")
528 | }
529 |
530 | // 确保密封服务实例存在
531 | // ensure we have a sector builder
532 | if node.SectorBuilder() == nil {
533 | if err := node.setupMining(ctx); err != nil {
534 | return err
535 | }
536 | }
537 |
538 | // 获取地址
539 | minerOwnerAddr, err := node.miningOwnerAddress(ctx, minerAddr)
540 | minerSigningAddress := node.MiningSignerAddress()
541 | if err != nil {
542 | return errors.Wrapf(err, "failed to get mining owner address for miner %s", minerAddr)
543 | }
544 |
545 | blockTime, mineDelay := node.MiningTimes()
546 |
547 | // 实例化挖矿调度服务
548 | if node.MiningScheduler == nil {
549 | getStateFromKey := func(ctx context.Context, tsKey string) (state.Tree, error) {
550 | tsas, err := node.ChainReader.GetTipSetAndState(ctx, tsKey)
551 | if err != nil {
552 | return nil, err
553 | }
554 | return state.LoadStateTree(ctx, node.CborStore(), tsas.TipSetStateRoot, builtin.Actors)
555 | }
556 | getState := func(ctx context.Context, ts types.TipSet) (state.Tree, error) {
557 | return getStateFromKey(ctx, ts.String())
558 | }
559 | getWeight := func(ctx context.Context, ts types.TipSet) (uint64, error) {
560 | parent, err := ts.Parents()
561 | if err != nil {
562 | return uint64(0), err
563 | }
564 | // TODO handle genesis cid more gracefully
565 | if parent.Len() == 0 {
566 | return node.Consensus.Weight(ctx, ts, nil)
567 | }
568 | pSt, err := getStateFromKey(ctx, parent.String())
569 | if err != nil {
570 | return uint64(0), err
571 | }
572 | return node.Consensus.Weight(ctx, ts, pSt)
573 | }
574 | getAncestors := func(ctx context.Context, ts types.TipSet, newBlockHeight *types.BlockHeight) ([]types.TipSet, error) {
575 | return chain.GetRecentAncestors(ctx, ts, node.ChainReader, newBlockHeight, consensus.AncestorRoundsNeeded, consensus.LookBackParameter)
576 | }
577 | processor := consensus.NewDefaultProcessor()
578 | worker := mining.NewDefaultWorker(node.MsgPool, getState, getWeight, getAncestors, processor, node.PowerTable,
579 | node.Blockstore, node.CborStore(), minerAddr, minerOwnerAddr, minerSigningAddress, node.Wallet, blockTime)
580 | node.MiningScheduler = mining.NewScheduler(worker, mineDelay, node.ChainReader.Head)
581 | }
582 |
583 | // paranoid check
584 | // 启动挖矿服务
585 | if !node.MiningScheduler.IsStarted() {
586 | node.miningCtx, node.cancelMining = context.WithCancel(context.Background())
587 | outCh, doneWg := node.MiningScheduler.Start(node.miningCtx)
588 |
589 | node.miningDoneWg = doneWg
590 | node.AddNewlyMinedBlock = node.addNewlyMinedBlock
591 | node.miningDoneWg.Add(1)
592 | go node.handleNewMiningOutput(outCh)
593 | }
594 |
595 | // initialize a storage miner
596 | // 初始化存储矿工
597 | storageMiner, err := initStorageMinerForNode(ctx, node)
598 | if err != nil {
599 | return errors.Wrap(err, "failed to initialize storage miner")
600 | }
601 | node.StorageMiner = storageMiner
602 |
603 | // loop, turning sealing-results into commitSector messages to be included
604 | // in the chain
605 | // 新开线程处理,1 密封完成处理;2 接受停止挖矿消息
606 | go func() {
607 | for {
608 | select {
609 | // 密封完成处理
610 | case result := <-node.SectorBuilder().SectorSealResults():
611 | if result.SealingErr != nil {
612 | log.Errorf("failed to seal sector with id %d: %s", result.SectorID, result.SealingErr.Error())
613 | } else if result.SealingResult != nil {
614 |
615 | // TODO: determine these algorithmically by simulating call and querying historical prices
616 | gasPrice := types.NewGasPrice(0)
617 | gasUnits := types.NewGasUnits(300)
618 |
619 | val := result.SealingResult
620 | // This call can fail due to, e.g. nonce collisions. Our miners existence depends on this.
621 | // We should deal with this, but MessageSendWithRetry is problematic.
622 | _, err := node.PorcelainAPI.MessageSend(
623 | node.miningCtx,
624 | minerOwnerAddr,
625 | minerAddr,
626 | nil,
627 | gasPrice,
628 | gasUnits,
629 | "commitSector",
630 | val.SectorID,
631 | val.CommD[:],
632 | val.CommR[:],
633 | val.CommRStar[:],
634 | val.Proof[:],
635 | )
636 | if err != nil {
637 | log.Errorf("failed to send commitSector message from %s to %s for sector with id %d: %s", minerOwnerAddr, minerAddr, val.SectorID, err)
638 | continue
639 | }
640 |
641 | node.StorageMiner.OnCommitmentAddedToChain(val, nil)
642 | }
643 | // 挖矿取消
644 | case <-node.miningCtx.Done():
645 | return
646 | }
647 | }
648 | }()
649 |
650 | // schedules sealing of staged piece-data
651 | // 定时密封阶段性的碎片数据
652 | if node.Repo.Config().Mining.AutoSealIntervalSeconds > 0 {
653 | go func() {
654 | for {
655 | select {
656 | // 取消
657 | case <-node.miningCtx.Done():
658 | return
659 | // 定时密封
660 | case <-time.After(time.Duration(node.Repo.Config().Mining.AutoSealIntervalSeconds) * time.Second):
661 | log.Info("auto-seal has been triggered")
662 | if err := node.SectorBuilder().SealAllStagedSectors(node.miningCtx); err != nil {
663 | log.Errorf("scheduler received error from node.SectorBuilder.SealAllStagedSectors (%s) - exiting", err.Error())
664 | return
665 | }
666 | }
667 | }
668 | }()
669 | } else {
670 | log.Debug("auto-seal is disabled")
671 | }
672 | // 设置微挖矿状态
673 | node.setIsMining(true)
674 |
675 | return nil
676 | }
677 | ```
678 |
679 | - 停止挖矿
680 |
681 | ```
682 | // StopMining stops mining on new blocks.
683 | func (node *Node) StopMining(ctx context.Context) {
684 | node.setIsMining(false)
685 |
686 | // 取消挖矿
687 | if node.cancelMining != nil {
688 | node.cancelMining()
689 | }
690 |
691 | // 等待执行中的挖矿任务完成后结束
692 | if node.miningDoneWg != nil {
693 | node.miningDoneWg.Wait()
694 | }
695 |
696 | // TODO: stop node.StorageMiner
697 | }
698 | ```
699 |
700 | ## 15.3 阶段性分析结束说明
701 |
702 | > 至此笔者针对go-filecoin部分的分析快告一个小的段落了
703 |
704 | > 文章因为时间的关系,书面出来只是将关键部分书面表达出来,更多的像是笔者的一个分析笔记,但是我相信对于想分析源码的朋友有一定帮助
705 |
706 | > 后面会抽空补充一章总结,笔者在第4章中有提到过,薄读->厚读->再薄读,我们还需要一次薄读,来加深我们对filecoin的认识。
707 |
708 |
--------------------------------------------------------------------------------
/2.md:
--------------------------------------------------------------------------------
1 | # [先河系统杨尉] filecoin技术架构分析之二:filecoin通用语言理解
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 | ## 目录
6 | - [2 filecoin通用语言理解](#filecoin通用语言理解)
7 | - [2.1 为什么要把filecoin通用语言单独列为一讲](#为什么要把filecoin通用语言单独列为一讲)
8 | - [2.2 存储证明](#存储证明)
9 | - [2.2.1 为什么使用存储证明](#为什么使用存储证明)
10 | - [2.2.2 复制证明](#复制证明)
11 | - [2.2.3 时空证明](#时空证明)
12 | - [2.3 预期共识](#预期共识)
13 | - [2.3.1 基础前提](#基础前提)
14 | - [2.3.2 使用power达成共识](#使用power达成共识)
15 | - [2.3.3 选举方案](#选举方案)
16 | - [2.4 filecoin智能合约](#filecoin智能合约)
17 | - [2.4.1 文件合约](#文件合约)
18 | - [2.4.2 智能合约](#智能合约)
19 | - [2.4.3 与其他系统的兼容](#与其他系统的兼容)
20 | - [2.5 交易市场](#交易市场)
21 | - [2.4.1 存储市场](#存储市场)
22 | - [2.4.2 检索市场](#检索市场)
23 | - [2.6 filecoin节点](#filecoin节点)
24 |
25 | ## 2.1 为什么要把filecoin通用语言单独列为一讲
26 |
27 | - 笔者认为一位优秀的软件从业人员,必须具备两种必备能力
28 | - 架构设计能力
29 | - 架构剖析能力
30 |
31 | > 这两者是相辅相成的,架构设计师所设计之架构不可能超过自己的认知范畴,故架构设计师必须有效高效地拓展自己的技术认知视图,以适应当代软件架构高速发展的现实。而拓展的途径,一方面就是相关理论体系的快速学习跟进;另一方面,就是实战,对有显著价值的优秀软件项目进行架构剖析。有理论、有实战方是王道。理解具体架构的通用语言就是分析他人架构设计思维的一条捷径。
32 |
33 |
34 | - 理解具体技术架构的通用语言是分析架构的一条捷径
35 | - 通用语言是架构设计人员为实现某个具体技术架构,所高度抽象出来的名词或者称谓,通过理解通用语言,可以快速理解架构设计人员的思维和设计目的。
36 | - 与读书方法类似(薄读->厚读>再薄读),理解通用语言就是第一次的薄读过程,非常重要。
37 |
38 |
39 | - 对业务的理解是非常有必要的,所以在[第一讲](https://www.jianshu.com/p/97b454d81b6e)中,笔者铺垫了filecoin的一些基本概念,任何架构的设计不能脱离业务而行,业务驱动开发仍是非常实用的架构模式;filecoin 技术架构从业务来划分,可划分为两个大的范畴。
40 | - 分布式存储解决方案
41 | - 存储矿工
42 | - 检索矿工
43 | - 存储客户端
44 | - 检索客户端
45 | - 区块链项目
46 | - filecoin公链
47 | - filecoin actors 智能合约
48 |
49 | - filecoin核心通用业务组件
50 |
51 | |组件名称| 目的 |
52 | |:-------:|:--------:|
53 | |DSN |保障数据安全、包括故障容错、数据完整性、数据可恢复等|
54 | |新型存储证明 | 证明矿工按照协议规范存储了客户指定的数据,数据有效性|
55 | |可验证市场 |对矿工与客户组成的交易市场进行了建模,保证交易的有效性 |
56 | |有效工作量证明 |出块的共识机制,很重要,做到激励兼容 |
57 |
58 | **下面各节将会对filecoin技术架构中的核心通用语言进行解释。**
59 |
60 | - [回到目录](#目录)
61 |
62 | ## 2.2 存储证明
63 | Proof-of-Storage包含复制证明(PoR)和时空证明(PoSt),其作用主要有两点:
64 | - 证明矿工做了有效存储
65 |
66 | - 竞争区块打包出块,获取区块奖励
67 |
68 | - [回到目录](#目录)
69 |
70 | ### 2.2.1 为什么使用存储证明
71 | - 相对于PoW(Proof-of-Work)或者PoC
72 | - PoW耗能严重;PoC以空间换时间,同样存在耗能严重问题
73 | - 而filecoin网络的耗能必须远低于类似比特币的PoW,参见[第一讲](https://www.jianshu.com/p/97b454d81b6e)filecoin的对标对象,filecoin必须实现以更低的成对去应对商业竞争,同时提供相同级别的安全性,以及文件存储的效用
74 | - 存储证明需要做要与实体经济挂钩,减少无谓浪费
75 |
76 | - 相对于PoS(Proof-of-Stake)或者PoC
77 |
78 | - Proof-of-Storage在定向领域(分布式存储)以更简单方式,协调激励,并驱使矿工以有竞争力的价格提供真实的新存储,它促使矿工积极保证filecoin网络的效用
79 | - 当然Proof-of-Stake是区块链领域的热点研究问题
80 |
81 | - Proof-of-Storage阻止网络攻击
82 |
83 | |攻击类型| 说明 |阻止攻击原理|
84 | |:-------:|:--------:|----------|
85 | |女巫攻击Sybil attack| 作恶节点创造多个女巫身份,谎称存储了多个副本|每个节点的副本都是有签名的,想通过复制证明,就相当于真实做了有效存储|
86 | |外包攻击outsourcing attacks| 作恶节点快速从其他节点获取内容,谎称他们存储了比他们实际存储更多的内容|针对外包攻击,从其他节点获取的整个过程,满足不了证明人随机挑战的要求,依然需要重新生成副本(重新seal需要时间),从而阻止外包攻击|
87 | |生成攻击generation attacks|作恶节点宣称将要存储超过其实际容量的内容但并未存储内容,以此增加出块的概率 |宣称无用,存储证明一定要确认密封动作并能应对随机挑战才能OK,如果重新密封就来不及证明,每次挑战是有时间要求的|
88 |
89 | - [回到目录](#目录)
90 |
91 | ### 2.2.2 复制证明
92 | #### 2.2.2.1 基础
93 | - 复制证明本质上可以理解为一种零知识证明,既然是零知识证明,我们在后面需要理解filecoin复制证明的题目和答案
94 |
95 | > zk-SNARK zero knowledge Succinct Non-interactive ARgument of Knowledge
96 | > zero knowledge:零知识,即在证明的过程中不透露任何内情
97 | > succinct:简洁的,主要是指验证过程不涉及大量数据传输以及验证算法简单
98 | > non-interactive:无交互。
99 |
100 |
101 |
102 | - 生成证明的方法在filecoin架构中称之为seal密封
103 |
104 | > 密封过程是需要时间的,Seal过程串行加密的过程,无法并行操作,seal密封过程是有意设计慢的,主要目的是为了防攻击。
105 |
106 |
107 | - [回到目录](#目录)
108 |
109 |
110 | #### 2.2.2.2 filecoin复制证明的题目和答案
111 | - 公开的信息
112 |
113 | - 矿工的节点公钥、密封公钥、存储公钥、原始Data哈希、该矿工存储的副本根哈希
114 |
115 | - 隐含因素理解:
116 | - 特有节点的副本哈希是由哪些哈希组成(DAG),任意挑战者或者攻击者是不知情的
117 | - 挑战随机参数,通过CRH(防碰撞的哈希散列Collision-resistant hashing)生成哈希之后传递给证明者,作用是确定特定的叶子节点的哈希,比如让证明者自行计算离H(c))最近的叶子节点哈希。
118 |
119 | - 复制证明的题目与答案
120 | - 挑战参数:副本哈希rt,挑战随机参数c -> H(c)
121 | - 证明者输入(题目):
122 | - H(c)(每一次挑战都会变)
123 | - 隐含信息比喻:该叶子节点是与H(c)最近的节点
124 | - 证明者输出(答案):
125 | - H(c)对应的叶子节点 ——> rt的路径(攻击者是很难反推的)
126 |
127 |
128 | - [回到目录](#目录)
129 |
130 | ### 2.2.3 时空证明
131 |
132 | - 时空证明可以理解为矿工持续性地生成复制证明
133 | - 挑战者输入一个随机参数c,后面的随机参数由证明者基于上一个的挑战答案去生成。(不用与挑战者持续交互)
134 | - 下图中变量i会轮询生成新的时间变量产生随机挑战。
135 |
136 |
137 | 
138 |
139 |
140 | - [回到目录](#目录)
141 |
142 | ## 2.3 预期共识
143 | ### 2.3.1 基础前提
144 | - filecoin基于存储证明(有效存储量)来作为矿工在整个网络中的power
145 |
146 | |power属性|说明|
147 | |:------:|:------:|
148 | |公开|1 某一时刻,整个网络存储总量是公开的
2 单个矿工某一时刻,有效存储总量是公开的|
149 | |可公开验证的|对于每个存储任务,矿工都需要生成”时空证明“,证明持续提供服务。通过读取区块链,任何人都可以验证矿工的power声明是否是正确的。|
150 | |变化|在任意时间点,矿工都可以通过增加新增扇区和扇区补充的抵押来增加新的存储。这样矿工就能变更他们能提供的power。|
151 |
152 | - [回到目录](#目录)
153 |
154 | #### 2.3.2 使用power达成共识
155 | - 目的:
156 | > 每一轮选举一个(或多个)矿工,**使得赢得选举的概率与每个矿工分配的存储成比例**
157 |
158 | - filecoin预期共识(Expected Consensus,EC)
159 | - 预期共识的基本直觉是确定性的,不可预测的
160 | - 预期的期望是每个周期内当选的Leader是1,但一些周期内可能有0个或者许多的Leader。
161 | - 在每个周期,每个区块链被延伸一个或多个区块,见下图
162 | - 区块线性扩展,但是数据结构是DAG
163 | - EC是一个概率共识,每个周期都使得比前面的区块更加确定,最终达到了足够的确定性
164 |
165 |
166 | - [回到目录](#目录)
167 |
168 | #### 2.3.3 选举方案
169 |
170 | > 预期共识通过选举方案产生
171 |
172 | 
173 |
174 | 
175 |
176 |
177 | |选举方案属性|说明|
178 | |:------:|:------:|
179 | |公平|每个参与者每次选举只有一次试验,因为签名是确定性的,而且t和rand(t)是固定的。随机值rand(t)在时刻t之前是未知的
|
180 | |保密|由于有能力的攻击者不拥有Mi用来计算签名的秘钥
|
181 | |公开可验证|当选Leader i ∈ Lt 可以通过给出t,rand(t),H(i)/2L,来说服一个有效的验证者。鉴于前面的观点(复制证明与时间证明),有能力的攻击者在不拥有获胜秘密秘钥的情况下不能生成证明。|
182 |
183 | - [回到目录](#目录)
184 |
185 | ## 2.4 filecoin智能合约
186 | ### 2.4.1 文件合约
187 |
188 | > 允许用户对他们提供的存储服务进行条件编程,**会形成一个多样化市场**。
189 |
190 | - 承包矿工:客户可以提前指定矿工提供服务而不参与市场
191 | - 付款策略:客户可以为矿工设计不同的奖励策略,例如合约可以给矿工支付随着时间的推移越来高的费用
192 | - 票务服务:合约可以允许矿工存放token和用于代表用户的存储/检索的支付
193 | - 更复杂的操作:客户可以创建合约来运行数据更新
194 |
195 | - [回到目录](#目录)
196 |
197 | ### 2.4.2 智能合约
198 | > 用户可以将程序关联到其他系统(如以太坊)的交易上,他们不直接依赖存储的使用。
199 |
200 | - 回到[目录](#目录)
201 |
202 | ### 2.4.3 与其他系统的兼容
203 | > 规格支持跨链交互,以便能将filecoin存储带入其他基于区块链的平台,同时也将其他平台的功能带入filecoin。
204 |
205 | - [回到目录](#目录)
206 |
207 | ## 2.5 交易市场
208 | - 存储需求和供给组成了两个Filecoin市场:存储市场和检索市场。这两个市场是两个去中心化交易所,简而言之,客户和矿工们通过向各自的市场提交订单来设定他们请求服务或者提供服务的订单的价格。交易所为客户和矿工们提供了一种方式来查看匹配出价并执行订单。如果服务请求被成功满足,通过运行管理协议,网络保证了矿工得到报酬,客户将被收取费用。
209 | - 可以类比为淘宝商城
210 |
211 |
212 |
213 | - [回到目录](#目录)
214 |
215 | ### 2.5.1 存储市场
216 |
217 | - 交易数据会上链,包含于区块之中。
218 | - 本质上也属于filecoin智能合约中的文件合约。
219 | - 20190214上线的开发网络已支持
220 |
221 |
222 | - [回到目录](#目录)
223 |
224 | ### 2.5.2 检索市场
225 |
226 | - 交易数据不会上链,属于offchain的方式。
227 | - 本质上也属于filecoin智能合约中的文件合约。
228 |
229 |
230 | - [回到目录](#目录)
231 |
232 | ## 2.6 filecoin节点
233 | - filecoin节点相关
234 | - node id表示filecoin网络节点
235 | - account id并表示账号,默认与钱包地址一致
236 | - wallet addr表示钱包地址
237 | - miner id表示矿工id
238 |
239 | 
240 |
241 | - [回到目录](#目录)
242 |
--------------------------------------------------------------------------------
/3.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之三:filecoin开发网络使用
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 | ## 目录
6 | - [3 filecoin开发网使用](#filecoin开发网使用)
7 | - [3.1 辅助资源](#辅助资源)
8 | - [3.2 使用](#使用)
9 | - [3.2.1 接入filecoin开发网络](#接入filecoin开发网络)
10 | - [3.2.2 获取Mock FIL用于测试](#从faucet中获取mock代币)
11 | - [3.2.3 矿工操作](#矿工操作)
12 | - [3.2.3.1 存储矿工](#存储矿工)
13 | - [3.2.3.2 检索矿工](#检索矿工)
14 | - [3.2.3.3 修复矿工](#修复矿工)
15 | - [3.2.4 客户操作](#客户操作)
16 | - [3.2.4.1 存储客户](#存储客户)
17 | - [3.2.4.2 检索客户](#检索客户)
18 | - [3.2.5 filecoin合约](#filecoin合约)
19 | - [3.2.5.1 文件合约](#文件合约)
20 | - [3.2.5.2 智能合约](#智能合约)
21 | - [3.2.6 单机运行多个filecoin节点](#单机运行多个filecoin节点)
22 | - [3.2.6.1 修改资源目录和服务端口的方式](#修改资源目录和服务端口的方式)
23 | - [3.2.6.2 容器部署方式](#容器部署方式)
24 |
25 | ## 3 filecoin开发网络使用
26 | ### 3.1 辅助资源
27 |
28 | - Filecoin状态: https://stats.kittyhawk.wtf
29 | - 网络
30 | - 存储实时价格 FIL/GB/Month
31 | - 当前存储容量 GB
32 | - 当前网络利用率
33 | - 检索平均价格
34 | - 激活节点以及分布图
35 | - 存储平均价格曲线
36 | - best tipset
37 | - 存储矿工
38 | - 存储矿工数量变化曲线
39 | - 存储矿工共识结果
40 | - 近30天的矿工top图
41 | - 检索矿工
42 | - 平均检索价格
43 | - 平均检索时间
44 | - 平均检索容量
45 | - FIL指数
46 | - 流通FIL及抵押FIL变化图
47 | - FIL地址总数
48 | - FIL总抵押数及对应存储空间
49 | - FIL总数上升曲线图
50 | - FIL区块奖励下降曲线图
51 |
52 | - Filecoin区块浏览器: http://user.kittyhawk.wtf:8000
53 | - Chain信息
54 | - BestBlock信息
55 | - Actor合约信息
56 |
57 | - 获取FIL用于抵押或支付:http://user.kittyhawk.wtf:9797
58 |
59 | - 获取mock FIL代币
60 |
61 | - Dashboard: http://user.kittyhawk.wtf:8010
62 |
63 | - Network概览,最新区块信息
64 | - 区块浏览器链接
65 |
66 | - Genesis File: http://user.kittyhawk.wtf:8020/genesis.car
67 |
68 | - 创始文件,用于初始化filecoin资源
69 |
70 | - Prometheus Endpoint: http://user.kittyhawk.wtf:9082/metrics
71 |
72 | - 一些技术指标,比如内存、进程、线程等
73 |
74 | - Connected Nodes PeerID's: http://user.kittyhawk.wtf:9082/nodes
75 |
76 | - 连接的节点信息
77 |
78 |
79 | ### 3.2 使用
80 | #### 3.2.1 接入filecoin开发网络
81 | - 初始化filecoin资源目录
82 |
83 | >如果之前有运行过filecoin,想重新开始,需要删除filecoin资源,同时重新初始化是需要重新花时间同步开发网区块信息的。
84 |
85 | ```
86 | rm -rf ~/.filecoin
87 | ```
88 |
89 | > 初始化资源目录,使用--devnet-user表示连接至开发网
90 |
91 | ```
92 | waynewyang:Downloads waynewyang$ go-filecoin init --devnet-user --genesisfile=http://user.kittyhawk.wtf:8020/genesis.car
93 | initializing filecoin node at ~/.filecoin
94 | waynewyang:Downloads waynewyang$
95 | ```
96 |
97 | - 启动filecoin进程,接入开发网
98 | ```
99 | go-filecoin daemon
100 |
101 | //如果开发者,需要接入nightly devnet,请设置环境变量后启动filecoin
102 | env FIL_USE_SMALL_SECTORS=true go-filecoin daemon
103 | ```
104 |
105 | - 检查连接性
106 |
107 | > go-filecoin swarm peers 查看已经连接的节点
108 |
109 | ```
110 | waynewyang:filecoin waynewyang$ go-filecoin swarm peers
111 | /ip4/115.238.154.84/tcp/19109/ipfs/Qmb6ZYi7GLFAje3UekGZ2LZymck7RVHKSKb1bhPzzPTQkm
112 | /ip4/115.238.154.84/tcp/41187/ipfs/QmZ9UHdU2fwDN7emWW8AeaUdkF9fT7RwJrnbbdcQFUq9X6
113 | /ip4/123.134.67.81/tcp/6000/ipfs/QmccrEQsauwge4BZQeN1jBtFyd7dnTi4pSDvkikMWaFccw
114 | /ip4/123.134.67.82/tcp/6000/ipfs/QmWuA1AW4qDqztDrwo2pBgT2au67BJbGtEzWRufbc8isgn
115 | /ip4/123.134.67.83/tcp/6000/ipfs/QmbPCabGcngs3bCgMK8dC3w9pjoyPd1NFyDhbkgLyT2eJ7
116 | /ip4/123.134.67.85/tcp/6000/ipfs/QmUqSSZrwfSUU3vfw7D1UyKaLvEv1Ykcvx3ntvSXWaA7kj
117 | /ip4/123.134.67.86/tcp/6000/ipfs/QmPrz2z764AVaHivM7iX2JqRw5EdE3jcZTrjwVxS4VukyK
118 | /ip4/123.134.67.87/tcp/6000/ipfs/QmTxVFq3u7qPxsXFQdoyqPrdh6meW6JBGkSJ8HJXAiMUfh
119 | /ip4/123.134.67.88/tcp/6000/ipfs/QmXAVRPYu57XDwJHszn9U9x1KtTwPsJBaS1mTdNZzAQVyQ
120 | /ip4/123.134.67.89/tcp/6000/ipfs/Qmc5umx9R3bpD5VxvUmfyLoDz5wtVT43p5xjSEmTe26qTD
121 | ```
122 |
123 | > go-filecoin ping peerID 确认连通性
124 |
125 | ```
126 | waynewyang:filecoin waynewyang$ go-filecoin ping QmW4Z8p7FCspLV1FeTRW6uCNApUXqkm8xYYw4yuBnqBGeB
127 | PING
128 | Pong received: time=245.12 ms
129 | Pong received: time=245.61 ms
130 | Pong received: time=251.98 ms
131 | Pong received: time=245.69 ms
132 | Pong received: time=255.64 ms
133 | ```
134 |
135 | - 给你的filecoin Node设置昵称
136 | ```
137 | waynewyang:filecoin waynewyang$ go-filecoin config heartbeat.nickname "wwwarsyuncom"
138 | "wwwarsyuncom"
139 | waynewyang:filecoin waynewyang$ go-filecoin config heartbeat.nickname
140 | "wwwarsyuncom"
141 | ```
142 |
143 | - 激活节点
144 |
145 | ```
146 | go-filecoin config heartbeat.beatTarget "/dns4/stats-infra.kittyhawk.wtf/tcp/8080/ipfs/QmUWmZnpZb6xFryNDeNU7KcJ1Af5oHy7fB9npU67sseEjR"
147 | ```
148 |
149 | > 在 https://stats.kittyhawk.wtf/ 查看filecoin网络,节点已经激活
150 |
151 | 
152 |
153 | #### 3.2.2 获取Mock FIL用于测试
154 |
155 | > FIL用于矿工抵押;或者作为客户进行交易需要
156 |
157 | - 注意:开发网目前运行的都是全节点,获取mock FIL需要建立在本地区块数据同步完成的基础上进行,必须同步完区块之后才能生效,根据个人机器配置情况,这需要较长一段时间。
158 | - go-filecoin message wait ${MESSAGE_CID} 本质上是转账交易,wiki上说明的是等待30s,但是这是在本地区块数据同步完成的基础上才行的。
159 | - 笔者已提交建议给官方,在wiki上更为清晰地表述。
160 |
161 | 
162 |
163 |
164 | ```
165 | waynewyang:filecoin waynewyang$ go-filecoin wallet addrs ls
166 | fcq09qtmrxgq5sdr95gs93tx79u9uymdwfdsaphpa
167 |
168 | waynewyang:filecoin waynewyang$ export WALLET_ADDR=`go-filecoin wallet addrs ls`
169 |
170 | waynewyang:filecoin waynewyang$ MESSAGE_CID=`curl -X POST -F "target=${WALLET_ADDR}" "http://user.kittyhawk.wtf:9797/tap" | cut -d" " -f4`
171 | % Total % Received % Xferd Average Speed Time Time Time Current
172 | Dload Upload Total Spent Left Speed
173 | 100 232 100 50 100 182 48 177 0:00:01 0:00:01 --:--:-- 177
174 |
175 | waynewyang:go-filecoin waynewyang$ go-filecoin message wait ${MESSAGE_CID}
176 | {
177 | "meteredMessage": {
178 | "message": {
179 | "to": "fcqm0u932ja5thlsy4dgpz5urlapk8qhtd0clqv5e",
180 | "from": "fcq09sqhrd4gls86muuenzvqdc37mzscagapjveal",
181 | "nonce": "rQQ=",
182 | "value": "1000",
183 | "method": "",
184 | "params": null
185 | },
186 | "gasPrice": "0",
187 | "gasLimit": "AA=="
188 | },
189 | "signature": "WKA+eRY7XCQlSmallzoFu8Tps7NZ2AOAKLRFo21rTERFYJqXJT2qEWZ8sFvm6ZShR5syb7RSAJnDp4Am2Vzp0gE="
190 | }
191 | {
192 | "exitCode": 0,
193 | "return": null,
194 | "gasAttoFIL": "0"
195 | }
196 | waynewyang:filecoin waynewyang$ go-filecoin wallet balance fcq09qtmrxgq5sdr95gs93tx79u9uymdwfdsaphpa
197 | 1000
198 | waynewyang:filecoin waynewyang$ go-filecoin wallet balance ${WALLET_ADDR}
199 | 1000
200 | ```
201 |
202 | #### 3.2.3 矿工操作
203 | #### 3.2.3.1 存储矿工
204 | - 创建存储矿工示例,需要等待1分钟左右
205 | - 抵押10个扇区的存储空间(当前默认每个扇区256MiB)
206 | - 支付100个FIL为担保
207 | - gas价格为0
208 | - 限制gas消耗最大为1000个FIL
209 |
210 |
211 |
212 | ```
213 | waynewyang:filecoin waynewyang$ go-filecoin miner create 10 100 --price=0 --limit=1000 --peerid `go-filecoin id | jq -r '.ID'`
214 | fcqjge872spqrgtm8dhlndjgfhhuxzx0y3ujvxxsl //所返回的就是矿工地址minerAddress
215 |
216 | ```
217 |
218 | - 启动挖矿
219 | ```
220 | waynewyang:filecoin waynewyang$ go-filecoin mining start
221 | Started mining
222 | ```
223 |
224 | - 收益之一: 启动挖矿之后就可以参与挖区块奖励
225 | > 查询区块头
226 |
227 | ```
228 | waynewyang:go-filecoin waynewyang$ go-filecoin chain head
229 | [{"/":"zDPWYqFD2mBqLx7bwQNdeVoMxj6SC5HxzorZAoXpT6xjaythnENw"}]
230 | ```
231 |
232 | > 查询具体区块信息
233 |
234 | ```
235 | go-filecoin show block
236 |
237 | waynewyang:go-filecoin waynewyang$ go-filecoin show block zDPWYqFD2mBqLx7bwQNdeVoMxj6SC5HxzorZAoXpT6xjaythnENw
238 | Block Details
239 | Miner: fcq0y72meekgwnvchwml0uzx759q25nk0rqc47ret
240 | Weight: 293567.552
241 | Height: 10787
242 | Nonce: 0
243 | ```
244 |
245 | - 收益之二:创建报价单ask
246 |
247 | ```
248 | 1) 获取矿工地址
249 | export MINER_ADDR=`go-filecoin config mining.minerAddress | tr -d \"`
250 |
251 | 2) 设置矿机Owner
252 | export MINER_OWNER_ADDR=`go-filecoin miner owner $MINER_ADDR`
253 |
254 | 3) 创建报价单,价格0.000000001 FIL/byte/block, 交易费0,gas限制1000,提供2880个block空间存储
255 |
256 | go-filecoin miner set-price --from=$MINER_OWNER_ADDR --miner=$MINER_ADDR --price=0 --limit=1000 0.000000001 2880 # output: CID of the ask
257 |
258 | 发布报价单,需要打包进去区块
259 |
260 | waynewyang:filecoin waynewyang$ go-filecoin miner set-price --from=$MINER_OWNER_ADDR --miner=$MINER_ADDR --price=0 --limit=1000 0.000000001 15315
261 | Set price for miner fcqjge872spqrgtm8dhlndjgfhhuxzx0y3ujvxxsl to 0.000000001.
262 | Published ask, cid: zDPWYqFCxL3VW3xzmHhCBqPTvhoQa53pn6DzV3uY23jNL76za1Vt.
263 | Ask confirmed on chain in block: zDPWYqFD7wjnj74sdB9HqupDmWmpPPEvygB14Pbo6rQC7ho2687D.
264 |
265 | 4) 查询区块信息(第三步中是zDPWYqFD7wjnj74sdB9HqupDmWmpPPEvygB14Pbo6rQC7ho2687D)可以找到对应报价单信息
266 |
267 | waynewyang:filecoin waynewyang$ go-filecoin show block zDPWYqFD7wjnj74sdB9HqupDmWmpPPEvygB14Pbo6rQC7ho2687D --enc=json
268 | {"miner":"fcqnam6n2qml2eyngws25srzvhcdf0t8gcgrsvnrk","ticket":"AM0p5IC9ph+o9dTwd/MXYdeOJW25PfDwhTgonNRkSP4=","parents":[{"/":"zDPWYqFCwNWHJXdeXcjx7ipUvRKFq5WhLbtSm6ESuNufkLuGiAgW"}],"parentWeight":"kujBrQE=","height":"2Hc=","nonce":"AA==","messages":[{"meteredMessage":{"message":{"to":"fcqp606qfk5gwmq6ac24g4mhv3cr8zzf67vqkpulh","from":"fcqr89lj0lvduj475zw002j6q5yrl30ks7uep2p5e","nonce":"Ag==","value":"215.6046624","method":"createChannel","params":"glYAABXMs4C0hXqcW94YGyVKxii6SLQhQ6HoAg=="},"gasPrice":"0","gasLimit":"rAI="},"signature":"vHzwO73TvM8MW1FKg8Qgfy/IP+wfJIQkEK0ExBB75gBbPMhv6GiU4aBq1T2Gb2OeMfrch8Zg3EFOJd0uUJltwAE="},{"meteredMessage":{"message":{"to":"fcqafmqgvzkzpvc6wjxecm7gsweuawjv8t6falk6r","from":"fcqr89lj0lvduj475zw002j6q5yrl30ks7uep2p5e","nonce":"Aw==","value":"100","method":"createMiner","params":"g0EKWEEEiA8ArEoyzhjWwijpTWYqDsOFfwxa2F0pUfOyRI/6yY28OD4QHcwUdb3a9omX9DNxVzdS2a8pWgiLNowe9wYVcFgiEiD3rKfg/NyDnrLF9IGxfp6U72jZxuniXlPcv5SG5OZHrA=="},"gasPrice":"0","gasLimit":"6Ac="},"signature":"/X/87zil8InOeLeQ6kqkqnpg7mP/e5jMaaVS4LRMIdYN84HTbABBpvt6quRqVQsadJnqOW7mn+6NA+2d9FDjPAA="},{"meteredMessage":{"message":{"to":"fcqp606qfk5gwmq6ac24g4mhv3cr8zzf67vqkpulh","from":"fcqmqr5f2a5qnwnvftpuzd6sjfy5tcq5dd0k24h85","nonce":"Ug==","value":"0.008596","method":"createChannel","params":"glYAAIVV3axUhfe7OGwwSH/IIONRcbinQ/OWAQ=="},"gasPrice":"0","gasLimit":"rAI="},"signature":"dxhSaVRvFBtdrbnEByza7a5JqLzm6n6rVZYGuFN6zegCTMDKbGGh++EvVmWo0WSbdcUo2vB/jFTgqzATh9+1NQA="},{"meteredMessage":{"message":{"to":"fcqugc6nql2eqglfwq0dw7ep7l9a07jacqgstely7","from":"fcq0nmdcq7updgwc3uh2lz2rnjms7gprdggcvxjqj","nonce":"BA==","value":null,"method":"commitSector","params":"hUECWCCFzRGqHyJ3VWk3GueHzfkcWF218hOqRGtLxsJ0oJ3pXVgg6qrxCGo6SSjyUSbJWVKPKGaY/wrymC21t5LSScCNpQBYIOeZkdzp7lPt8Fh/Sdl9YqJ8BCaJ7etWEnDnLzRnV/geWQGAlTMp95t1Hh61eFBmzy6Ex/Ee1cso7Cethz+Z2EHCfhi5UzOMeLqeA/Wfypcnrw15mF4OrYR8648RXx6jp8svbgZ6Jg9fP/q0RukszZ/SD9f0pCMg2N/xt5hVIPG7jowSCfkj//CpdRj1GRPLzXvzyWmW4SgKR4lNpJNnmuiXSe8nYLsZgY2v8xy4NB448e/slxh7D4NQPanCoN3WO10oBR42ZxeCZY6stq+JfwucGr5OajgXSK2rGwz/Sj+GYpMbtgpfxSd4Z+jZ6mnoY03NaIHvwnDKchRz797lFL3so6AQRRnctN3Pl7LSn52YA0EOkmhJLMev6DKBWEqSfjXTY4AJSJ7RmGq88BXoHzwGjndRj0QHFtSTjHIoxF9uN86zB6gfSS+A7ZviuTvfturtKee243b9OIojIf2ne2hF8+7PSIwCp5FPLXEqR/UtXnJ6pxjBKhF36k3FRdzsxo52DMImgPhluGWI3xRhpIMmFNDMNynOBQ9F6mnR8fRBdPKB"},"gasPrice":"0","gasLimit":"rAI="},"signature":"hEluqbFG8TTSeeJyfOs10fZD/gOsrnFV8QgRAb4mhSFlzYpcijT9ye1yUYam5hcsW1eq1MFRfVGHhqYYUZ4LYwA="},{"meteredMessage":{"message":{"to":"fcqjge872spqrgtm8dhlndjgfhhuxzx0y3ujvxxsl","from":"fcqm0u932ja5thlsy4dgpz5urlapk8qhtd0clqv5e","nonce":"AQ==","value":"0","method":"addAsk","params":"gkWAlOvcA0I70w=="},"gasPrice":"0","gasLimit":"6Ac="},"signature":"IGUvf7CZ8lKDSTyzbg5kyArIPv8TIoFEWpA3ihRC7I86NFvgebCdEKQ6PqYhTJj1GQK/+JF28kCinXFN/9G8FgE="},{"meteredMessage":{"message":{"to":"fcqp606qfk5gwmq6ac24g4mhv3cr8zzf67vqkpulh","from":"fcqsvmdzpy5mjc9m0cuh6uhmprr6gk5w2zcrnjy02","nonce":"Aw==","value":"0.0000301989888","method":"createChannel","params":"glYAAGsLRe4dsJwsvfE2+dkEh1DPX11jQ+OdAQ=="},"gasPrice":"0","gasLimit":"rAI="},"signature":"Zg5kUOtEZ9Um+mGKicHaqTCaORppGv5KAaSlTpN/qTcoTptRUP3ZbQ5YOL7zjTG6aF7Y4r0Ck0NsnG0J/i2B0AA="},{"meteredMessage":{"message":{"to":"fcqp606qfk5gwmq6ac24g4mhv3cr8zzf67vqkpulh","from":"fcqrqtfcug5hlx7gugvwj0f2dyx6j9cxdn0ynmpu3","nonce":"Ag==","value":"0","method":"createChannel","params":"glYAAJsQ6e5i/M0X04YZwzl3VWckPu4RQ62HAQ=="},"gasPrice":"0","gasLimit":"rAI="},"signature":"oisXV83sTFkJw7y5KbO5fLhx2oa48qZKAVwX+1fIvWUDgm5PQNDddPeCkklPg2L+fmp4wL2fLF9R2qPRciLUfAA="}],"stateRoot":{"/":"zdpuAvwpuqNR4J6PJDqGfF5GbyjWarD1BhujTUfyREMHSY1eF"},"messageReceipts":[{"exitCode":0,"return":["Ag=="],"gasAttoFIL":"0"},{"exitCode":0,"return":["AAA2Qrupiubk6ljOMnMUrnnHKaVXmQ=="],"gasAttoFIL":"0"},{"exitCode":0,"return":["0gA="],"gasAttoFIL":"0"},{"exitCode":0,"return":null,"gasAttoFIL":"0"},{"exitCode":0,"return":[""],"gasAttoFIL":"0"},{"exitCode":0,"return":["Aw=="],"gasAttoFIL":"0"},{"exitCode":0,"return":["Ag=="],"gasAttoFIL":"0"}],"proof":[177,165,90,219,1,18,240,190,113,56,243,22,167,201,232,75,124,152,130,111,74,132,5,192,33,191,102,220,102,9,99,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]}
269 |
270 | //查询最新区块信息
271 | waynewyang:go-filecoin waynewyang$ go-filecoin show block `go-filecoin chain head --repodir=~/.filecoin2/ |jq -r '.[0]'|jq -r '.["/"]'`
272 | Block Details
273 | Miner: fcq973y2y7hvcce8zkwds7r2847xmfjvdecn98lws
274 | Weight: 134386.836
275 | Height: 5013
276 | Nonce: 0
277 |
278 | 5) 获取所有矿工的报价信息
279 | go-filecoin client list-asks --enc=json | jq
280 | ```
281 |
282 | > 现在默认是只要客户出价高于矿工报价,默认接受交易。
283 |
284 | - 停止挖矿
285 |
286 | ```
287 | go-filecoin mining stop
288 | rm -rf ~/.filecoin //删除filecoin矿工实例,区块同步也被删除,再次实例化,需要再次同步区块
289 | ```
290 |
291 | #### 3.2.3.2 检索矿工
292 |
293 | > 暂未发现支持,目前可以自己的供应商(具体矿工)处获取;后面通过更深入的分析之后另行补充。
294 |
295 | #### 3.2.3.3 修复矿工
296 |
297 | > 修复矿工的概念是白皮书之后提出的,后面继续深入分析之后再另行补充。
298 |
299 | #### 3.2.4 客户操作
300 | #### 3.2.4.1 存储客户
301 |
302 | - filecoin 与IPFS数据结构是兼容的
303 |
304 | ```
305 | //创建测试文件
306 | waynewyang:test waynewyang$ echo "Hi my name is $USER"> hello.txt
307 | waynewyang:test waynewyang$ cat hello.txt
308 | Hi my name is waynewyang
309 |
310 | //导入filecoin本地资源库
311 | waynewyang:test waynewyang$ export CID=`go-filecoin client import ./hello.txt`
312 | waynewyang:test waynewyang$ echo $CID
313 | Qmchgh3N3kxWiaZ2cp9PbV93i77H3K8KtQCBTeVR5Q7wzs
314 |
315 | //这里会发现用IPFS上传得到的CID也是一样
316 | waynewyang:test waynewyang$ ipfs add hello.txt
317 | added Qmchgh3N3kxWiaZ2cp9PbV93i77H3K8KtQCBTeVR5Q7wzs hello.txt
318 | 25 B / 25 B [========================================================================] 100.00%
319 |
320 | //用go-filecoin或者IPFS命令获取数据,结果一致
321 | waynewyang:test waynewyang$ go-filecoin client cat $CID
322 | Hi my name is waynewyang
323 | waynewyang:test waynewyang$ ipfs block get $CID
324 | Hi my name is waynewyang
325 | ```
326 |
327 | - 导入测试数据
328 | ```
329 | waynewyang:sample-data waynewyang$ export CID=`go-filecoin client import camel.jpg`
330 | waynewyang:sample-data waynewyang$ go-filecoin client cat $CID > image.png && open image.png
331 | waynewyang:sample-data waynewyang$ echo $CID
332 | QmeubcGKFXpafFT4xRFGf3NqDRzJUVoAqe5sh1ugbRPZ7u
333 | ```
334 |
335 | - 查询矿工的报价单
336 |
337 | ```
338 | waynewyang:sample-data waynewyang$ go-filecoin client list-asks --enc=json | jq
339 | {
340 | "Miner": "fcqvnwlanfu7ecflnp3rc5gm0ecdamvxgvlawref4",
341 | "Price": "0.000000001",
342 | "Expiry": 7079,
343 | "ID": 0,
344 | "Error": null
345 | }
346 | {
347 | "Miner": "fcqsmut6jnwchq0qlc3t6v44pzgf8l49lg6r8wl4a",
348 | "Price": "0.000000001",
349 | "Expiry": 16522,
350 | "ID": 0,
351 | "Error": null
352 | }
353 | {
354 | "Miner": "fcqsmut6jnwchq0qlc3t6v44pzgf8l49lg6r8wl4a",
355 | "Price": "0.000000000000000001",
356 | "Expiry": 18753,
357 | "ID": 1,
358 | "Error": null
359 | }
360 | {
361 | "Miner": "fcqghrce7vaf6czj54x5qke0mn2uzzg8ckvgvcjpe",
362 | "Price": "0.000000001",
363 | "Expiry": 14404,
364 | "ID": 0,
365 | "Error": null
366 | }
367 | ......
368 | ```
369 |
370 | - 下单
371 |
372 | ```
373 | go-filecoin client propose-storage-deal
374 |
375 | address of the miner from list-asks
376 | CID of the imported data that you want to store
377 | ID of the ask, also from list-asks (usually 0)
378 | how long you want to store (in # of ~30sec blocks). For example, storing for 1 day (2 blocks/min * 60 min/hr * 24 hr/day) = 2880 blocks.
379 | ```
380 |
381 | - 发送数据和支付
382 |
383 | ```
384 | 1 支付
385 |
386 | 1)支付到paych中
387 | 2)定期向矿工付款
388 |
389 | 2 数据
390 |
391 | 1)未密封完的数据称之为暂存区
392 | 2)密封完成后阶段性支付
393 | ```
394 |
395 | #### 3.2.4.2 检索客户
396 |
397 | > 现在是指定所对应的存储矿工进行检索,暂未发现更多支持,在后面的深入分析中会继续跟进。
398 |
399 | - 查询订单状态,必须是密封,posted交易结束后才能查询
400 |
401 | ```
402 | go-filecoin client query-storage-deal < dealID >
403 | ```
404 |
405 | - 检索
406 |
407 | ```
408 | go-filecoin retrieval-client retrieve-piece < minerAddress > < CID >
409 | ```
410 |
411 | - [回到目录](#目录)
412 |
413 | ### 3.2.5 filecoin合约
414 | #### 3.2.5.1 文件合约
415 |
416 | > 其实现在的创建存储矿工,以及矿工创建报价、存储客户提交订单存储,这些笔者认为属于filecoin文件合约的范畴。
417 |
418 | > 与以太坊类似,以太坊抽象出了代币合约以及通用智能合约; 而filecoin则是抽象出了文件合约和通用智能合约。
419 |
420 | - [回到目录](#目录)
421 |
422 | #### 3.2.5.2 智能合约
423 |
424 | > 暂未发现支持,在后面的深入分析中会继续跟进。
425 |
426 | - [回到目录](#目录)
427 |
428 | ### 3.2.6 单机运行多个filecoin节点
429 | #### 3.2.6.1 修改资源目录和服务端口的方式
430 |
431 | - go-filecoin init的时候,通过 ‘--repodir=所指定资源目录路径’ 命令进行初始化目录资源,后面的其他命令同样需要所指定资源目录路径进行操作。
432 |
433 | - 修改资源目录下的config.json文件,将默认的端口予以修改,避免与另外的本机实例相冲突。
434 |
435 | - [回到目录](#目录)
436 |
437 | #### 3.2.6.2 容器部署方式
438 |
439 | - 可以打包成docker镜像,有兴趣的朋友可以自行尝试。
440 |
441 |
442 | - [回到目录](#目录)
443 |
--------------------------------------------------------------------------------
/4.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之四:filecoin源码顶层架构分析
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - [4 filecoin源码顶层架构分析](#filecoin源码顶层架构分析)
8 | - [4.1 题外话——关于竞争力](#题外话关于竞争力)
9 | - [4.2 filecoin顶层架构概览及分析思路](#filecoin顶层架构概览及分析思路)
10 | - [4.2.1 分析思路](#分析思路)
11 | - [4.2.2 filecoin顶层架构概览](#filecoin顶层架构概览)
12 | - [4.3 网络层](#网络层)
13 | - [4.4 协议层](#协议层)
14 | - [4.5 REST/CMD](#restcmd)
15 | - [4.6 内部api层](#内部api层)
16 | - [4.7 core服务层](#core服务层)
17 |
18 |
19 |
20 | ## 4 filecoin源码顶层架构分析
21 |
22 | ## 4.1 题外话——关于竞争力
23 |
24 | > 网络技术的高速发展带领我们进入了知识大爆炸、技术快速跃迁的时代,5G已经开始走向商业落地,网络速率的再次跃迁给我们带来了无限的想象空间,全息投影、即时翻译、远程医疗、人工智能等等会更加成熟落地?路由器在个人家庭中的角色可能会发生变化?IOT万物互联的时代将会真正到来?区块链的TPS提升?高速网络下的云应用、大数据会出现什么新的玩法?
25 |
26 | >笔者想说的是,整个世界都在急速变化,在波涛汹涌的竞争浪潮之中,如何保持自己的竞争力。我偶尔会问同事、朋友,你与刚毕业的大学生相比,优势在哪里?
27 |
28 | 笔者认为如下两点才是在这个高速时代的真正竞争力,个人如此,公司团队亦如此。
29 |
30 | - 高效的学习能力
31 | - 高维的思维能力
32 |
33 |
34 |
35 | 以上为笔者观点,也欢迎大家探讨。在分析具体架构之前,笔者在4.2.1中分享自己的分析思路,我认为这也许也值得分享。
36 |
37 |
38 |
39 |
40 |
41 | ## 4.2 filecoin源码顶层架构概览及分析思路
42 | ### 4.2.1 分析思路
43 |
44 | - 终于进入到源码分析环节了,其实回顾一下前面三章,filecoin的概念及通用语言可以总结为filecoin的本质,分析源码的过程归根接底还是理解设计者的意图,第三章filecoin开发网络的实战使用对于笔者来说也是为了更清晰地对filecoin本质及设计意图进行深入理解。
45 |
46 | - 分析总思路为:抓住本质分析,理解设计者意图
47 |
48 | - 自上而下逐层分析,从抽象到具体
49 | - 自下而上反向总结,从具体到抽象
50 |
51 | - 分析过程分为三大步骤
52 |
53 | - 第一步,理解filecoin本质及设计目的(前面三章)
54 | - 第二步,理解filecoin的顶层架构设计(本章),反向加深对filecoin本质的理解
55 | - 第三步,各层的具体源码分析(后面章节),反向加深对filecoin本质的理解
56 |
57 | > 详细参见下图
58 |
59 |
60 | 
61 |
62 | - 在顶层源码中分为go-filecon和rust-fil-proofs。分别为主框架和存储证明部分,本文主要分析go-filecoin源码的顶层框架。
63 |
64 |
65 |
66 |
67 |
68 | ### 4.2.2 filecoin顶层架构概览
69 | #### 4.2.2.1 架构图
70 |
71 | ```
72 | ┌─────────────────────────────────────┐
73 | │ │
74 | Network │ network (gossipsub, bitswap, etc.) │ | | \/
75 | │ │ |_| /\
76 | └─────▲────────────▲────────────▲─────┘
77 | │ │ │ ┌────────────────────────────┐
78 | ┌─────▼────┐ ┌─────▼─────┐ ┌────▼─────┐ │ │
79 | │ │ │ │ │ │ │ Commands / REST API │
80 | Protocols │ Storage │ │ Mining │ │Retrieval │ │ │
81 | │ Protocol │ │ Protocol │ │ Protocol │ └────────────────────────────┘
82 | │ │ │ │ │ │ │
83 | └──────────┘ └───────────┘ └──────────┘ │
84 | │ │ │ │
85 | └──────────┬─┴─────────────┴───────────┐ │
86 | ▼ ▼ ▼
87 | ┌────────────────────────────────┐ ┌───────────────────┬─────────────────┐
88 | Internal │ Core API │ │ Porcelain │ Plumbing │
89 | API │ │ ├───────────────────┘ │
90 | └────────────────────────────────┘ └─────────────────────────────────────┘
91 | │ │
92 | ┌─────────┴────┬──────────────┬──────────────┬─┴────────────┐
93 | ▼ ▼ ▼ ▼ ▼
94 | ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
95 | │ │ │ │ │ │ │ │ │ │
96 | Core │ Message │ │ Chain │ │ Processor │ │ Block │ │ Wallet │
97 | │ Pool │ │ Store │ │ │ │ Service │ │ │
98 | │ │ │ │ │ │ │ │ │ │
99 | └────────────┘ └────────────┘ └────────────┘ └────────────┘ └────────────┘
100 | ```
101 |
102 |
103 |
104 | - 官方给出的如上架构概览图是小于实际源码的,但是不影响理解。
105 | - 官方的spec项目中,有较多文档说明已经滞后于源码,其引用的源码有些已经从go-filecoin源码中消失了,想深入分析的朋友建议可以结合源码和文档同步进行看。
106 | - 本文后面的章节中,只会简述各个层的设计目的,每一层的具体源码分析,将放到后面章节分享给大家。
107 |
108 |
109 |
110 |
111 |
112 | #### 4.2.2.2 IPFS与filecoin在技术架构层面的关系
113 |
114 | 
115 |
116 | - IPFS与filecoin同样采用IPLD结构,数据结构是互通的,简而言之,在IPFS之上存储的数据,filecoin可以读取。filecoin存储的未密封数据,IPFS也是可以读取的。
117 | - IPFS与filecoin网络部分均复用libp2p部分。
118 | - filecoin复用了大量IPFS组件,比如CID、IPLD、bitswap等等。
119 |
120 |
121 |
122 |
123 |
124 | ## 4.3 网络层
125 |
126 | - 网络层的实现依赖协议实验室的libp2p项目,如果不熟悉的可以先简单记住如下要点,后面笔者考虑视情况补充IPFS/libp2p的相关分享。
127 | - libp2p的网络层实现了节点之间的联通性问题,包括节点发现、NAT穿透、pubsub、relay等。
128 |
129 | - libp2p的路由层的主要目的,包括节点路由、内容路由、DHT键值存储。
130 |
131 | - multistream需要理解,filecoin的协议层之协议定义就是基于mulitistream的。
132 |
133 | - filecoin网络层的目的
134 |
135 | - 处理请求信息、回复响应信息,包括存储订单处理、检索请求处理、区块同步等等。
136 |
137 |
138 |
139 |
140 |
141 | ## 4.4 协议层
142 |
143 | 协议层主要处理应用级的逻辑,状态切换等,具体会通过api层调用具体的core服务进行处理。
144 |
145 |
146 | ### 4.4.1 hello握手协议
147 | - 协议名称: /fil/hello/1.0.0
148 | - 目的:
149 | - 本节点上线,向其他节点发起hello握手请求,进而进行区块同步。
150 | - 响应其他新上线的节点hello握手请求,触发其进行区块同步。
151 |
152 |
153 |
154 | ### 4.4.2 存储协议
155 | #### 4.4.2.1 存储矿工
156 |
157 | - 协议名称:/fil/storage/mk/1.0.0、 /fil/storage/qry/1.0.0
158 | - 目的:
159 | - 接受客户发起的订单交易请求、查询订单请求,会提交对应处理状态到区块链上(包括清单处理成功或失败;密封成功或者失败等等)。
160 | - 更新本地的密封或者订单状态。
161 |
162 |
163 |
164 | #### 4.4.2.2 存储客户
165 |
166 | - 采用上述的/fil/storage/mk/1.0.0、 /fil/storage/qry/1.0.0协议,建立multistream,向矿工发起交易或者查询交易状态。
167 |
168 |
169 |
170 | ### 4.4.3 检索协议
171 |
172 | #### 4.4.3.1 检索矿工
173 | - 协议名称:/fil/retrieval/free/0.0.0
174 | - 目的:
175 |
176 | - 接受客户的检索请求,并响应处理
177 |
178 | - 注意:目前仅仅支持free检索,白皮书所描述的完整检索功能尚未实现。
179 |
180 |
181 | #### 4.4.3.2 检索客户
182 |
183 | - 采用上述的/fil/retrieval/free/0.0.0协议,建立multistream,向矿工发起检索请求。
184 |
185 |
186 | ### 4.4.4 心跳协议
187 |
188 | - 协议名称:fil/heartbeat/1.0.0
189 | - 目的:
190 | - 启动之后向指定节点发起心跳。
191 | - 如前面3.2.1章节中的设置Nick Name,以及激活极点,都属于心跳协议实现的。
192 |
193 |
194 |
195 | ## 4.5 REST/CMD
196 |
197 | - 这个应该不用多解释,提供cmd或者REST接口供用户操作具体节点。第三章中的开发网络使用基本都是使用的CMD方式。
198 |
199 |
200 |
201 | ## 4.6 内部api层
202 | ### 4.6.1 node对象
203 | - filecoin的node节点是一个上帝对象,从下面Node的结构可以看出,基本贯穿了filecoin的整个业务。
204 |
205 | - 为了解决耦合性问题,尤其是后续轻节点的实现,官方已经开始将原有的api包,往plumbing包以及porcelain包迁移,这样做的目的是让系统具备更好的解耦性,以满足更灵活的需求。
206 |
207 | - plumbing和 porcelain模式也是借鉴git的思维。
208 |
209 |
210 | ```
211 | ▼+Node : struct
212 | [fields]
213 | +AddNewlyMinedBlock : newBlockFunc
214 | +BlockSub : ps.Subscription
215 | +Blockstore : bstore.Blockstore
216 | +Bootstrapper : *filnet.Bootstrapper
217 | +ChainReader : chain.ReadStore
218 | +Consensus : consensus.Protocol
219 | +Exchange : exchange.Interface
220 | +HeaviestTipSetCh : chan interface{}
221 | +HeaviestTipSetHandled : func()
222 | +HelloSvc : *hello.Handler
223 | +MessageSub : ps.Subscription
224 | +MiningScheduler : mining.Scheduler
225 | +MsgPool : *core.MessagePool
226 | +OfflineMode : bool
227 | +OnlineStore : *hamt.CborIpldStore
228 | +PeerHost : host.Host
229 | +Ping : *ping.PingService
230 | +PorcelainAPI : *porcelain.API
231 | +PowerTable : consensus.PowerTableView
232 | +Repo : repo.Repo
233 | +RetrievalClient : *retrieval.Client
234 | +RetrievalMiner : *retrieval.Miner
235 | +Router : routing.IpfsRouting
236 | +StorageMiner : *storage.Miner
237 | +StorageMinerClient : *storage.Client
238 | +Syncer : chain.Syncer
239 | +Wallet : *wallet.Wallet
240 | -blockTime : time.Duration
241 | -blockservice : bserv.BlockService
242 | -cancelMining : context.CancelFunc
243 | -cancelSubscriptionsCtx : context.CancelFunc
244 | -cborStore : *hamt.CborIpldStore
245 | -host : host.Host
246 | -lookup : lookup.PeerLookupService
247 | -mining
248 | -miningCtx : context.Context
249 | -miningDoneWg : *sync.WaitGroup
250 | -sectorBuilder : sectorbuilder.SectorBuilder
251 | [methods]
252 | +BlockHeight() : *types.BlockHeight, error
253 | +BlockService() : bserv.BlockService
254 | +CborStore() : *hamt.CborIpldStore
255 | +ChainReadStore() : chain.ReadStore
256 | +CreateMiner(ctx context.Context, accountAddr address.Address, gasPrice types.AttoFIL, gasLimit types.GasUnits, pledge uint64, pid libp2ppeer.ID, collateral *types.AttoFIL) : *address.Address, error
257 | +GetBlockTime() : time.Duration
258 | +Host() : host.Host
259 | +Lookup() : lookup.PeerLookupService
260 | +MiningSignerAddress() : address.Address
261 | +MiningTimes() : time.Duration, time.Duration
262 | +NewAddress() : address.Address, error
263 | +SectorBuilder() : sectorbuilder.SectorBuilder
264 | +SetBlockTime(blockTime time.Duration)
265 | +Start(ctx context.Context) : error
266 | +StartMining(ctx context.Context) : error
267 | +Stop(ctx context.Context)
268 | +StopMining(ctx context.Context)
269 | -addNewlyMinedBlock(ctx context.Context, b *types.Block)
270 | -cancelSubscriptions()
271 | -getLastUsedSectorID(ctx context.Context, minerAddr address.Address) : uint64, error
272 | -getMinerActorPubKey() : []byte, error
273 | -handleNewHeaviestTipSet(ctx context.Context, head types.TipSet)
274 | -handleNewMiningOutput(miningOutCh chan mining.Output)
275 | -handleSubscription(ctx context.Context, f pubSubProcessorFunc, fname string, s ps.Subscription, sname string)
276 | -isMining() : bool
277 | -miningAddress() : address.Address, error
278 | -miningOwnerAddress(ctx context.Context, miningAddr address.Address) : address.Address, error
279 | -saveMinerConfig(minerAddr address.Address, signerAddr address.Address) : error
280 | -setIsMining(isMining bool)
281 | -setupMining(ctx context.Context) : error
282 | [functions]
283 | +New(ctx context.Context, opts ...ConfigOpt) : *Node, error
284 | ```
285 |
286 |
287 |
288 | ### 4.6.2 api包
289 | - 这基本上是早期实现的api接口,对应4.2.2.1中的Core API,严重依赖于Node,耦合性大。现在在逐步迁移。感兴趣可以参照如下源码逐层深入去看。
290 |
291 | ```
292 | package: api
293 | location: api/api.go
294 |
295 | type API interface {
296 | Actor() Actor
297 | Address() Address
298 | Client() Client
299 | Daemon() Daemon
300 | Dag() Dag
301 | ID() ID
302 | Log() Log
303 | Miner() Miner
304 | Mining() Mining
305 | Paych() Paych
306 | Ping() Ping
307 | RetrievalClient() RetrievalClient
308 | Swarm() Swarm
309 | Version() Version
310 | }
311 | ```
312 |
313 |
314 |
315 | ### 4.6.3 plumbing和porcelain包
316 |
317 | - plumbing api简而言之,是实现底层的公共api,其不依赖于Node的实现。
318 | - 而porcelain api则是在plumbing api之上,更偏应用级的调用,同样不依赖于Node实现。
319 |
320 | ```
321 | 源码分别参见go-filecoin目录下,./plumbing和./porcelain。
322 | ```
323 |
324 |
325 |
326 |
327 | ## 4.7 core服务层
328 |
329 | > 关于核心业务调度以及业务持久化的底层处理基本都在这一层,包含但不限于如下服务。
330 |
331 | ### 4.7.1 Message pool
332 |
333 | > 消息池主要保存还未上链的消息。
334 |
335 | ### 4.7.2 Chain store
336 |
337 | > 链存储主要持久化链信息,注意同步区块的逻辑是在协议层的hello协议所出发的。
338 |
339 | ### 4.7.3 Processor
340 |
341 | > 处理事务消息如何驱动状态转换。
342 |
343 | ### 4.7.4 Block service
344 |
345 | > 负责IPLD数据的内容寻址,包括区块链等。
346 |
347 | ### 4.7.5 Wallet
348 |
349 | > 钱包管理。
350 |
--------------------------------------------------------------------------------
/5.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之五:filecoin源码分析之协议层心跳协议
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 5 filecoin源码协议层分析之心跳协议
8 | - 5.1 源码信息
9 | - 5.2 源码分析
10 | - 5.2.1 数据结构
11 | - 5.2.2 方法
12 | - 5.2.3 函数
13 | - 5.2.4 实例化及业务逻辑
14 |
15 |
16 | ## 5.1 源码信息
17 |
18 | - version
19 | - master分支 619b0eb1(2019年3月2日)
20 | - package
21 | - metrics
22 | - location
23 | - metrics/heartbeat.go
24 | - node/node.go
25 |
26 | ## 5.2 源码分析
27 | ### 5.2.1 数据结构
28 |
29 | - 定义心跳协议名称以及连接超时时间
30 |
31 | ```
32 | // HeartbeatProtocol is the libp2p protocol used for the heartbeat service
33 | const (
34 | HeartbeatProtocol = "fil/heartbeat/1.0.0"
35 | // Minutes to wait before logging connection failure at ERROR level
36 | connectionFailureErrorLogPeriodMinutes = 10 * time.Minute
37 | )
38 | ```
39 |
40 | - 定义心跳信息结构
41 | - 节点的区块头
42 | - 节点的区块高度
43 | - 节点的昵称
44 | - 是否在区块同步中(TODO)
45 | - 矿工地址(如果没有挖矿,这里为零地址)
46 |
47 | ```
48 | // Heartbeat contains the information required to determine the current state of a node.
49 | // Heartbeats are used for aggregating information about nodes in a log aggregator
50 | // to support alerting and devnet visualization.
51 | type Heartbeat struct {
52 | // Head represents the heaviest tipset the nodes is mining on
53 | Head string
54 | // Height represents the current height of the Tipset
55 | Height uint64
56 | // Nickname is the nickname given to the filecoin node by the user
57 | Nickname string
58 | // TODO: add when implemented
59 | // Syncing is `true` iff the node is currently syncing its chain with the network.
60 | // Syncing bool
61 |
62 | // Address of this node's active miner. Can be empty - will return the zero address
63 | MinerAddress address.Address
64 | }
65 | ```
66 |
67 | - 心跳服务结构体
68 | - 主机结构体:对应libp2p主机
69 | - 心跳配置
70 | - 区块头获取
71 | - 挖矿地址获取
72 | - stream锁
73 | - stream
74 |
75 | ```
76 | // HeartbeatService is responsible for sending heartbeats.
77 | type HeartbeatService struct {
78 | Host host.Host
79 | Config *config.HeartbeatConfig
80 |
81 | // A function that returns the heaviest tipset
82 | HeadGetter func() types.TipSet
83 |
84 | // A function that returns the miner's address
85 | MinerAddressGetter func() address.Address
86 |
87 | streamMu sync.Mutex
88 | stream net.Stream
89 | }
90 | ```
91 |
92 | - 定义心跳服务Option函数
93 | - 函数入参为心跳服务结构体,主要用于对心跳服务结构体传参或者解析
94 | ```
95 | // HeartbeatServiceOption is the type of the heartbeat service's functional options.
96 | type HeartbeatServiceOption func(service *HeartbeatService)
97 | ```
98 |
99 | ### 5.2.2 方法
100 | - 获取心跳服务的stream实例
101 |
102 | ```
103 | // Stream returns the HeartbeatService stream. Safe for concurrent access.
104 | // Stream is a libp2p connection that heartbeat messages are sent over to an aggregator.
105 | func (hbs *HeartbeatService) Stream() net.Stream {
106 | hbs.streamMu.Lock()
107 | defer hbs.streamMu.Unlock()
108 | return hbs.stream
109 | }
110 | ```
111 |
112 | - 设置心跳服务的stream实例
113 |
114 | ```
115 | // SetStream sets the stream on the HeartbeatService. Safe for concurrent access.
116 | func (hbs *HeartbeatService) SetStream(s net.Stream) {
117 | hbs.streamMu.Lock()
118 | defer hbs.streamMu.Unlock()
119 | hbs.stream = s
120 | }
121 | ```
122 |
123 | - 定时确认连接性,并调用运行心跳服务
124 |
125 | ```
126 | // Start starts the heartbeat service by, starting the connection loop. The connection
127 | // loop will attempt to connected to the aggregator service, once a successful
128 | // connection is made with the aggregator service hearbeats will be sent to it.
129 | // If the connection is broken the heartbeat service will attempt to reconnect via
130 | // the connection loop. Start will not return until context `ctx` is 'Done'.
131 | func (hbs *HeartbeatService) Start(ctx context.Context) {
132 | log.Debug("starting heartbeat service")
133 |
134 | rd, err := time.ParseDuration(hbs.Config.ReconnectPeriod)
135 | if err != nil {
136 | log.Errorf("invalid heartbeat reconnectPeriod: %s", err)
137 | return
138 | }
139 |
140 | //启动重连定时器
141 | reconTicker := time.NewTicker(rd)
142 | defer reconTicker.Stop()
143 | // Timestamp of the first connection failure since the last successful connection.
144 | // Zero initially and while connected.
145 | var failedAt time.Time
146 | // Timestamp of the last ERROR log (or of failure, before the first ERROR log).
147 | var erroredAt time.Time
148 | for {
149 | select {
150 | case <-ctx.Done():
151 | return
152 | case <-reconTicker.C:
153 | //重连定时周期到,重新连接
154 | if err := hbs.Connect(ctx); err != nil {
155 | // Logs once as a warning immediately on failure, then as error every 10 minutes.
156 | now := time.Now()
157 | logfn := log.Debugf
158 | if failedAt.IsZero() { // First failure since connection
159 | failedAt = now
160 | erroredAt = failedAt // Start the timer on raising to ERROR level
161 | logfn = log.Warningf
162 | } else if now.Sub(erroredAt) > connectionFailureErrorLogPeriodMinutes {
163 | logfn = log.Errorf
164 | erroredAt = now // Reset the timer
165 | }
166 | failureDuration := now.Sub(failedAt)
167 | logfn("Heartbeat service failed to connect for %s: %s", failureDuration, err)
168 | // failed to connect, continue reconnect loop
169 | continue
170 | }
171 | failedAt = time.Time{}
172 |
173 | // we connected, send heartbeats!
174 | // Run will block until it fails to send a heartbeat.
175 | //如果连接成功,运行心跳服务
176 | if err := hbs.Run(ctx); err != nil {
177 | log.Warning("disconnecting from aggregator, failed to send heartbeat")
178 | continue
179 | }
180 | }
181 | }
182 | }
183 | ```
184 |
185 |
186 | - 运行心跳服务
187 |
188 | ```
189 | // Run is called once the heartbeat service connects to the aggregator. Run
190 | // send the actual heartbeat. Run will block until `ctx` is 'Done`. An error will
191 | // be returned if Run encounters an error when sending the heartbeat and the connection
192 | // to the aggregator will be closed.
193 | func (hbs *HeartbeatService) Run(ctx context.Context) error {
194 | bd, err := time.ParseDuration(hbs.Config.BeatPeriod)
195 | if err != nil {
196 | log.Errorf("invalid heartbeat beatPeriod: %s", err)
197 | return err
198 | }
199 |
200 | //启动心跳定时器
201 | beatTicker := time.NewTicker(bd)
202 | defer beatTicker.Stop()
203 |
204 | //通过encoder进行流写入
205 | // TODO use cbor instead of json
206 | encoder := json.NewEncoder(hbs.stream)
207 | for {
208 | select {
209 | case <-ctx.Done():
210 | return nil
211 | case <-beatTicker.C:
212 | //心跳定时周期到,调用Beat方法获取心跳参数
213 | hb := hbs.Beat()
214 | //写入流,发起心跳
215 | if err := encoder.Encode(hb); err != nil {
216 | //发生错误会关闭流连接
217 | hbs.stream.Conn().Close() // nolint: errcheck
218 | return err
219 | }
220 | }
221 | }
222 | }
223 | ```
224 |
225 | - 获取心跳参数
226 |
227 | ```
228 | // Beat will create a heartbeat.
229 | func (hbs *HeartbeatService) Beat() Heartbeat {
230 | nick := hbs.Config.Nickname
231 | ts := hbs.HeadGetter()
232 | tipset := ts.ToSortedCidSet().String()
233 | height, err := ts.Height()
234 | if err != nil {
235 | log.Warningf("heartbeat service failed to get chain height: %s", err)
236 | }
237 | addr := hbs.MinerAddressGetter()
238 | return Heartbeat{
239 | Head: tipset,
240 | Height: height,
241 | Nickname: nick,
242 | MinerAddress: addr,
243 | }
244 | }
245 | ```
246 |
247 | - 心跳流连接
248 | ```
249 | // Connect will connects to `hbs.Config.BeatTarget` or returns an error
250 | func (hbs *HeartbeatService) Connect(ctx context.Context) error {
251 | log.Debugf("Heartbeat service attempting to connect, targetAddress: %s", hbs.Config.BeatTarget)
252 | targetMaddr, err := ma.NewMultiaddr(hbs.Config.BeatTarget)
253 | if err != nil {
254 | return err
255 | }
256 |
257 | pid, err := targetMaddr.ValueForProtocol(ma.P_P2P)
258 | if err != nil {
259 | return err
260 | }
261 |
262 | peerid, err := peer.IDB58Decode(pid)
263 | if err != nil {
264 | return err
265 | }
266 |
267 | // Decapsulate the /p2p/ part from the target
268 | // /ip4//p2p/ becomes /ip4/
269 | targetPeerAddr, _ := ma.NewMultiaddr(
270 | fmt.Sprintf("/p2p/%s", peer.IDB58Encode(peerid)))
271 | targetAddr := targetMaddr.Decapsulate(targetPeerAddr)
272 |
273 | hbs.Host.Peerstore().AddAddr(peerid, targetAddr, pstore.PermanentAddrTTL)
274 |
275 | // 建立心跳服务流
276 | s, err := hbs.Host.NewStream(ctx, peerid, HeartbeatProtocol)
277 | if err != nil {
278 | log.Debugf("failed to open stream, peerID: %s, targetAddr: %s %s", peerid, targetAddr, err)
279 | return err
280 | }
281 | log.Infof("successfully to open stream, peerID: %s, targetAddr: %s", peerid, targetAddr)
282 |
283 | //设置流函数
284 | hbs.SetStream(s)
285 | return nil
286 | }
287 | ```
288 |
289 | ### 5.2.3 函数
290 | - 向心跳服务结构体传参,用于设置获取矿工地址函数
291 |
292 | ```
293 | // WithMinerAddressGetter returns an option that can be used to set the miner address getter.
294 | func WithMinerAddressGetter(ag func() address.Address) HeartbeatServiceOption {
295 | return func(service *HeartbeatService) {
296 | service.MinerAddressGetter = ag
297 | }
298 | }
299 | ```
300 | - 获取默认的矿工地址
301 |
302 | ```
303 | func defaultMinerAddressGetter() address.Address {
304 | return address.Address{}
305 | }
306 | ```
307 |
308 | - 实例化心跳服务,具体的实例化在node包中实现。
309 |
310 | ```
311 | // NewHeartbeatService returns a HeartbeatService
312 | func NewHeartbeatService(h host.Host, hbc *config.HeartbeatConfig, hg func() types.TipSet, options ...HeartbeatServiceOption) *HeartbeatService {
313 | srv := &HeartbeatService{
314 | Host: h,
315 | Config: hbc,
316 | HeadGetter: hg,
317 | MinerAddressGetter: defaultMinerAddressGetter,
318 | }
319 |
320 | // 设置心跳服务的获取矿工属性,这会覆盖到上面设置的默认矿工地址
321 | for _, option := range options {
322 | option(srv)
323 | }
324 |
325 | return srv
326 | }
327 | ```
328 |
329 | ### 5.2.4 实例化及业务逻辑
330 | - 主要由node调用,location:node/node.go,主要逻辑如下
331 |
332 | - 在node的启动方法中,调用node.setupHeartbeatServices方法,建立心跳服务
333 |
334 | ```
335 | // Start boots up the node.
336 | func (node *Node) Start(ctx context.Context) error {
337 | ......
338 |
339 | if err := node.setupHeartbeatServices(ctx); err != nil {
340 | return errors.Wrap(err, "failed to start heartbeat services")
341 | }
342 |
343 | return nil
344 | }
345 | ```
346 |
347 | - 建立心跳服务,具体见如下注释
348 |
349 | ```
350 | func (node *Node) setupHeartbeatServices(ctx context.Context) error {
351 | // 设置“矿工地址获取函数”
352 | mag := func() address.Address {
353 | addr, err := node.miningAddress()
354 | // the only error miningAddress() returns is ErrNoMinerAddress.
355 | // if there is no configured miner address, simply send a zero
356 | // address across the wire.
357 | if err != nil {
358 | return address.Address{}
359 | }
360 | return addr
361 | }
362 |
363 | // 存在心跳目标的时候,实例化心跳服务实例
364 | // start the primary heartbeat service
365 | if len(node.Repo.Config().Heartbeat.BeatTarget) > 0 {
366 | //调用metrics包中的建立心跳服务实例、以及启动心跳服务实例方法
367 | hbs := metrics.NewHeartbeatService(node.Host(), node.Repo.Config().Heartbeat, node.ChainReader.Head, metrics.WithMinerAddressGetter(mag))
368 | go hbs.Start(ctx)
369 | }
370 |
371 | // 确认是否用户有通过环境变量配置额外的心跳告警服务(自定义指向其他节点),根据用户配置的数目,拉起对应的多线程心跳服务。
372 | // check if we want to connect to an alert service. An alerting service is a heartbeat
373 | // service that can trigger alerts based on the contents of heatbeats.
374 | if alertTarget := os.Getenv("FIL_HEARTBEAT_ALERTS"); len(alertTarget) > 0 {
375 | ahbs := metrics.NewHeartbeatService(node.Host(), &config.HeartbeatConfig{
376 | BeatTarget: alertTarget,
377 | BeatPeriod: "10s",
378 | ReconnectPeriod: "10s",
379 | Nickname: node.Repo.Config().Heartbeat.Nickname,
380 | }, node.ChainReader.Head, metrics.WithMinerAddressGetter(mag))
381 | go ahbs.Start(ctx)
382 | }
383 | return nil
384 | }
385 | ```
386 |
--------------------------------------------------------------------------------
/6.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之六:filecoin源码协议层分析之hello握手协议
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 6 filecoin源码协议层分析之hello握手协议
8 | - 6.1 目的
9 | - 6.2 源码信息
10 | - 6.3 源码分析
11 | - 6.3.1 数据结构
12 | - 6.3.2 方法
13 | - 6.3.3 函数
14 | - 6.3.4 实例化及业务逻辑
15 |
16 | ## 6.1 目的
17 | - 处理节点上线后的区块同步握手。
18 |
19 | ## 6.2 源码信息
20 |
21 | - version
22 | - master分支 619b0eb1(2019年3月2日)
23 | - package
24 | - hello
25 | - location
26 | - protocol/hello
27 | - node/node.go
28 |
29 | ## 6.3 源码分析
30 | ### 6.3.1 数据结构
31 |
32 | - 定义协议名称
33 |
34 | ```
35 | // Protocol is the libp2p protocol identifier for the hello protocol.
36 | const protocol = "/fil/hello/1.0.0"
37 | ```
38 |
39 | - 定义hello协议消息体结构
40 | - TipSet切片
41 | - TipSet高度
42 | - 创世区块cid
43 |
44 | ```
45 | // Message is the data structure of a single message in the hello protocol.
46 | type Message struct {
47 | HeaviestTipSetCids []cid.Cid
48 | HeaviestTipSetHeight uint64
49 | GenesisHash cid.Cid
50 | }
51 | ```
52 |
53 | - 同步回调函数类型定义
54 |
55 | ```
56 | type syncCallback func(from peer.ID, cids []cid.Cid, height uint64)
57 | ```
58 |
59 | - 获取Tipset函数类型定义
60 |
61 | ```
62 | type getTipSetFunc func() types.TipSet
63 | ```
64 |
65 | - Handler结构体,当连接到其他节点的时候,其一,会发送包含本节点信息的hello 消息给对端节点; 其二, 对端也会回复一个包含对端节点信息的消息体过来。
66 | - host 对应libp2p上的主机
67 | - 创世区块cid
68 | - 区块同步回调函数
69 | - 获取TipSet的函数
70 |
71 | ```
72 | // Handler implements the 'Hello' protocol handler. Upon connecting to a new
73 | // node, we send them a message containing some information about the state of
74 | // our chain, and receive the same information from them. This is used to
75 | // initiate a chainsync and detect connections to forks.
76 | type Handler struct {
77 | host host.Host
78 |
79 | genesis cid.Cid
80 |
81 | // chainSyncCB is called when new peers tell us about their chain
82 | chainSyncCB syncCallback
83 |
84 | // getHeaviestTipSet is used to retrieve the current heaviest tipset
85 | // for filling out our hello messages.
86 | getHeaviestTipSet getTipSetFunc
87 | }
88 | ```
89 |
90 | - 错误的创世区块
91 |
92 | ```
93 | // ErrBadGenesis is the error returned when a missmatch in genesis blocks happens.
94 | var ErrBadGenesis = fmt.Errorf("bad genesis block")
95 | ```
96 |
97 | - 以上基本是作为hello客户端的一些定义,以下作为hello服务端的一些定义
98 |
99 | ```
100 | // New peer connection notifications
101 | type helloNotify Handler
102 |
103 | // 连接超时时间
104 | const helloTimeout = time.Second * 10
105 | ```
106 |
107 | ### 6.3.2 方法
108 | #### 6.3.2.1 Handler 方法
109 |
110 | - 流函数处理,接收远端节点的hello消息
111 |
112 |
113 | ```
114 | func (h *Handler) handleNewStream(s net.Stream) {
115 | defer s.Close() // nolint: errcheck
116 |
117 | //获取远端节点实例
118 | from := s.Conn().RemotePeer()
119 |
120 | var hello Message
121 | // 读取流信息到hello结构体中
122 | if err := cbu.NewMsgReader(s).ReadMsg(&hello); err != nil {
123 | log.Warningf("bad hello message from peer %s: %s", from, err)
124 | return
125 | }
126 |
127 | // 调用processHelloMessage方法对接收到的消息进行处理
128 | switch err := h.processHelloMessage(from, &hello); err {
129 | // 如果创世区块不一样,关闭流连接退出,不予处理
130 | case ErrBadGenesis:
131 | log.Warningf("genesis cid: %s does not match: %s, disconnecting from peer: %s", &hello.GenesisHash, h.genesis, from)
132 | s.Conn().Close() // nolint: errcheck
133 | return
134 | case nil: // ok, noop
135 | default:
136 | log.Error(err)
137 | }
138 | }
139 | ```
140 |
141 | - 处理hello消息
142 |
143 | ```
144 | func (h *Handler) processHelloMessage(from peer.ID, msg *Message) error {
145 | // 如果创世区块不一样,报错
146 | if !msg.GenesisHash.Equals(h.genesis) {
147 | return ErrBadGenesis
148 | }
149 |
150 | // 调用区块同步方法
151 | // 此回调函数实在node包实例化hello协议的时候中定义的
152 | h.chainSyncCB(from, msg.HeaviestTipSetCids, msg.HeaviestTipSetHeight)
153 | return nil
154 | }
155 | ```
156 |
157 | - 响应远端节点的连接,回复hello消息体
158 |
159 | ```
160 | func (h *Handler) getOurHelloMessage() *Message {
161 | heaviest := h.getHeaviestTipSet()
162 | height, err := heaviest.Height()
163 | if err != nil {
164 | panic("somehow heaviest tipset is empty")
165 | }
166 |
167 | return &Message{
168 | GenesisHash: h.genesis,
169 | HeaviestTipSetCids: heaviest.ToSortedCidSet().ToSlice(),
170 | HeaviestTipSetHeight: height,
171 | }
172 | }
173 |
174 | func (h *Handler) sayHello(ctx context.Context, p peer.ID) error {
175 | s, err := h.host.NewStream(ctx, p, protocol)
176 | if err != nil {
177 | return err
178 | }
179 | defer s.Close() // nolint: errcheck
180 |
181 | //获取本节点的hello消息体
182 | msg := h.getOurHelloMessage()
183 |
184 | //向远端节点发送消息体
185 | return cbu.NewMsgWriter(s).WriteMsg(&msg)
186 | }
187 | ```
188 |
189 | #### 6.3.2.2 helloNotify方法
190 |
191 | - hello方法,返回一个handler实例
192 |
193 | ```
194 | func (hn *helloNotify) hello() *Handler {
195 | return (*Handler)(hn)
196 | }
197 | ```
198 |
199 | - helloNotify实现了libp2p-net/interface.go中的Notifiee接口
200 |
201 | ```
202 | func (hn *helloNotify) Connected(n net.Network, c net.Conn) {
203 | go func() {
204 | ctx, cancel := context.WithTimeout(context.Background(), helloTimeout)
205 | defer cancel()
206 | p := c.RemotePeer()
207 | // 有其他节点连接的时候调用sayHello,发送hello消息体
208 | if err := hn.hello().sayHello(ctx, p); err != nil {
209 | log.Warningf("failed to send hello handshake to peer %s: %s", p, err)
210 | }
211 | }()
212 | }
213 |
214 | func (hn *helloNotify) Listen(n net.Network, a ma.Multiaddr) {}
215 | func (hn *helloNotify) ListenClose(n net.Network, a ma.Multiaddr) {}
216 | func (hn *helloNotify) Disconnected(n net.Network, c net.Conn) {}
217 | func (hn *helloNotify) OpenedStream(n net.Network, s net.Stream) {}
218 | func (hn *helloNotify) ClosedStream(n net.Network, s net.Stream) {}
219 | ```
220 |
221 | ### 6.3.3 函数
222 |
223 | - 创建hello实例
224 |
225 | ```
226 | // New creates a new instance of the hello protocol and registers it to
227 | // the given host, with the provided callbacks.
228 | func New(h host.Host, gen cid.Cid, syncCallback syncCallback, getHeaviestTipSet getTipSetFunc) *Handler {
229 | hello := &Handler{
230 | host: h,
231 | genesis: gen,
232 | chainSyncCB: syncCallback,
233 | getHeaviestTipSet: getHeaviestTipSet,
234 | }
235 |
236 | //设置流处理回调函数
237 | h.SetStreamHandler(protocol, hello.handleNewStream)
238 |
239 | //注册网络状态改变通知回调函数
240 | // register for connection notifications
241 | h.Network().Notify((*helloNotify)(hello))
242 |
243 | return hello
244 | }
245 |
246 |
247 |
248 | //上文中的helloNotify 实现了libp2p-net/interface.go中的Notifiee接口
249 | // Notifiee is an interface for an object wishing to receive
250 | // notifications from a Network.
251 | type Notifiee interface {
252 | Listen(Network, ma.Multiaddr) // called when network starts listening on an addr
253 | ListenClose(Network, ma.Multiaddr) // called when network stops listening on an addr
254 | Connected(Network, Conn) // called when a connection opened
255 | Disconnected(Network, Conn) // called when a connection closed
256 | OpenedStream(Network, Stream) // called when a stream opened
257 | ClosedStream(Network, Stream) // called when a stream closed
258 |
259 | // TODO
260 | // PeerConnected(Network, peer.ID) // called when a peer connected
261 | // PeerDisconnected(Network, peer.ID) // called when a peer disconnected
262 | }
263 | ```
264 |
265 | ### 6.3.4 实例化及业务逻辑
266 |
267 | - location: node/node.go
268 |
269 | - Node节点中定义了hello服务
270 |
271 | ```
272 | type Node struct {
273 | ......
274 |
275 | HelloSvc *hello.Handler
276 | ......
277 | }
278 | ```
279 |
280 | - 启动hello服务
281 |
282 | ```
283 | // Start boots up the node.
284 | func (node *Node) Start(ctx context.Context) error {
285 | ......
286 |
287 | // Start up 'hello' handshake service
288 | // 定义区块同步的回调函数
289 | syncCallBack := func(pid libp2ppeer.ID, cids []cid.Cid, height uint64) {
290 | // TODO it is possible the syncer interface should be modified to
291 | // make use of the additional context not used here (from addr + height).
292 | // To keep things simple for now this info is not used.
293 | // 触发调用会启动同步区块的动作
294 | err := node.Syncer.HandleNewBlocks(context.Background(), cids)
295 | if err != nil {
296 | log.Infof("error handling blocks: %s", types.NewSortedCidSet(cids...).String())
297 | }
298 | }
299 | //实例化hello服务
300 | node.HelloSvc = hello.New(node.Host(), node.ChainReader.GenesisCid(), syncCallBack, node.ChainReader.Head)
301 |
302 | ......
303 | }
304 | ```
305 |
--------------------------------------------------------------------------------
/7.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之七:filecoin源码协议层分析之存储协议
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 7 filecoin源码协议层分析之存储协议
8 | - 7.1 协议概览图
9 | - 7.2 源码信息
10 | - 7.3 源码分析
11 | - 7.3.1 存储矿工
12 | - 7.3.2 存储客户
13 |
14 | ## 7.1 协议概览图
15 | 
16 |
17 | ## 7.2 源码信息
18 |
19 | - version
20 | - master分支 619b0eb1(2019年3月2日)
21 | - package
22 | - storage
23 | - location
24 | - protocol/storage
25 |
26 | ## 7.3 源码分析
27 |
28 | ### 7.3.1 存储矿工
29 |
30 | ```
31 | ▼ package
32 | storage
33 |
34 | ▶ imports
35 |
36 | ▼ constants
37 | //等待密封数据前缀
38 | -dealsAwatingSealDatastorePrefix
39 | // 存储交易协议名称:"/fil/storage/mk/1.0.0"
40 | -makeDealProtocol
41 | // 矿工数据存储前缀
42 | -minerDatastorePrefix
43 | // 存储查询协议名称:"/fil/storage/qry/1.0.0"
44 | -queryDealProtocol
45 | // Gas及Gas限制
46 | -submitPostGasLimit
47 | -submitPostGasPrice
48 | // 支付通道建立等待时间
49 | -waitForPaymentChannelDuration
50 |
51 | ▼ variables
52 | -log
53 |
54 | ▼+Miner : struct
55 | [fields]
56 | // 交易集合
57 | -deals : map[cid.Cid]*storageDeal
58 | // 等待密封结构体
59 | -dealsAwaitingSeal : *dealsAwaitingSealStruct
60 | // 交易的资源对象
61 | -dealsDs : repo.Datastore
62 | // 交易锁
63 | -dealsLk : sync.Mutex
64 | // 存储矿工地址
65 | -minerAddr : address.Address
66 | // 节点的Owner地址
67 | -minerOwnerAddr : address.Address
68 | // 节点对象,有定义存储矿工必须实现的接口
69 | -node : node
70 | // 存储矿工的高层API
71 | -porcelainAPI : minerPorcelain
72 | // 是否在生成时空证明中,以及对应的锁
73 | -postInProcess : *types.BlockHeight
74 | -postInProcessLk : sync.Mutex
75 | // 接受交易以及拒绝交易
76 | -proposalAcceptor : func(ctx context.Context, m *Miner, p *DealProposal) *DealResponse, error
77 | -proposalRejector : func(ctx context.Context, m *Miner, p *DealProposal, reason string) *DealResponse, error
78 |
79 | [methods]
80 | // 密封消息提交到区块链时候,所执行的回调函数,在node中执行
81 | // 1 失败,则调用dealsAwaitingSeal.fail
82 | // 2 成功,则调用dealsAwaitingSeal.success
83 | // 3 成功之后,需要保存密封扇区信息,如果失败调用dealsAwaitingSeal.fail
84 | +OnCommitmentAddedToChain(sector *sectorbuilder.SealedSectorMetadata, err error)
85 | // 新区块产生的回调,由node调用,它将会触发新的存储证明
86 | // 如果时空证明过期,将会在新的周期重新出发时空证明
87 | +OnNewHeaviestTipSet(ts types.TipSet)
88 | // 由handleQueryDeal调用,返回查询结果
89 | +Query(ctx context.Context, c cid.Cid) : *DealResponse
90 | // 生成时空证明
91 | -generatePoSt(commRs []proofs.CommR, challenge proofs.PoStChallengeSeed) : proofs.PoStProof, []uint64, error
92 | // 获取支付通道信息
93 | // 1 等待支付通道建立完成
94 | // 2 获取支付通道信息并返回
95 | // 3 支付信息包括:合约地址、支付者地址、通道信息、支付通道消息cid、支付凭证合集
96 | -getPaymentChannel(ctx context.Context, p *DealProposal) : *paymentbroker.PaymentChannel, error
97 | // 获取新的时空证明时间
98 | -getProvingPeriodStart() : *types.BlockHeight, error
99 | // 获取存储矿工的特定交易
100 | -getStorageDeal(c cid.Cid) : *storageDeal
101 | // 获取存储矿工报价
102 | -getStoragePrice() : *types.AttoFIL, error
103 | // 存储交易请求的入口方法,交易请求流的handle函数
104 | // 1 读取流中交易请求信息
105 | // 2 调用receiveStorageProposal处理交易请求
106 | // 3 回复处理回复
107 | -handleMakeDeal(s inet.Stream)
108 | //解析具体流信息,处理查询请求,会调用Query请求
109 | -handleQueryDeal(s inet.Stream)
110 | // 从资源目录中加载交易信息到Miner实例中
111 | -loadDeals() : error
112 | // 加载待密封的信息
113 | -loadDealsAwaitingSeal() : error
114 | // 密封失败,更新响应信息
115 | -onCommitFail(dealCid cid.Cid, message string)
116 | // 密封成功,更新响应信息
117 | // 1 切换状态至Posted
118 | // 2 更新证明信息:扇区ID,副本信息,原始数据信息
119 | -onCommitSuccess(dealCid cid.Cid, sector *sectorbuilder.SealedSectorMetadata)
120 | // 处理存储交易
121 | // 1,获取存储交易信息
122 | // 2,数据处理,密封
123 | -processStorageDeal(c cid.Cid)
124 | // 处理交易请求
125 | // 1 检查签名的正确性
126 | // 2 检查支付信息正确性,调用validateDealPayment方法
127 | // 3 不合法调用proposalRejector(rejectProposal)拒绝请求;合法调用proposalAcceptor(acceptProposal)回复
128 | -receiveStorageProposal(ctx context.Context, sp *SignedDealProposal) : *DealResponse, error
129 | // 从Miner对象中存储交易信息到资源目录中
130 | -saveDeal(proposalCid cid.Cid) : error
131 | // 存储待密封信息至资源目录
132 | -saveDealsAwaitingSeal() : error
133 | // 提交时空证明
134 | // 1 产生随机种子
135 | // 2 根据时空证明输入长度,生成副本切片
136 | // 3 随机种子+副本切片作为输入生成时空证明
137 | // 4 调用高层接口发送消息
138 | -submitPoSt(start, end *types.BlockHeight, inputs []generatePostInput)
139 | // 更新交易响应消息
140 | -updateDealResponse(proposalCid cid.Cid, f func(*DealResponse)) : error
141 | // 检查支付信息的正确性
142 | // 1 客户出价必须高于矿工报价
143 | // 2 收款人必须为本节点矿工
144 | // 3 支付通道总资金必须大于矿工报价
145 | // 4 必须有交易凭证,且交易凭证总金额必须大于矿工报价
146 | -validateDealPayment(ctx context.Context, p *DealProposal) : error
147 |
148 | [functions]
149 | // 实例化存储矿工
150 | // 1 通过node传参赋值
151 | // 2 指定密封成功失败的回调函数
152 | // 3 设置交易请求以及交易查询的流handle方法
153 | +NewMiner(ctx context.Context, minerAddr, minerOwnerAddr address.Address, nd node, dealsDs repo.Datastore, porcelainAPI minerPorcelain) : *Miner, error
154 |
155 | ▼-dealsAwaitingSealStruct : struct
156 | [fields]
157 | // 从扇区id获取失败信息
158 | +FailedSectors : map[uint64]string
159 | // 从扇区id获取交易的cid
160 | +SectorsToDeals : map[uint64][]cid.Cid
161 | // 从扇区id获取sector元数据
162 | +SuccessfulSectors : map[uint64]*sectorbuilder.SealedSectorMetadata
163 | -l : sync.Mutex
164 | // 失败处理回调,在实例化Miner指向onCommitFail
165 | -onFail : func(dealCid cid.Cid, message string)
166 | // 成功处理回调,在实例化Miner指向onCommitSuccess
167 | -onSuccess : func(dealCid cid.Cid, sector *sectorbuilder.SealedSectorMetadata)
168 |
169 | [methods]
170 | // 对数据进行密封
171 | -add(sectorID uint64, dealCid cid.Cid)
172 | // 密封失败处理dealsAwaitingSeal.onFail
173 | -fail(sectorID uint64, message string)
174 | // 密封成功处理dealsAwaitingSeal.onSuccess
175 | -success(sector *sectorbuilder.SealedSectorMetadata)
176 |
177 | ▼-generatePostInput : struct
178 | [fields]
179 | // 副本merkle根
180 | -commD : proofs.CommD
181 | // 原始数据merkle根
182 | -commR : proofs.CommR
183 | // 中间数据merkle根
184 | -commRStar : proofs.CommRStar
185 | // 扇区ID
186 | -sectorID : uint64
187 |
188 | ▼-storageDeal : struct
189 | [fields]
190 | // 交易请求结构体
191 | +Proposal : *DealProposal
192 | // 交易请求响应结构体
193 | +Response : *DealResponse
194 |
195 | // 存储矿工高层API
196 | ▼-minerPorcelain : interface
197 | [methods]
198 | // 区块高度
199 | +ChainBlockHeight(ctx context.Context) : *types.BlockHeight, error
200 | // 获取配置
201 | +ConfigGet(dottedPath string) : interface{}, error
202 | // 发送、查询、等待消息
203 | +MessageQuery(ctx context.Context, optFrom, to address.Address, method string, params ...interface{}) : [][]byte, *exec.FunctionSignature, error
204 | +MessageSend(ctx context.Context, from, to address.Address, value *types.AttoFIL, gasPrice types.AttoFIL, gasLimit types.GasUnits, method string, params ...interface{}) : cid.Cid, error
205 | +MessageWait(ctx context.Context, msgCid cid.Cid, cb func(*types.Block, *types.SignedMessage, *types.MessageReceipt) error) : error
206 |
207 | ▼-node : interface
208 | [methods]
209 | // 区块高度
210 | +BlockHeight() : *types.BlockHeight, error
211 | // 区块服务,存储/查询服务
212 | +BlockService() : bserv.BlockService
213 | // 区块时间
214 | +GetBlockTime() : time.Duration
215 | // 主机信息
216 | +Host() : host.Host
217 | // 扇区创建,具体包含
218 | // 1 增加、读取piece;
219 | // 2 密封所有非空分期扇区
220 | // 3 密封结果通过返回,通过通道channel的方式
221 | // 4 获取扇区中最大的piece字节大小
222 | // 5 生成时空证明
223 | +SectorBuilder() : sectorbuilder.SectorBuilder
224 |
225 | ▼ functions
226 | // 存储交易信息之后,调用processStorageDeal处理交易信息
227 | -acceptProposal(ctx context.Context, sm *Miner, p *DealProposal) : *DealResponse, error
228 | // 获取具体文件大小
229 | -getFileSize(ctx context.Context, c cid.Cid, dserv ipld.DAGService) : uint64, error
230 | -init()
231 | // 存储交易信息,更新响应消息,并返回
232 | -rejectProposal(ctx context.Context, sm *Miner, p *DealProposal, reason string) : *DealResponse, error
233 | ```
234 |
235 | ### 7.3.2 存储客户
236 |
237 | ```
238 | ▼ package
239 | storage
240 |
241 | ▼ imports
242 |
243 | ▼ constants
244 | +ChannelExpiryInterval
245 | // Gas及Gas限制
246 | +CreateChannelGasLimit
247 | +CreateChannelGasPrice
248 | +ErrDupicateDeal
249 | // 建立Voucher的周期
250 | +VoucherInterval
251 | // 存储前缀
252 | -clientDatastorePrefix
253 |
254 | ▼ variables
255 | +Errors
256 |
257 | ▼+Client : struct
258 | [fields]
259 |
260 | // 存储客户高层API
261 | -api : clientPorcelainAPI
262 | // 交易集合
263 | -deals : map[cid.Cid]*clientDeal
264 | // 交易资源目录对象及锁
265 | -dealsDs : repo.Datastore
266 | -dealsLk : sync.Mutex
267 | // 存储客户节点
268 | -node : clientNode
269 |
270 | [methods]
271 | // 加载特定交易的凭证
272 | +LoadVouchersForDeal(dealCid cid.Cid) : []*paymentbroker.PaymentVoucher, error
273 |
274 | // 发起存储交易
275 | // 1 获取文件大小、矿工报价、区块高度、目的地址
276 | // 2 建立支付通道
277 | // 3 调用MakeProtocolRequest发起交易请求
278 | // 4 检查交易响应
279 | // 5 持久化交易响应并回复
280 | +ProposeDeal(ctx context.Context, miner address.Address, data cid.Cid, askID uint64, duration uint64, allowDuplicates bool) : *DealResponse, error
281 |
282 | // 查询交易
283 | // 1 获取矿工信息,地址、节点ID
284 | // 2 调用MakeProtocolRequest发起请求
285 | +QueryDeal(ctx context.Context, proposalCid cid.Cid) : *DealResponse, error
286 |
287 | // 检查交易响应
288 | -checkDealResponse(ctx context.Context, resp *DealResponse) : error
289 |
290 | // 判断是否为重复交易
291 | -isMaybeDupDeal(p *DealProposal) : bool
292 |
293 | // 加载交易信息
294 | -loadDeals() : error
295 |
296 | // 返回目标矿工地址
297 | -minerForProposal(c cid.Cid) : address.Address, error
298 |
299 | // 持久化交易响应
300 | -recordResponse(resp *DealResponse, miner address.Address, p *DealProposal) : error
301 |
302 | // 保存交易信息
303 | -saveDeal(cid cid.Cid) : error
304 |
305 | [functions]
306 | // 实例化存储客户
307 | +NewClient(nd clientNode, api clientPorcelainAPI, dealsDs repo.Datastore) : *Client, error
308 |
309 | ▼+ClientNodeImpl : struct
310 | [fields]
311 | -blockTime : time.Duration
312 | -dserv : ipld.DAGService
313 | -host : host.Host
314 |
315 | [methods]
316 | //实现clientNode接口
317 | +GetBlockTime() : time.Duration
318 |
319 | // 获取文件大小
320 | +GetFileSize(ctx context.Context, c cid.Cid) : uint64, error
321 |
322 | // 发起协议请求
323 | // 1 建立对应的存储交易或者请求的协议流
324 | // 2 发起请求
325 | +MakeProtocolRequest(ctx context.Context, protocol protocol.ID, peer peer.ID, request interface{}, response interface{}) : error
326 |
327 | [functions]
328 | // 实例化客户节点
329 | +NewClientNodeImpl(ds ipld.DAGService, host host.Host, bt time.Duration) : *ClientNodeImpl
330 |
331 | ▼-clientDeal : struct
332 | [fields]
333 | // 目标矿工,请求及响应
334 | +Miner : address.Address
335 | +Proposal : *DealProposal
336 | +Response : *DealResponse
337 |
338 | ▼-clientNode : interface
339 | // 由ClientNodeImpl实现
340 | [methods]
341 | +GetBlockTime() : time.Duration
342 | +GetFileSize(context.Context, cid.Cid) : uint64, error
343 | +MakeProtocolRequest(ctx context.Context, protocol protocol.ID, peer peer.ID, request interface{}, response interface{}) : error
344 |
345 | ▼-clientPorcelainAPI : interface
346 | [embedded]
347 | +types.Signer
348 |
349 | [methods]
350 | // 获取区块高度
351 | +ChainBlockHeight(ctx context.Context) : *types.BlockHeight, error
352 | // 创建支付通道
353 | // 包括源及目的地址,价格,时间,支付间隔,通道超时时间,Gas及限制
354 | +CreatePayments(ctx context.Context, config porcelain.CreatePaymentsParams) : *porcelain.CreatePaymentsReturn, error
355 | // 获取目标地址
356 | +GetAndMaybeSetDefaultSenderAddress() : address.Address, error
357 | // 获取矿工报价
358 | +MinerGetAsk(ctx context.Context, minerAddr address.Address, askID uint64) : miner.Ask, error
359 | // 获取矿工Owner地址
360 | +MinerGetOwnerAddress(ctx context.Context, minerAddr address.Address) : address.Address, error
361 | // 获取矿工节点ID
362 | +MinerGetPeerID(ctx context.Context, minerAddr address.Address) : peer.ID, error
363 |
364 | ▼ functions
365 | -init()
366 | ```
367 |
--------------------------------------------------------------------------------
/8.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之八:filecoin源码分析之协议层检索协议
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 8 filecoin源码协议层分析之检索协议
8 | - 8.1 协议概览图
9 | - 8.2 源码信息
10 | - 8.3 源码分析
11 | - 8.3.1 检索矿工
12 | - 8.3.2 检索客户
13 |
14 | ## 8.1 协议概览图
15 | 
16 |
17 | - 此概览图为当前的实现,整个检索的代码还没有完善
18 | - 目前的逻辑比较简单,需要指定矿工、内容cid即可进行免费检索
19 |
20 | ## 8.2 源码信息
21 |
22 | - version
23 | - master分支 619b0eb1(2019年3月2日)
24 | - package
25 | - retrieval
26 | - location
27 | - protocol/retrieval
28 |
29 | ## 8.3 源码分析
30 |
31 | ### 8.3.1 检索矿工
32 |
33 | ```
34 | ▼ package
35 | retrieval
36 |
37 | ▼ imports
38 | github.com/filecoin-project/go-filecoin/cborutil
39 | github.com/filecoin-project/go-filecoin/proofs/sectorbuilder
40 | gx/ipfs/QmTGxDz2CjBucFzPNTiWwzQmTWdrBnzqbqrMucDYMsjuPb/go-libp2p-net
41 | gx/ipfs/QmZNkThpqfVXs9GNbexPrfBbXSLNYeKrE7jwFM2oqHbyqN/go-libp2p-protocol
42 | gx/ipfs/QmbkT7eMTyXfpeyB3ZMxxcxg7XH8t6uXp49jqzz4HB7BGF/go-log
43 | gx/ipfs/Qmd52WKRSwrBK5gUaJKawryZQ5by6UbNB8KVW2Zy6JtbyW/go-libp2p-host
44 | io/ioutil
45 |
46 | ▼ constants
47 | // 定义检索协议: "/fil/retrieval/free/0.0.0"
48 | -retrievalFreeProtocol
49 |
50 | ▼ variables
51 | -log
52 |
53 | ▼+Miner : struct
54 | [fields]
55 | // 矿工节点,参见minerNode
56 | -node : minerNode
57 |
58 | [methods]
59 | // 执行具体的检索服务
60 | // 通过解析协议流数据,执行检索动作并返回
61 | -handleRetrievePieceForFree(s inet.Stream)
62 |
63 | [functions]
64 | // 实例化检索矿工
65 | // 设置处理免费检索的handle方法:handleRetrievePieceForFree
66 | +NewMiner(nd minerNode) : *Miner
67 |
68 | ▼-minerNode : interface
69 | [methods]
70 | +Host() : host.Host
71 | +SectorBuilder() : sectorbuilder.SectorBuilder
72 | ```
73 |
74 | ### 8.3.2 检索客户
75 |
76 | ```
77 | ▼ package
78 | retrieval
79 |
80 | ▶ imports
81 |
82 | ▼ constants
83 | // 检索内容大小限制
84 | +RetrievePieceChunkSize
85 |
86 | ▼+Client : struct
87 | [fields]
88 | -node : clientNode
89 |
90 | [methods]
91 | // 通过cid进行检索
92 | // 通过协议流,发送检索请求以及接受检索回复和数据
93 | +RetrievePiece(ctx context.Context, minerPeerID peer.ID, pieceCID cid.Cid) : io.ReadCloser, error
94 |
95 | [functions]
96 | // 实例化检索客户
97 | +NewClient(nd clientNode) : *Client
98 |
99 | ▼-clientNode : interface
100 | [methods]
101 | +Host() : host.Host
102 | ```
103 |
--------------------------------------------------------------------------------
/9.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析之九:filecoin源码分析之支撑包分析(1)
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 |
6 | ## 目录
7 | - 9 filecoin源码分析之支撑包分析(1)
8 | - 9.1 目的
9 | - 9.2 编译相关
10 | - 9.3 cborutil
11 | - 9.4 address
12 | - 9.5 config
13 | - 9.6 crypto
14 | - 9.7 util/convert
15 | - 9.8 functional-tests
16 | - 9.9 flags
17 | - 9.10 fixtures
18 | - 9.11 filnet
19 |
20 | ## 9.1 目的
21 | > 简析一些支撑包,便于后面分析的理解
22 |
23 | ## 9.2 编译相关
24 | - bin目录:主要为编译用shell脚本
25 | - bls-signatures: 通过cgo编译,导出库及头文件
26 | - build: 编译相关
27 | - util/version:版本检查
28 | - scripts:相关脚本
29 |
30 | ## 9.3 cborutil
31 |
32 | - 对外提供功能
33 | - 读取流消息
34 | - 写入流消息
35 | - 主要被协议层使用
36 |
37 | ```
38 | ▼ package
39 | cborutil
40 |
41 | ▶ imports
42 |
43 | ▼ constants
44 | +MaxMessageSize
45 |
46 | ▼ variables
47 | +ErrMessageTooLarge
48 |
49 | ▼+MsgReader : struct
50 | [fields]
51 | -br : *bufio.Reader
52 | [methods]
53 | +ReadMsg(i interface{}) : error
54 | [functions]
55 | +NewMsgReader(r io.Reader) : *MsgReader
56 | ```
57 |
58 | ```
59 | ▼ package
60 | cborutil
61 |
62 | ▶ imports
63 |
64 | ▼+MsgWriter : struct
65 | [fields]
66 | -w : *bufio.Writer
67 | [methods]
68 | +WriteMsg(i interface{}) : error
69 | [functions]
70 | +NewMsgWriter(w io.Writer) : *MsgWriter
71 | ```
72 |
73 | ## 9.4 address
74 |
75 | - 对外提供功能
76 | - 地址相关操作功能
77 | - 实例化铸币地址、存储市场地址、支付通道地址
78 | - 实例化两个测试地址
79 | - 提供主网地址、测试网地址创建接口
80 | - 提供地址格式转换功能,包含22bytes与41bytes、切片字符串转换、打印。
81 | - 提供地址的合法性检查功能
82 | - 地址格式
83 | - 要与id区分开,id用的是ipfs中的cid,而地址则是filecoin独立定义的。
84 | - 22 bytes地址:包含1byte网络类型、1byte地址版本、20bytes哈希
85 | - 41 bytes地址:包含2bytes网络类型、1byte地址版本、32bytes编码值、6bytes校验和
86 | - 用命令显示的是41bytes格式的地址,address包提供了22bytes与41bytes地址的转换接口
87 |
88 | ```
89 | location: address/constants.go
90 |
91 | ▼ package
92 | address
93 |
94 | ▶ imports
95 |
96 | ▼ constants
97 | // Base32编码的字符集
98 | +Base32Charset
99 | // 地址的哈希部分,目前为20 bytes
100 | +HashLength, 20bytes,160bit
101 | // 地址长度,为HashLength+1+1= 22 bytes
102 | +Length
103 | // 地址格式的版本定义:当前为0
104 | +Version : byte
105 |
106 | ▼ variables
107 | // 基于Base32Charset的Base32实例,用于编解码
108 | +Base32
109 | // Base32 Reverse集合
110 | +Base32CharsetReverse
111 | // 铸币地址,基于"filecoin"哈希生成
112 | +NetworkAddress : Address
113 | // 支付通道地址
114 | +PaymentBrokerAddress : Address
115 | // 存储市场地址
116 | +StorageMarketAddress : Address
117 | // 测试地址
118 | +TestAddress : Address
119 | // 测试地址
120 | +TestAddress2 : Address
121 |
122 | ▼ functions
123 | -init()
124 | ```
125 |
126 | ```
127 | location: address/address.go
128 |
129 | ▼ package
130 | address
131 |
132 | ▶ imports
133 |
134 | ▼ constants
135 | +Mainnet : Network
136 | +Testnet
137 |
138 | ▼ variables
139 | // 错误提示
140 | +ErrInvalidBytes
141 | +ErrUnknownNetwork
142 | +ErrUnknownVersion
143 | -generator
144 | // 配置输入哈希长度20bytes
145 | -hashConfig
146 |
147 | // Address为22字节字符串
148 | ▼+Address : []byte
149 | [methods]
150 | // 转换为编码前地址切片输出
151 | +Bytes() : []byte
152 | // 判断地址是否为空
153 | +Empty() : bool
154 | // 打印地址信息
155 | +Format(f fmt.State, c rune)
156 | // 输出地址中的20bytes哈希值
157 | +Hash() : []byte
158 | // 转换为编码后地址切片输出
159 | +MarshalText() : []byte, error
160 | // 输出地址的网络类型
161 | +Network() : Network
162 | // 转换为41bytes的编码输出
163 | // 2(网络类型)+1(地址版本)+32(base32编码)+6(base32校验位)
164 | +String() : string
165 | // 编码后地址切片输出转换为字符
166 | +UnmarshalText(in []byte) : error
167 | // 获取地址版本号
168 | +Version() : byte
169 |
170 | // 类型定义
171 | +Network : byte
172 |
173 | ▼ functions
174 | // 采用blake2b-160再次哈希
175 | +Hash(input []byte) : []byte
176 | // 生成测试网络地址,输入为原始哈希,会执行blake2b-160再次哈希
177 | +MakeTestAddress(input string) : Address
178 | // 通过字符串网络类型转换为byte网络类型
179 | // fc:主网转化为0
180 | // tf:测试网化为1
181 | +NetworkFromString(input string) : Network, error
182 | // 通过byte网络类型转换为字符串网络类型
183 | // 0:主网转化为fc
184 | // 1:测试网化为tf
185 | +NetworkToString(n Network) : string
186 | // 构建新地址:输入为原始20bytes哈希+网络类型+地址版本
187 | +New(network Network, hash []byte) : Address
188 | // 构建新地址:输入为22bytes的原始切片
189 | +NewFromBytes(raw []byte) : Address, error
190 | // 通过41bytes的字串串生成22bytes的原始地址
191 | +NewFromString(s string) : Address, error
192 | // 构建新地址:输入为原始20bytes哈希,调用New
193 | +NewMainnet(hash []byte) : Address
194 | // 生成测试网络地址,输入为原始哈希再次哈希,被MakeTestAddress调用
195 | +NewTestnet(hash []byte) : Address
196 | // 校验41bytes地址的合法性
197 | +ParseError(addr string) : error
198 |
199 | // base32编码校验码生成,结果为6bytes
200 | -createChecksum(hrp string, data []byte) : []byte
201 | // 解码
202 | -decode(addr string) : string, byte, []byte, error
203 | // 编码
204 | -encode(hrp string, version byte, data []byte) : string, error
205 | -hrpExpand(hrp string) : []byte
206 | -init()
207 | -polymod(values []byte) : uint32
208 | // 校验和验证
209 | -verifyChecksum(hrp string, data []byte) : bool
210 |
211 | ```
212 |
213 | ```
214 | location: address/set.go
215 |
216 | ▼ package
217 | address
218 |
219 | ▶ imports
220 |
221 | ▼ variables
222 | -addrSetEntry
223 |
224 | // 地址集合
225 | +Set : map[Address]
226 |
227 | ▼ functions
228 | -init()
229 | ```
230 |
231 | ## 9.5 config
232 |
233 | - 对外提供功能
234 | - 提供对内存中配置的实例化操作
235 | - 对具体实例的设置和读取
236 | - 对配置文件的读写
237 | - 包含API、启动、数据存储、网络连接、挖矿、钱包、心跳相关配置
238 |
239 | ```
240 | ▼ package
241 | config
242 |
243 | ▶ imports
244 |
245 | ▼ variables
246 | // 对特定参数的合法性校验规则集合
247 | // 1 目前只是限定昵称为字符
248 | +Validators
249 |
250 | ▼+APIConfig : struct
251 | [fields]
252 | // 是否允许跨域请求
253 | +AccessControlAllowCredentials : bool
254 | // 允许的方法列表
255 | +AccessControlAllowMethods : []string
256 | // 允许的元列表
257 | +AccessControlAllowOrigin : []string
258 | // 地址
259 | +Address : string
260 | [functions]
261 | // 实例化APIconfig
262 | -newDefaultAPIConfig() : *APIConfig
263 |
264 | ▼+BootstrapConfig : struct
265 | [fields]
266 | // 启动地址集合
267 | +Addresses : []string
268 | // 最小节点阈值
269 | +MinPeerThreshold : int
270 | // 启动时间阈值,目前为10s
271 | +Period : string
272 | [functions]
273 | // 实例化启动配置的接口
274 | -newDefaultBootstrapConfig() : *BootstrapConfig
275 |
276 | // 存储在内存之中的filecoin配置
277 | ▼+Config : struct
278 | [fields]
279 | // API相关
280 | +API : *APIConfig
281 | // 启动相关
282 | +Bootstrap : *BootstrapConfig
283 | // 数据存储相关
284 | +Datastore : *DatastoreConfig
285 | // 心跳相关
286 | +Heartbeat : *HeartbeatConfig
287 | // 挖矿相关
288 | +Mining : *MiningConfig
289 | // 网络连接相关
290 | +Swarm : *SwarmConfig
291 | // 钱包相关
292 | +Wallet : *WalletConfig
293 | [methods]
294 | // 获取配置,参数为API的上述子结构
295 | +Get(key string) : interface{}, error
296 | // 设置配置,参数为API的上述子结构
297 | +Set(dottedKey string, jsonString string) : error
298 | // 写对应目录的配置文件
299 | +WriteFile(file string) : error
300 | [functions]
301 | // 实例化配置,会调用各字节口的实例化
302 | +NewDefaultConfig() : *Config
303 | // 读对应目录的配置文件
304 | +ReadFile(file string) : *Config, error
305 |
306 | ▼+DatastoreConfig : struct
307 | [fields]
308 | // 路径
309 | +Path : string
310 | // 类型
311 | +Type : string
312 | [functions]
313 | -newDefaultDatastoreConfig() : *DatastoreConfig
314 |
315 | ▼+HeartbeatConfig : struct
316 | [fields]
317 | // 心跳周期
318 | +BeatPeriod : string
319 | // 心跳目标
320 | +BeatTarget : string
321 | // 昵称
322 | +Nickname : string
323 | // 重连时间
324 | +ReconnectPeriod : string
325 | [functions]
326 | -newDefaultHeartbeatConfig() : *HeartbeatConfig
327 |
328 | ▼+MiningConfig : struct
329 | [fields]
330 | // 自动密封间隔周期
331 | +AutoSealIntervalSeconds : uint
332 | // 区块签名地址
333 | +BlockSignerAddress : address.Address
334 | // 矿工地址
335 | +MinerAddress : address.Address
336 | // 存储报价
337 | +StoragePrice : *types.AttoFIL
338 | [functions]
339 | -newDefaultMiningConfig() : *MiningConfig
340 |
341 | ▼+SwarmConfig : struct
342 | [fields]
343 | // 地址
344 | +Address : string
345 | // 转发地址
346 | +PublicRelayAddress : string
347 | [functions]
348 | -newDefaultSwarmConfig() : *SwarmConfig
349 |
350 | ▼+WalletConfig : struct
351 | [fields]
352 | // 默认钱包地址
353 | +DefaultAddress : address.Address
354 | [functions]
355 | -newDefaultWalletConfig() : *WalletConfig
356 |
357 | ▼ functions
358 | -validate(dottedKey string, jsonString string) : error
359 | -validateLettersOnly(key string, value string) : error
360 | ```
361 |
362 | ## 9.6 crypto
363 |
364 | - 对外提供功能
365 | - 生成私钥接口
366 | - 签名接口
367 | - 私钥转公钥接口
368 | - 从签名消息中提取公钥接口
369 | - 验证消息合法性接口
370 | - 主要用于地址生成、钱包相关
371 |
372 | ```
373 | ▼ package
374 | crypto
375 |
376 | ▶ imports
377 |
378 | ▼ constants
379 | // 定义私钥长度32位
380 | +PrivateKeyBytes
381 | // 定义公钥长度65位
382 | +PublicKeyBytes
383 |
384 | ▼ functions
385 | // 从签名消息中恢复公钥
386 | +EcRecover(msg, signature []byte) : []byte, error
387 | // 比较私钥是否相同
388 | +Equals(sk, other []byte) : bool
389 | // 生成私钥,调用GenerateKeyFromSeed
390 | +GenerateKey() : []byte, error
391 | // 生成私钥
392 | +GenerateKeyFromSeed(seed io.Reader) : []byte, error
393 | // 由私钥得到公钥
394 | +PublicKey(sk []byte) : []byte
395 | // 使用私钥签名
396 | +Sign(sk, msg []byte) : []byte, error
397 | // 验证签名合法性
398 | +Verify(pk, msg, signature []byte) : bool
399 | ```
400 |
401 | ## 9.7 util/convert
402 |
403 | - 提供功能
404 | ToCid:转cid功能
405 |
406 | ## 9.8 functional-tests
407 |
408 | - 测试脚本
409 |
410 | ## 9.9 flags
411 |
412 | - 通过ldflags注入,表示git提交版本号
413 |
414 | ```
415 | var Commit string
416 | ```
417 |
418 | ## 9.10 fixtures
419 |
420 | - 提供功能
421 | - 定义不同网络启动相关地址
422 | - 预先分配初始网络状态,比如代币的预先分配
423 |
424 | ```
425 | ▼ package
426 | fixtures
427 |
428 | ▶ imports
429 |
430 | ▼ constants
431 | // 开发人员,开发网络启动相关地址
432 | -nightlyFilecoinBootstrap0 : string
433 | -nightlyFilecoinBootstrap1 : string
434 | -nightlyFilecoinBootstrap2 : string
435 | -nightlyFilecoinBootstrap3 : string
436 | -nightlyFilecoinBootstrap4 : string
437 | // 测试网络启动相关地址
438 | -testFilecoinBootstrap0 : string
439 | -testFilecoinBootstrap1 : string
440 | -testFilecoinBootstrap2 : string
441 | -testFilecoinBootstrap3 : string
442 | -testFilecoinBootstrap4 : string
443 | // 用户,开发网络启动相关地址
444 | -userFilecoinBootstrap0 : string
445 | -userFilecoinBootstrap1 : string
446 | -userFilecoinBootstrap2 : string
447 | -userFilecoinBootstrap3 : string
448 | -userFilecoinBootstrap4 : string
449 |
450 | ▼ variables
451 | // 开发人员,开发网络启动相关地址
452 | +DevnetNightlyBootstrapAddrs
453 | // 测试网络启动相关地址
454 | +DevnetTestBootstrapAddrs
455 | // 用户,开发网络启动相关地址
456 | +DevnetUserBootstrapAddrs
457 | // 预生成测试网络地址集合
458 | +TestAddresses : []string
459 | // 预生成测试矿工账户集合
460 | +TestMiners : []string
461 | // 预生成地址的私钥
462 | -testKeys : []string
463 |
464 | ▼-detailsStruct : struct
465 | [fields]
466 | // 创世区块cid
467 | +GenesisCid : cid.Cid
468 | +Keys : []*types.KeyInfo
469 | +Miners : []
470 |
471 | ▼ functions
472 | // 预生成的Key文件路径
473 | +KeyFilePaths() : []string
474 |
475 | // 预生成信息
476 | // 1 解析gen.json文件到detailsStruct结构体
477 | // 2 追击Miners信息到TestMiners中
478 | -init()
479 | ```
480 | - 如下为gen.json文件,可据此预先给特定矿工分配代币
481 |
482 | ```
483 | {
484 | "keys": 5,
485 | "preAlloc": [
486 | "1000000000000",
487 | "1000000000000",
488 | "1000000000000",
489 | "1000000000000",
490 | "1000000000000"
491 | ],
492 | "miners": [{
493 | "owner": 0,
494 | "power": 1
495 | }]
496 | }
497 | ```
498 |
499 | ## 9.11 filnet
500 |
501 | - 提供功能
502 | - 节点启动
503 | - 定期检查连接节点,如果数量不够会链接随机节点
504 |
505 | ```
506 | location: filnet/address.go
507 |
508 | ▼ package
509 | filnet
510 |
511 | ▼ imports
512 | gx/ipfs/QmNTCey11oxhb1AxDnQBRHtdhap6Ctud872NjAYPYYXPuc/go-multiaddr
513 | gx/ipfs/QmRhFARzTHcFh8wUxwN5KvyTGq73FLC65EfFAhz8Ng7aGb/go-libp2p-peerstore
514 |
515 | ▼ functions
516 | // 节点id转换为完整的节点信息,包括所有的多地址格式
517 | +PeerAddrsToPeerInfos(addrs []string) : []pstore.PeerInfo, error
518 | ```
519 |
520 | ```
521 | location: filnet/bootstrap.go
522 |
523 | ▼ package
524 | filnet
525 |
526 | ▶ imports
527 |
528 | ▼ variables
529 | -log
530 |
531 | ▼+Bootstrapper : struct
532 | [fields]
533 | // 对应bootstrap
534 | +Bootstrap : func([]peer.ID)
535 | // 连接超时时间,用于连接随机节点
536 | +ConnectionTimeout : time.Duration
537 | // 最小连接节点数量阈值
538 | +MinPeerThreshold : int
539 | // 定时检查连接节点数量,小于阈值会处理
540 | +Period : time.Duration
541 | // 随机节点切片
542 | -bootstrapPeers : []pstore.PeerInfo
543 | -cancel : context.CancelFunc
544 | -ctx : context.Context
545 | -d : inet.Dialer
546 | -dhtBootStarted : bool
547 | -h : host.Host
548 | -r : routing.IpfsRouting
549 | -ticker : *time.Ticker
550 | [methods]
551 | // 定时调用Bootstrap 检查连接节点数量,小于阈值会处理
552 | +Start(ctx context.Context)
553 | // 停止节点
554 | +Stop()
555 | // 如果启动节点不够,将会尝试连接随机节点。
556 | -bootstrap(currentPeers []peer.ID)
557 | [functions]
558 | // 实例化
559 | +NewBootstrapper(bootstrapPeers []pstore.PeerInfo, h host.Host, d inet.Dialer, r routing.IpfsRouting, minPeer int, period time.Duration) : *Bootstrapper
560 |
561 | ▼ functions
562 | -hasPID(pids []peer.ID, pid peer.ID) : bool
563 | ```
564 |
--------------------------------------------------------------------------------
/img/active.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/active.png
--------------------------------------------------------------------------------
/img/filecoin-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/filecoin-1.png
--------------------------------------------------------------------------------
/img/filecoin_arch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/filecoin_arch.png
--------------------------------------------------------------------------------
/img/ipfsandfilecoin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/ipfsandfilecoin.png
--------------------------------------------------------------------------------
/img/payment.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/payment.png
--------------------------------------------------------------------------------
/img/post.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/post.png
--------------------------------------------------------------------------------
/img/retrieval_protocol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/retrieval_protocol.png
--------------------------------------------------------------------------------
/img/sle.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/sle.png
--------------------------------------------------------------------------------
/img/sle2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/sle2.png
--------------------------------------------------------------------------------
/img/storage_protocol.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/storage_protocol.png
--------------------------------------------------------------------------------
/img/wikiissue1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/waynewyang/analysis-of-filecoin-in-Chinese/32e112119bfad01d64cdb7ac84778a38247ab6a7/img/wikiissue1.png
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # filecoin技术架构分析
2 |
3 | > 作者:杨尉(waynewyang),转载请注明出处
4 |
5 | ## 目录
6 | - [1 filecoin概念](1.md)
7 | - [2 filecoin通用语言理解](2.md)
8 | - [3 filecoin开发网使用](3.md)
9 | - [4 filecoin源码顶层架构分析](4.md)
10 | - [5 filecoin源码协议层分析之心跳协议](5.md)
11 | - [6 filecoin源码协议层分析之hello握手协议](6.md)
12 | - [7 filecoin源码协议层分析之存储协议](7.md)
13 | - [8 filecoin源码协议层分析之检索协议](8.md)
14 | - [9 filecoin源码分析之支撑包分析(1)](9.md)
15 | - [10 filecoin源码分析之支撑包分析(2/2)](10.md)
16 | - [11 filecoin源码分析之内部接口层api包分析](11.md)
17 | - [12 filecoin源码分析之内部接口层plumbing&porcelain接口](12.md)
18 | - [13 filecoin源码分析之服务层actor及vm](13.md)
19 | - [14 filecoin源码分析之服务层链同步、共识协议及挖矿](14.md)
20 | - [15 filecoin源码分析之节点运行逻辑](15.md)
21 |
--------------------------------------------------------------------------------