github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/swarm/bmt/bmt.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:43</date> 10 //</624450112587829248> 11 12 13 //包bmt提供了一个用于swarm块散列的二进制merkle树实现 14 package bmt 15 16 import ( 17 "fmt" 18 "hash" 19 "strings" 20 "sync" 21 "sync/atomic" 22 ) 23 24 /* 25 binary merkle tree hash是一个针对有限大小的任意数据块的哈希函数。 26 它被定义为在固定大小的段上构建的二进制merkle树的根散列 27 使用任何基哈希函数(例如,keccak 256 sha3)的底层块。 28 数据长度小于固定大小的块被散列,就像它们没有填充一样。 29 30 BMT散列被用作群中的块散列函数,而块散列函数又是 31 128分支群哈希http://swarm guide.readthedocs.io/en/latest/architecture.html swarm哈希 32 33 BMT最适合提供紧凑的包含证明,即证明 34 段是从特定偏移量开始的块的子字符串。 35 基础段的大小固定为基哈希(称为分辨率)的大小 36 在bmt散列中),使用keccak256 sha3散列为32个字节,evm字大小为链上bmt验证优化。 37 以及群散列的Merkle树中包含证明的最佳散列大小。 38 39 提供了两种实现: 40 41 *refhasher针对代码简单性进行了优化,是一种参考实现。 42 这很容易理解 43 *哈希优化了速度,利用了最低限度的并发性 44 协调并发例程的控制结构 45 46 bmt hasher实现以下接口 47 *标准golang hash.hash-同步,可重复使用 48 *swarmhash-提供跨度 49 *IO.WRITER-从左到右同步数据编写器 50 *AsyncWriter-并发节写入和异步SUM调用 51 **/ 52 53 54 const ( 55 //池大小是散列程序使用的最大BMT树数,即, 56 //由同一哈希程序执行的最大并发BMT哈希操作数 57 PoolSize = 8 58 ) 59 60 //basehasherfunc是用于bmt的基哈希的hash.hash构造函数函数。 61 //由keccak256 sha3.newlegacykeccak256实施 62 type BaseHasherFunc func() hash.Hash 63 64 //hasher用于表示BMT的固定最大大小块的可重用hasher 65 //-实现hash.hash接口 66 //-重新使用一个树池进行缓冲内存分配和资源控制 67 //-支持顺序不可知的并发段写入和段(双段)写入 68 //以及顺序读写 69 //-不能对多个块同时调用同一哈希实例 70 //-同一哈希实例可同步重用 71 //-SUM将树还给游泳池并保证离开 72 //树及其自身处于可重用的状态,用于散列新块 73 //-生成和验证段包含证明(TODO:) 74 type Hasher struct { 75 pool *TreePool //BMT资源池 76 bmt *tree //用于流程控制和验证的预建BMT资源 77 } 78 79 //new创建一个可重用的bmt散列器, 80 //从资源池中提取新的树,以便对每个块进行哈希处理 81 func New(p *TreePool) *Hasher { 82 return &Hasher{ 83 pool: p, 84 } 85 } 86 87 //Treepool提供了BMT哈希程序用作资源的一个树池。 88 //从池中弹出的一棵树保证处于干净状态。 89 //用于散列新块。 90 type TreePool struct { 91 lock sync.Mutex 92 c chan *tree //从池中获取资源的通道 93 hasher BaseHasherFunc //用于BMT级别的基本哈希器 94 SegmentSize int //叶段的大小,规定为=哈希大小 95 SegmentCount int //BMT基准面上的段数 96 Capacity int //池容量,控制并发性 97 Depth int //bmt树的深度=int(log2(segmentcount))+1 98 Size int //数据的总长度(计数*大小) 99 count int //当前(曾经)分配的资源计数 100 zerohashes [][]byte //所有级别的可预测填充子树的查找表 101 } 102 103 //newtreepool创建一个树池,其中包含哈希、段大小、段计数和容量 104 //在hasher.gettree上,它重用空闲的树,或者在未达到容量时创建一个新的树。 105 func NewTreePool(hasher BaseHasherFunc, segmentCount, capacity int) *TreePool { 106 //初始化零哈希查找表 107 depth := calculateDepthFor(segmentCount) 108 segmentSize := hasher().Size() 109 zerohashes := make([][]byte, depth+1) 110 zeros := make([]byte, segmentSize) 111 zerohashes[0] = zeros 112 h := hasher() 113 for i := 1; i < depth+1; i++ { 114 zeros = doSum(h, nil, zeros, zeros) 115 zerohashes[i] = zeros 116 } 117 return &TreePool{ 118 c: make(chan *tree, capacity), 119 hasher: hasher, 120 SegmentSize: segmentSize, 121 SegmentCount: segmentCount, 122 Capacity: capacity, 123 Size: segmentCount * segmentSize, 124 Depth: depth, 125 zerohashes: zerohashes, 126 } 127 } 128 129 //排干水池中的水,直到其资源不超过n个。 130 func (p *TreePool) Drain(n int) { 131 p.lock.Lock() 132 defer p.lock.Unlock() 133 for len(p.c) > n { 134 <-p.c 135 p.count-- 136 } 137 } 138 139 //保留正在阻止,直到它返回可用的树 140 //它重用空闲的树,或者在未达到大小时创建一个新的树。 141 //TODO:应在此处使用上下文 142 func (p *TreePool) reserve() *tree { 143 p.lock.Lock() 144 defer p.lock.Unlock() 145 var t *tree 146 if p.count == p.Capacity { 147 return <-p.c 148 } 149 select { 150 case t = <-p.c: 151 default: 152 t = newTree(p.SegmentSize, p.Depth, p.hasher) 153 p.count++ 154 } 155 return t 156 } 157 158 //释放会将树放回池中。 159 //此树保证处于可重用状态 160 func (p *TreePool) release(t *tree) { 161 p.c <- t //永远不会失败… 162 } 163 164 //树是表示BMT的可重用控制结构 165 //以二叉树组织 166 //散列器使用treepool为每个块散列获取树 167 //树不在池中时被“锁定” 168 type tree struct { 169 leaves []*node //树的叶节点,通过父链接可访问的其他节点 170 cursor int //当前最右侧打开段的索引 171 offset int //当前打开段内的偏移量(光标位置) 172 section []byte //最右边的开口段(双段) 173 result chan []byte //结果通道 174 span []byte //包含在块下的数据范围 175 } 176 177 //节点是表示BMT中节点的可重用段散列器。 178 type node struct { 179 isLeft bool //是否为父双段的左侧 180 parent *node //指向BMT中父节点的指针 181 state int32 //原子增量impl并发布尔切换 182 left, right []byte //这是写两个孩子部分的地方 183 hasher hash.Hash //节点上的预构造哈希 184 } 185 186 //newnode在bmt中构造一个段散列器节点(由newtree使用) 187 func newNode(index int, parent *node, hasher hash.Hash) *node { 188 return &node{ 189 parent: parent, 190 isLeft: index%2 == 0, 191 hasher: hasher, 192 } 193 } 194 195 //抽签抽BMT(不好) 196 func (t *tree) draw(hash []byte) string { 197 var left, right []string 198 var anc []*node 199 for i, n := range t.leaves { 200 left = append(left, fmt.Sprintf("%v", hashstr(n.left))) 201 if i%2 == 0 { 202 anc = append(anc, n.parent) 203 } 204 right = append(right, fmt.Sprintf("%v", hashstr(n.right))) 205 } 206 anc = t.leaves 207 var hashes [][]string 208 for l := 0; len(anc) > 0; l++ { 209 var nodes []*node 210 hash := []string{""} 211 for i, n := range anc { 212 hash = append(hash, fmt.Sprintf("%v|%v", hashstr(n.left), hashstr(n.right))) 213 if i%2 == 0 && n.parent != nil { 214 nodes = append(nodes, n.parent) 215 } 216 } 217 hash = append(hash, "") 218 hashes = append(hashes, hash) 219 anc = nodes 220 } 221 hashes = append(hashes, []string{"", fmt.Sprintf("%v", hashstr(hash)), ""}) 222 total := 60 223 del := " " 224 var rows []string 225 for i := len(hashes) - 1; i >= 0; i-- { 226 var textlen int 227 hash := hashes[i] 228 for _, s := range hash { 229 textlen += len(s) 230 } 231 if total < textlen { 232 total = textlen + len(hash) 233 } 234 delsize := (total - textlen) / (len(hash) - 1) 235 if delsize > len(del) { 236 delsize = len(del) 237 } 238 row := fmt.Sprintf("%v: %v", len(hashes)-i-1, strings.Join(hash, del[:delsize])) 239 rows = append(rows, row) 240 241 } 242 rows = append(rows, strings.Join(left, " ")) 243 rows = append(rows, strings.Join(right, " ")) 244 return strings.Join(rows, "\n") + "\n" 245 } 246 247 //NewTree通过构建BMT的节点初始化树 248 //-段大小规定为哈希的大小 249 func newTree(segmentSize, depth int, hashfunc func() hash.Hash) *tree { 250 n := newNode(0, nil, hashfunc()) 251 prevlevel := []*node{n} 252 //迭代级别并创建2^(深度级别)节点 253 //0级位于双段段,因此我们从深度-2开始,因为 254 count := 2 255 for level := depth - 2; level >= 0; level-- { 256 nodes := make([]*node, count) 257 for i := 0; i < count; i++ { 258 parent := prevlevel[i/2] 259 var hasher hash.Hash 260 if level == 0 { 261 hasher = hashfunc() 262 } 263 nodes[i] = newNode(i, parent, hasher) 264 } 265 prevlevel = nodes 266 count *= 2 267 } 268 //datanode级别是最后一级的节点 269 return &tree{ 270 leaves: prevlevel, 271 result: make(chan []byte), 272 section: make([]byte, 2*segmentSize), 273 } 274 } 275 276 //实现hash.hash所需的方法 277 278 //SIZE返回大小 279 func (h *Hasher) Size() int { 280 return h.pool.SegmentSize 281 } 282 283 //块大小返回块大小 284 func (h *Hasher) BlockSize() int { 285 return 2 * h.pool.SegmentSize 286 } 287 288 //sum返回缓冲区的bmt根哈希 289 //使用SUM预先假定顺序同步写入(io.writer接口) 290 //hash.hash接口sum方法将字节片附加到基础 291 //在计算和返回块散列之前的数据 292 //调用方必须确保SUM不与WRITE、WRITESECTION同时调用 293 func (h *Hasher) Sum(b []byte) (s []byte) { 294 t := h.getTree() 295 //将最后一节的final标志设置为true 296 go h.writeSection(t.cursor, t.section, true, true) 297 //等待结果 298 s = <-t.result 299 span := t.span 300 //将树资源释放回池 301 h.releaseTree() 302 //B+Sha3(SPAN+BMT(纯块) 303 if len(span) == 0 { 304 return append(b, s...) 305 } 306 return doSum(h.pool.hasher(), b, span, s) 307 } 308 309 //实现swarmhash和io.writer接口所需的方法 310 311 //按顺序写入调用,添加到要散列的缓冲区, 312 //在Go例程中的每个完整段调用中都有writesection 313 func (h *Hasher) Write(b []byte) (int, error) { 314 l := len(b) 315 if l == 0 || l > h.pool.Size { 316 return 0, nil 317 } 318 t := h.getTree() 319 secsize := 2 * h.pool.SegmentSize 320 //计算缺失位的长度以完成当前开放段 321 smax := secsize - t.offset 322 //如果在块的开头或节的中间 323 if t.offset < secsize { 324 //从缓冲区填充当前段 325 copy(t.section[t.offset:], b) 326 //如果输入缓冲区被占用,并且打开的部分不完整,则 327 //提前抵销和返还 328 if smax == 0 { 329 smax = secsize 330 } 331 if l <= smax { 332 t.offset += l 333 return l, nil 334 } 335 } else { 336 //如果节的结尾 337 if t.cursor == h.pool.SegmentCount*2 { 338 return 0, nil 339 } 340 } 341 //从输入缓冲区读取完整部分和最后一个可能的部分部分。 342 for smax < l { 343 //部分完成;异步推到树 344 go h.writeSection(t.cursor, t.section, true, false) 345 //复位段 346 t.section = make([]byte, secsize) 347 //从smax的输入缓冲区复制到节的右半部分 348 copy(t.section, b[smax:]) 349 //前进光标 350 t.cursor++ 351 //此处的smax表示输入缓冲区中的连续偏移量 352 smax += secsize 353 } 354 t.offset = l - smax + secsize 355 return l, nil 356 } 357 358 //在写入哈希之前需要调用Reset。 359 func (h *Hasher) Reset() { 360 h.releaseTree() 361 } 362 363 //实现swarmhash接口所需的方法 364 365 //在写入哈希之前需要调用ResetWithLength 366 //参数应该是的字节片二进制表示。 367 //哈希下包含的数据长度,即跨度 368 func (h *Hasher) ResetWithLength(span []byte) { 369 h.Reset() 370 h.getTree().span = span 371 } 372 373 //releasetree将树放回池中解锁 374 //它重置树、段和索引 375 func (h *Hasher) releaseTree() { 376 t := h.bmt 377 if t == nil { 378 return 379 } 380 h.bmt = nil 381 go func() { 382 t.cursor = 0 383 t.offset = 0 384 t.span = nil 385 t.section = make([]byte, h.pool.SegmentSize*2) 386 select { 387 case <-t.result: 388 default: 389 } 390 h.pool.release(t) 391 }() 392 } 393 394 //NewAsyncWriter使用一个接口扩展哈希,用于并发段/节写入 395 func (h *Hasher) NewAsyncWriter(double bool) *AsyncHasher { 396 secsize := h.pool.SegmentSize 397 if double { 398 secsize *= 2 399 } 400 write := func(i int, section []byte, final bool) { 401 h.writeSection(i, section, double, final) 402 } 403 return &AsyncHasher{ 404 Hasher: h, 405 double: double, 406 secsize: secsize, 407 write: write, 408 } 409 } 410 411 //节编写器是异步段/节编写器接口 412 type SectionWriter interface { 413 Reset() //在重用前调用标准init 414 Write(index int, data []byte) //写入索引部分 415 Sum(b []byte, length int, span []byte) []byte //返回缓冲区的哈希值 416 SectionSize() int //要使用的异步节单元的大小 417 } 418 419 //AsyncHasher使用异步段/节编写器接口扩展BMT哈希器 420 //AsyncHasher不安全,不检查索引和节数据长度 421 //它必须与正确的索引和长度以及正确的节数一起使用 422 // 423 //如果 424 //*非最终截面比secsize短或长 425 //*如果最后一部分与长度不匹配 426 //*编写索引大于长度/秒大小的节 427 //*当length/secsize<maxsec时,在sum调用中设置长度 428 // 429 //*如果未对完全写入的哈希表调用sum()。 430 //一个进程将阻塞,可通过重置终止。 431 //*如果不是所有部分都写了,但它会阻塞,则不会泄漏进程。 432 //并保留可释放的资源,调用reset()。 433 type AsyncHasher struct { 434 *Hasher //扩展哈希 435 mtx sync.Mutex //锁定光标访问 436 double bool //是否使用双段(调用hasher.writesection) 437 secsize int //基节大小(哈希或双精度大小) 438 write func(i int, section []byte, final bool) 439 } 440 441 //实现AsyncWriter所需的方法 442 443 //SECTIONSIZE返回要使用的异步节单元的大小 444 func (sw *AsyncHasher) SectionSize() int { 445 return sw.secsize 446 } 447 448 //写入BMT基的第i部分 449 //此函数可以并打算同时调用 450 //它安全地设置最大段 451 func (sw *AsyncHasher) Write(i int, section []byte) { 452 sw.mtx.Lock() 453 defer sw.mtx.Unlock() 454 t := sw.getTree() 455 //光标跟踪迄今为止写入的最右边的部分 456 //如果索引低于光标,则只需按原样编写非最终部分。 457 if i < t.cursor { 458 //如果索引不是最右边的,则可以安全地写入节 459 go sw.write(i, section, false) 460 return 461 } 462 //如果有上一个最右边的节可安全写入 463 if t.offset > 0 { 464 if i == t.cursor { 465 //i==cursor表示游标是通过哈希调用设置的,因此我们可以将节写入最后一个节。 466 //因为它可能更短,所以我们首先将它复制到填充缓冲区 467 t.section = make([]byte, sw.secsize) 468 copy(t.section, section) 469 go sw.write(i, t.section, true) 470 return 471 } 472 //最右边的部分刚刚改变,所以我们把前一部分写为非最终部分。 473 go sw.write(t.cursor, t.section, false) 474 } 475 //将i设置为迄今为止编写的最基本部分的索引 476 //将t.offset设置为cursor*secsize+1 477 t.cursor = i 478 t.offset = i*sw.secsize + 1 479 t.section = make([]byte, sw.secsize) 480 copy(t.section, section) 481 } 482 483 //一旦长度和跨度已知,可以随时调用sum。 484 //甚至在所有段都被写入之前 485 //在这种情况下,SUM将阻塞,直到所有段都存在,并且 486 //可以计算长度的哈希值。 487 // 488 //B:摘要附加到B 489 //长度:输入的已知长度(不安全;超出范围时未定义) 490 //meta:要与最终摘要的bmt根一起哈希的元数据 491 //例如,防止存在伪造的跨度 492 func (sw *AsyncHasher) Sum(b []byte, length int, meta []byte) (s []byte) { 493 sw.mtx.Lock() 494 t := sw.getTree() 495 if length == 0 { 496 sw.mtx.Unlock() 497 s = sw.pool.zerohashes[sw.pool.Depth] 498 } else { 499 //对于非零输入,最右边的部分异步写入树 500 //如果写入了实际的最后一节(t.cursor==length/t.secsize) 501 maxsec := (length - 1) / sw.secsize 502 if t.offset > 0 { 503 go sw.write(t.cursor, t.section, maxsec == t.cursor) 504 } 505 //将光标设置为maxsec,以便在最后一节到达时写入它 506 t.cursor = maxsec 507 t.offset = length 508 result := t.result 509 sw.mtx.Unlock() 510 //等待结果或重置 511 s = <-result 512 } 513 //把树放回水池里 514 sw.releaseTree() 515 //如果没有给定元,只需将摘要附加到b 516 if len(meta) == 0 { 517 return append(b, s...) 518 } 519 //使用池一起散列meta和bmt根散列 520 return doSum(sw.pool.hasher(), b, meta, s) 521 } 522 523 //WriteSection将第i个节的哈希写入BMT树的1级节点 524 func (h *Hasher) writeSection(i int, section []byte, double bool, final bool) { 525 //选择节的叶节点 526 var n *node 527 var isLeft bool 528 var hasher hash.Hash 529 var level int 530 t := h.getTree() 531 if double { 532 level++ 533 n = t.leaves[i] 534 hasher = n.hasher 535 isLeft = n.isLeft 536 n = n.parent 537 //散列该节 538 section = doSum(hasher, nil, section) 539 } else { 540 n = t.leaves[i/2] 541 hasher = n.hasher 542 isLeft = i%2 == 0 543 } 544 //将哈希写入父节点 545 if final { 546 //对于最后一段,使用writefinalnode 547 h.writeFinalNode(level, n, hasher, isLeft, section) 548 } else { 549 h.writeNode(n, hasher, isLeft, section) 550 } 551 } 552 553 //WriteNode将数据推送到节点 554 //如果是两个姐妹中的第一个写的,程序就终止了 555 //如果是第二个,则计算散列并写入散列 556 //递归到父节点 557 //由于对父对象进行哈希操作是同步的,因此可以使用相同的哈希操作。 558 func (h *Hasher) writeNode(n *node, bh hash.Hash, isLeft bool, s []byte) { 559 level := 1 560 for { 561 //在bmt的根目录下,只需将结果写入结果通道 562 if n == nil { 563 h.getTree().result <- s 564 return 565 } 566 //否则,将子哈希赋给左或右段 567 if isLeft { 568 n.left = s 569 } else { 570 n.right = s 571 } 572 //首先到达的子线程将终止 573 if n.toggle() { 574 return 575 } 576 //现在第二个线程可以确保左孩子和右孩子都被写入 577 //所以它计算左右的散列值并将其推送到父对象 578 s = doSum(bh, nil, n.left, n.right) 579 isLeft = n.isLeft 580 n = n.parent 581 level++ 582 } 583 } 584 585 //WriteFinalNode正在沿着从最终数据集到 586 //通过父级的bmt根 587 //对于不平衡树,它使用 588 //所有零部分的bmt子树根散列的池查找表 589 //否则行为类似于“writenode” 590 func (h *Hasher) writeFinalNode(level int, n *node, bh hash.Hash, isLeft bool, s []byte) { 591 592 for { 593 //在bmt的根目录下,只需将结果写入结果通道 594 if n == nil { 595 if s != nil { 596 h.getTree().result <- s 597 } 598 return 599 } 600 var noHash bool 601 if isLeft { 602 //来自左姊妹枝 603 //当最后一节的路径经过左子节点时 604 //我们为正确的级别包含一个全零子树散列并切换节点。 605 n.right = h.pool.zerohashes[level] 606 if s != nil { 607 n.left = s 608 //如果左最后一个节点带有哈希,则它必须是第一个(并且只能是线程) 609 //所以开关已经处于被动状态,不需要呼叫 610 //然而线程需要继续向父线程推送哈希 611 noHash = false 612 } else { 613 //如果再次是第一个线程,那么传播nil并计算no hash 614 noHash = n.toggle() 615 } 616 } else { 617 //右姐妹支 618 if s != nil { 619 //如果从右子节点推送哈希,则写入右段更改状态 620 n.right = s 621 //如果toggle为true,则我们首先到达,因此不需要散列,只需将nil推送到父级 622 noHash = n.toggle() 623 624 } else { 625 //如果s为nil,那么线程首先到达前一个节点,这里将有两个, 626 //所以不需要做任何事情,保持s=nil作为家长 627 noHash = true 628 } 629 } 630 //第一个到达的子线程将继续重置为nil 631 //第二条线索现在可以确定左、右两个孩子都写了 632 //它计算左右的哈希并将其推送到父级 633 if noHash { 634 s = nil 635 } else { 636 s = doSum(bh, nil, n.left, n.right) 637 } 638 //迭代到父级 639 isLeft = n.isLeft 640 n = n.parent 641 level++ 642 } 643 } 644 645 //gettree通过从池中保留一个bmt资源并将其分配给bmt字段来获取bmt资源 646 func (h *Hasher) getTree() *tree { 647 if h.bmt != nil { 648 return h.bmt 649 } 650 t := h.pool.reserve() 651 h.bmt = t 652 return t 653 } 654 655 //实现并发可重用2状态对象的原子布尔切换 656 //具有%2的原子加载项实现原子布尔切换 657 //如果切换开关刚刚将其置于活动/等待状态,则返回true。 658 func (n *node) toggle() bool { 659 return atomic.AddInt32(&n.state, 1)%2 == 1 660 } 661 662 //使用hash.hash计算数据的哈希 663 func doSum(h hash.Hash, b []byte, data ...[]byte) []byte { 664 h.Reset() 665 for _, v := range data { 666 h.Write(v) 667 } 668 return h.Sum(b) 669 } 670 671 //hashstr是用于tree.draw中字节的漂亮打印机 672 func hashstr(b []byte) string { 673 end := len(b) 674 if end > 4 { 675 end = 4 676 } 677 return fmt.Sprintf("%x", b[:end]) 678 } 679 680 //CalculateDepthfor计算BMT树中的深度(层数) 681 func calculateDepthFor(n int) (d int) { 682 c := 2 683 for ; c < n; c *= 2 { 684 d++ 685 } 686 return d + 1 687 } 688