Go-ethereum 源码解析之 core/types/bloom9.go
package types
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
type bytesBacked interface {
Bytes() []byte
}
const (
// BloomByteLength represents the number of bytes used in a header log bloom.
BloomByteLength = 256
// BloomBitLength represents the number of bits used in a header log bloom.
BloomBitLength = 8 * BloomByteLength
)
// Bloom represents a 2048 bit bloom filter.
type Bloom [BloomByteLength]byte
// BytesToBloom converts a byte slice to a bloom filter.
// It panics if b is not of suitable size.
func BytesToBloom(b []byte) Bloom {
var bloom Bloom
bloom.SetBytes(b)
return bloom
}
// SetBytes sets the content of b to the given bytes.
// It panics if d is not of suitable size.
func (b *Bloom) SetBytes(d []byte) {
if len(b) < len(d) {
panic(fmt.Sprintf("bloom bytes too big %d %d", len(b), len(d)))
}
copy(b[BloomByteLength-len(d):], d)
}
// Add adds d to the filter. Future calls of Test(d) will return true.
func (b *Bloom) Add(d *big.Int) {
bin := new(big.Int).SetBytes(b[:])
bin.Or(bin, bloom9(d.Bytes()))
b.SetBytes(bin.Bytes())
}
// Big converts b to a big integer.
func (b Bloom) Big() *big.Int {
return new(big.Int).SetBytes(b[:])
}
func (b Bloom) Bytes() []byte {
return b[:]
}
func (b Bloom) Test(test *big.Int) bool {
return BloomLookup(b, test)
}
func (b Bloom) TestBytes(test []byte) bool {
return b.Test(new(big.Int).SetBytes(test))
}
// MarshalText encodes b as a hex string with 0x prefix.
func (b Bloom) MarshalText() ([]byte, error) {
return hexutil.Bytes(b[:]).MarshalText()
}
// UnmarshalText b as a hex string with 0x prefix.
func (b *Bloom) UnmarshalText(input []byte) error {
return hexutil.UnmarshalFixedText("Bloom", input, b[:])
}
func CreateBloom(receipts Receipts) Bloom {
bin := new(big.Int)
for _, receipt := range receipts {
bin.Or(bin, LogsBloom(receipt.Logs))
}
return BytesToBloom(bin.Bytes())
}
func LogsBloom(logs []*Log) *big.Int {
bin := new(big.Int)
for _, log := range logs {
bin.Or(bin, bloom9(log.Address.Bytes()))
for _, b := range log.Topics {
bin.Or(bin, bloom9(b[:]))
}
}
return bin
}
func bloom9(b []byte) *big.Int {
b = crypto.Keccak256(b)
r := new(big.Int)
for i := 0; i < 6; i += 2 {
t := big.NewInt(1)
b := (uint(b[i+1]) + (uint(b[i]) << 8)) & 2047
r.Or(r, t.Lsh(t, b))
}
return r
}
var Bloom9 = bloom9
func BloomLookup(bin Bloom, topic bytesBacked) bool {
bloom := bin.Big()
cmp := bloom9(topic.Bytes())
return bloom.And(bloom, cmp).Cmp(cmp) == 0
}
Appendix A. 批注
type bytesBacked interface
定义接口 bytesBacked。
- Bytes() []byte
方法 Bytes() 返回字节列表。
const
- BloomByteLength:表示区块头中日志布隆过滤器的字节数。
- BloomBitLength:表示区块头中日志布隆过滤器的位数。
type Bloom [BloomByteLength]byte
Bloom 表示 2048 位的布隆过滤器。
- func (b *Bloom) SetBytes(d []byte)
方法 SetBytes() 将 b 的内容设为 d。
主要的实现细节:
- 校验输入的字节数
- 调用函数 copy() 完成字节的拷贝操作
- func (b *Bloom) Add(d *big.Int)
方法 Add() 向布隆过滤器添加 d。
主要的实现细节:
- 将布隆过滤器恢复为 big.Int
- 通过函数 bloom9() 对输入 d 进行转换,然后通过或操作添加进原布隆过滤器
- 将 big.Int 转换为布隆过滤器
func (b Bloom) Big() *big.Int
方法 Big() 将布隆过滤器转换为 big.Int。func (b Bloom) Bytes() []byte
方法 Bytes() 返回布隆过滤器的字节。func (b Bloom) Test(test *big.Int) bool
方法 Test() 返回给定输入是否存在于布隆过滤器中。
主要的实现细节:
- 通过函数 BloomLookup() 实现主要操作。
- func (b Bloom) TestBytes(test []byte) bool
方法 Test() 返回给定输入是否存在于布隆过滤器中。
主要的实现细节:
- 将输入 []byte 转换为 big.Int
- 再将具体实现转发给方法 Test()
- func (b Bloom) MarshalText() ([]byte, error)
方法 MarshalText() 将布隆过滤器编码为带前缀 0x 的 16 进制字符串。
主要的实现细节:
- 通过方法 hexutil.Bytes.MarshalText() 实现主要操作。
- func (b *Bloom) UnmarshalText(input []byte) error
方法 UnmarshalText() 将带前缀 0x 的 16 进制字符串解码为布隆过滤器。
主要的实现细节:
- 这里参数 input 的字节数应该就是布隆过滤器的字节数
- 通过方法 hexutil.UnmarshalFixedText() 实现主要操作。
func BytesToBloom(b []byte) Bloom
函数 BytesToBloom() 将字节切片转换成布隆过滤器。
func CreateBloom(receipts Receipts) Bloom
函数 CreateBloom() 根据给定的收据列表(Receipts)创建布隆过滤器。
主要的实现细节:
- 通过函数 LogsBloom() 对 Receipts 中每个 Receipt 中的 Logs 生成 big.Int,并添加进布隆过滤器中
- 通过函数 BytesToBloom() 将计算出的字节转换成布隆过滤器
func LogsBloom(logs []*Log) *big.Int
函数 LogsBloom() 对给定的输入 logs (日志列表)生成对应的布隆项。
主要的实现细节:
- 对 logs 中的每个 log 做累积计算
- 对 log 中的 Address 和 Topics 调用函数 bloom9()
func bloom9(b []byte) *big.Int
函数 bloom9() 根据 Keccak256 将字节转换成 big.Int。
var Bloom9 = bloom9
func BloomLookup(bin Bloom, topic bytesBacked) bool
函数 BloomLookup() 在 Bloom 中查找是否包含接口 bytesBacked 返回的字节。
主要的实现细节:
- 实现方法很巧妙
- 通过 bit 的 And 和 Cmp 操作判定是否包含
func BloomLookup(bin Bloom, topic bytesBacked) bool {
bloom := bin.Big()
cmp := bloom9(topic.Bytes())
return bloom.And(bloom, cmp).Cmp(cmp) == 0
}
Reference
Contributor
- Windstamp, https://github.com/windstamp