mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
les, light, eth/filter: implement transformed bloom bitmap based quick log filtering
This commit is contained in:
parent
fa99986143
commit
595bb1cbdb
21 changed files with 1376 additions and 76 deletions
|
|
@ -1283,7 +1283,7 @@ func (self *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
||||||
self.mu.Lock()
|
self.mu.Lock()
|
||||||
defer self.mu.Unlock()
|
defer self.mu.Unlock()
|
||||||
|
|
||||||
_, err := self.hc.WriteHeader(header)
|
_, err := self.hc.WriteHeader(header, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1306,7 +1306,7 @@ func (self *BlockChain) writeHeader(header *types.Header) error {
|
||||||
self.mu.Lock()
|
self.mu.Lock()
|
||||||
defer self.mu.Unlock()
|
defer self.mu.Unlock()
|
||||||
|
|
||||||
_, err := self.hc.WriteHeader(header)
|
_, err := self.hc.WriteHeader(header, nil)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
74
core/bloombits/fetcher_test.go
Normal file
74
core/bloombits/fetcher_test.go
Normal file
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package bloombits
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/binary"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func testVector(b uint, s uint64) BitVector {
|
||||||
|
r := make(BitVector, 10)
|
||||||
|
binary.BigEndian.PutUint16(r[0:2], uint16(b))
|
||||||
|
binary.BigEndian.PutUint64(r[2:10], s)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFetcher(t *testing.T) {
|
||||||
|
f := &fetcher{
|
||||||
|
reqMap: make(map[uint64]req),
|
||||||
|
distChn: make(chan distReq, channelCap),
|
||||||
|
}
|
||||||
|
in := make(chan uint64, channelCap)
|
||||||
|
stop := make(chan struct{})
|
||||||
|
out := f.fetch(in, stop)
|
||||||
|
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
req, ok := <-f.distChn
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(rand.Intn(1000000)))
|
||||||
|
f.deliver([]uint64{req.sectionIdx}, []BitVector{testVector(req.bitIdx, req.sectionIdx)})
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
for i := uint64(0); i < 10000; i++ {
|
||||||
|
in <- i
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := uint64(0); i < 10000; i++ {
|
||||||
|
bv := <-out
|
||||||
|
if !bytes.Equal(bv, testVector(0, i)) {
|
||||||
|
if len(bv) != 10 {
|
||||||
|
t.Errorf("Vector #%d length is %d, expected 10", i, len(bv))
|
||||||
|
} else {
|
||||||
|
j := binary.BigEndian.Uint64(bv[2:10])
|
||||||
|
t.Errorf("Expected vector #%d, fetched #%d", i, j)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(stop)
|
||||||
|
}
|
||||||
465
core/bloombits/matcher.go
Normal file
465
core/bloombits/matcher.go
Normal file
|
|
@ -0,0 +1,465 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package bloombits
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maxRequestLength = 16
|
||||||
|
channelCap = 100
|
||||||
|
)
|
||||||
|
|
||||||
|
// when received by fetcher: data == nil, requested == false, fetched == chan struct{}
|
||||||
|
// when returned by NextRequest: data == nil, requested == true, fetched == chan struct{}
|
||||||
|
// when data is delivered: data == BitVector, requested == true, fetched == nil
|
||||||
|
type req struct {
|
||||||
|
data BitVector
|
||||||
|
requested bool
|
||||||
|
fetched chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type distReq struct {
|
||||||
|
bitIdx uint
|
||||||
|
sectionIdx uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type fetcher struct {
|
||||||
|
bitIdx uint
|
||||||
|
reqMap map[uint64]req
|
||||||
|
reqLock sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fetcher) fetch(sectionChn chan uint64, distChn chan distReq, stop chan struct{}) chan BitVector {
|
||||||
|
dataChn := make(chan BitVector, channelCap)
|
||||||
|
returnChn := make(chan uint64, channelCap)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(returnChn)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
case idx, ok := <-sectionChn:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.reqLock.Lock()
|
||||||
|
r := f.reqMap[idx]
|
||||||
|
if r.data == nil {
|
||||||
|
if r.fetched == nil {
|
||||||
|
r.fetched = make(chan struct{})
|
||||||
|
}
|
||||||
|
if !r.requested {
|
||||||
|
distChn <- distReq{bitIdx: f.bitIdx, sectionIdx: idx}
|
||||||
|
}
|
||||||
|
f.reqMap[idx] = r
|
||||||
|
}
|
||||||
|
f.reqLock.Unlock()
|
||||||
|
returnChn <- idx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(dataChn)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
case idx, ok := <-returnChn:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
f.reqLock.RLock()
|
||||||
|
r := f.reqMap[idx]
|
||||||
|
f.reqLock.RUnlock()
|
||||||
|
|
||||||
|
if r.data == nil {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
case <-r.fetched:
|
||||||
|
f.reqLock.RLock()
|
||||||
|
r = f.reqMap[idx]
|
||||||
|
f.reqLock.RUnlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dataChn <- r.data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return dataChn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fetcher) requested(sectionIdxList []uint64) {
|
||||||
|
//fmt.Println("requested", f.bitIdx, sectionIdxList)
|
||||||
|
f.reqLock.Lock()
|
||||||
|
defer f.reqLock.Unlock()
|
||||||
|
|
||||||
|
for _, idx := range sectionIdxList {
|
||||||
|
r := f.reqMap[idx]
|
||||||
|
r.requested = true
|
||||||
|
f.reqMap[idx] = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fetcher) deliver(sectionIdxList []uint64, data []BitVector) {
|
||||||
|
//fmt.Println("deliver", f.bitIdx, sectionIdxList, data != nil)
|
||||||
|
f.reqLock.Lock()
|
||||||
|
defer f.reqLock.Unlock()
|
||||||
|
|
||||||
|
for i, idx := range sectionIdxList {
|
||||||
|
r := f.reqMap[idx]
|
||||||
|
if data != nil {
|
||||||
|
r.data = data[i]
|
||||||
|
close(r.fetched)
|
||||||
|
r.fetched = nil
|
||||||
|
} else {
|
||||||
|
r.requested = false
|
||||||
|
}
|
||||||
|
f.reqMap[idx] = r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Matcher struct {
|
||||||
|
addresses []types.BloomIndexList
|
||||||
|
topics [][]types.BloomIndexList
|
||||||
|
fetchers map[uint]*fetcher
|
||||||
|
|
||||||
|
distChn chan distReq
|
||||||
|
getNextReqChn chan chan nextRequests
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMatcher() *Matcher {
|
||||||
|
return &Matcher{fetchers: make(map[uint]*fetcher)}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAddresses matches only logs that are generated from addresses that are included
|
||||||
|
// in the given addresses.
|
||||||
|
func (m *Matcher) SetAddresses(addr []common.Address) {
|
||||||
|
m.addresses = make([]types.BloomIndexList, len(addr))
|
||||||
|
for i, b := range addr {
|
||||||
|
m.addresses[i] = types.BloomIndexes(b.Bytes())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetTopics matches only logs that have topics matching the given topics.
|
||||||
|
func (m *Matcher) SetTopics(topics [][]common.Hash) {
|
||||||
|
m.topics = nil
|
||||||
|
loop:
|
||||||
|
for _, topicList := range topics {
|
||||||
|
t := make([]types.BloomIndexList, len(topicList))
|
||||||
|
for i, b := range topicList {
|
||||||
|
if (b == common.Hash{}) {
|
||||||
|
continue loop
|
||||||
|
}
|
||||||
|
t[i] = types.BloomIndexes(b.Bytes())
|
||||||
|
}
|
||||||
|
m.topics = append(m.topics, t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) match(sectionChn chan uint64, stop chan struct{}) (chan uint64, chan BitVector) {
|
||||||
|
subIdx := m.topics
|
||||||
|
if len(m.addresses) > 0 {
|
||||||
|
subIdx = append([][]types.BloomIndexList{m.addresses}, subIdx...)
|
||||||
|
}
|
||||||
|
//fmt.Println("idx", subIdx)
|
||||||
|
m.distChn = make(chan distReq, channelCap)
|
||||||
|
m.getNextReqChn = make(chan chan nextRequests) // should be a blocking channel
|
||||||
|
go m.distributeRequests(stop)
|
||||||
|
|
||||||
|
s := sectionChn
|
||||||
|
var bv chan BitVector
|
||||||
|
for _, idx := range subIdx {
|
||||||
|
s, bv = m.subMatch(s, bv, idx, stop)
|
||||||
|
}
|
||||||
|
return s, bv
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) getOrNewFetcher(idx uint) *fetcher {
|
||||||
|
if f, ok := m.fetchers[idx]; ok {
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
f := &fetcher{
|
||||||
|
bitIdx: idx,
|
||||||
|
reqMap: make(map[uint64]req),
|
||||||
|
}
|
||||||
|
m.fetchers[idx] = f
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// andVector == nil
|
||||||
|
func (m *Matcher) subMatch(sectionChn chan uint64, andVectorChn chan BitVector, idxs []types.BloomIndexList, stop chan struct{}) (chan uint64, chan BitVector) {
|
||||||
|
// set up fetchers
|
||||||
|
fetchIdx := make([][3]chan uint64, len(idxs))
|
||||||
|
fetchData := make([][3]chan BitVector, len(idxs))
|
||||||
|
for i, idx := range idxs {
|
||||||
|
for j, ii := range idx {
|
||||||
|
fetchIdx[i][j] = make(chan uint64, channelCap)
|
||||||
|
fetchData[i][j] = m.getOrNewFetcher(ii).fetch(fetchIdx[i][j], m.distChn, stop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processChn := make(chan uint64, channelCap)
|
||||||
|
resIdxChn := make(chan uint64, channelCap)
|
||||||
|
resDataChn := make(chan BitVector, channelCap)
|
||||||
|
|
||||||
|
// goroutine for starting retrievals
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
case s, ok := <-sectionChn:
|
||||||
|
if !ok {
|
||||||
|
close(processChn)
|
||||||
|
for _, ff := range fetchIdx {
|
||||||
|
for _, f := range ff {
|
||||||
|
close(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
processChn <- s
|
||||||
|
for _, ff := range fetchIdx {
|
||||||
|
for _, f := range ff {
|
||||||
|
f <- s
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// goroutine for processing retrieved data
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
case s, ok := <-processChn:
|
||||||
|
if !ok {
|
||||||
|
close(resIdxChn)
|
||||||
|
close(resDataChn)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var orVector BitVector
|
||||||
|
for _, ff := range fetchData {
|
||||||
|
var andVector BitVector
|
||||||
|
for _, f := range ff {
|
||||||
|
data := <-f
|
||||||
|
if andVector == nil {
|
||||||
|
andVector = bvCopy(data)
|
||||||
|
} else {
|
||||||
|
bvAnd(andVector, data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if orVector == nil {
|
||||||
|
orVector = andVector
|
||||||
|
} else {
|
||||||
|
bvOr(orVector, andVector)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if orVector == nil {
|
||||||
|
orVector = bvZero()
|
||||||
|
}
|
||||||
|
if andVectorChn != nil {
|
||||||
|
bvAnd(orVector, <-andVectorChn)
|
||||||
|
}
|
||||||
|
if bvIsNonZero(orVector) {
|
||||||
|
resIdxChn <- s
|
||||||
|
resDataChn <- orVector
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return resIdxChn, resDataChn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) GetMatches(start, end uint64, stop chan struct{}) chan uint64 {
|
||||||
|
sectionChn := make(chan uint64, channelCap)
|
||||||
|
resultsChn := make(chan uint64, channelCap)
|
||||||
|
|
||||||
|
s, bv := m.match(sectionChn, stop)
|
||||||
|
|
||||||
|
startSection := start / SectionSize
|
||||||
|
endSection := end / SectionSize
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(sectionChn)
|
||||||
|
|
||||||
|
for i := startSection; i <= endSection; i++ {
|
||||||
|
select {
|
||||||
|
case sectionChn <- i:
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
defer close(resultsChn)
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case idx, ok := <-s:
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
match := <-bv //nil check
|
||||||
|
sectionStart := idx * SectionSize
|
||||||
|
s := sectionStart
|
||||||
|
if start > s {
|
||||||
|
s = start
|
||||||
|
}
|
||||||
|
e := sectionStart + SectionSize - 1
|
||||||
|
if end < e {
|
||||||
|
e = end
|
||||||
|
}
|
||||||
|
for i := s; i <= e; i++ {
|
||||||
|
b := match[(i-sectionStart)/8]
|
||||||
|
bit := 7 - i%8
|
||||||
|
if b != 0 {
|
||||||
|
if b&(1<<bit) != 0 {
|
||||||
|
resultsChn <- i
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
i += bit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return resultsChn
|
||||||
|
}
|
||||||
|
|
||||||
|
type nextRequests struct {
|
||||||
|
bitIdx uint
|
||||||
|
sectionIdxList []uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) distributeRequests(stop chan struct{}) {
|
||||||
|
reqCnt := 0
|
||||||
|
reqs := make(map[uint][]uint64)
|
||||||
|
storeReq := func(r distReq) {
|
||||||
|
queue := reqs[r.bitIdx]
|
||||||
|
i := 0
|
||||||
|
for i < len(queue) && r.sectionIdx > queue[i] {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
reqs[r.bitIdx] = append(append(queue[:i], r.sectionIdx), queue[i:]...)
|
||||||
|
reqCnt++
|
||||||
|
}
|
||||||
|
|
||||||
|
storeReqs := func(r distReq) {
|
||||||
|
storeReq(r)
|
||||||
|
timeout := time.After(time.Microsecond)
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-timeout:
|
||||||
|
return
|
||||||
|
case r := <-m.distChn:
|
||||||
|
storeReq(r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if reqCnt == 0 {
|
||||||
|
select {
|
||||||
|
case r := <-m.distChn:
|
||||||
|
storeReqs(r)
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
select {
|
||||||
|
case r := <-m.distChn:
|
||||||
|
storeReqs(r)
|
||||||
|
case <-stop:
|
||||||
|
return
|
||||||
|
case c := <-m.getNextReqChn:
|
||||||
|
var (
|
||||||
|
found bool
|
||||||
|
bestBit uint
|
||||||
|
bestSection uint64
|
||||||
|
)
|
||||||
|
|
||||||
|
for bitIdx, queue := range reqs {
|
||||||
|
if len(queue) > 0 && (!found || queue[0] < bestSection) {
|
||||||
|
found = true
|
||||||
|
bestBit = bitIdx
|
||||||
|
bestSection = queue[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
panic(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
bestQueue := reqs[bestBit]
|
||||||
|
cnt := len(bestQueue)
|
||||||
|
if cnt > maxRequestLength {
|
||||||
|
cnt = maxRequestLength
|
||||||
|
}
|
||||||
|
res := nextRequests{bestBit, bestQueue[:cnt]}
|
||||||
|
reqs[bestBit] = bestQueue[cnt:]
|
||||||
|
reqCnt -= cnt
|
||||||
|
|
||||||
|
c <- res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) NextRequest(stop chan struct{}) (bitIdx uint, sectionIdxList []uint64) {
|
||||||
|
c := make(chan nextRequests)
|
||||||
|
select {
|
||||||
|
case m.getNextReqChn <- c:
|
||||||
|
r := <-c
|
||||||
|
//fmt.Println("request", r.bitIdx, r.sectionIdxList)
|
||||||
|
m.fetchers[r.bitIdx].requested(r.sectionIdxList)
|
||||||
|
return r.bitIdx, r.sectionIdxList
|
||||||
|
case <-stop:
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is possible to deliver data even after GetMatches has been stopped. Once a vector has been
|
||||||
|
// requested, the next call to GetMatches will keep waiting for delivery.
|
||||||
|
// If retrieval has been cancelled, call Deliver with data == nil. In this case the next call to
|
||||||
|
// GetMatches will re-request it.
|
||||||
|
func (m *Matcher) Deliver(bitIdx uint, sectionIdxList []uint64, data []BitVector) {
|
||||||
|
m.fetchers[bitIdx].deliver(sectionIdxList, data)
|
||||||
|
}
|
||||||
165
core/bloombits/utils.go
Normal file
165
core/bloombits/utils.go
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
// Copyright 2017 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
package bloombits
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
const SectionSize = 4096
|
||||||
|
|
||||||
|
type (
|
||||||
|
BitVector []byte
|
||||||
|
CompVector []byte
|
||||||
|
)
|
||||||
|
|
||||||
|
func bvAnd(a, b BitVector) {
|
||||||
|
for i, bb := range b {
|
||||||
|
a[i] &= bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bvOr(a, b BitVector) {
|
||||||
|
for i, bb := range b {
|
||||||
|
a[i] |= bb
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bvZero() BitVector {
|
||||||
|
return make(BitVector, SectionSize/8)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bvCopy(a BitVector) BitVector {
|
||||||
|
c := make(BitVector, SectionSize/8)
|
||||||
|
copy(c, a)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func bvIsNonZero(a BitVector) bool {
|
||||||
|
for _, b := range a {
|
||||||
|
if b != 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompressBloomBits(bits BitVector) CompVector {
|
||||||
|
if len(bits) != SectionSize/8 {
|
||||||
|
panic(nil)
|
||||||
|
}
|
||||||
|
c := compressBits(bits)
|
||||||
|
if len(c) >= SectionSize/8 {
|
||||||
|
// make a copy so that output is always detached from input
|
||||||
|
return CompVector(bvCopy(bits))
|
||||||
|
}
|
||||||
|
return CompVector(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressBits(bits []byte) []byte {
|
||||||
|
l := len(bits)
|
||||||
|
b := make([]byte, l/8)
|
||||||
|
c := make([]byte, l)
|
||||||
|
cl := 0
|
||||||
|
for i, v := range bits {
|
||||||
|
if v != 0 {
|
||||||
|
c[cl] = v
|
||||||
|
cl++
|
||||||
|
b[i/8] |= 1 << byte(7-i%8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cl == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if l > 8 {
|
||||||
|
b = compressBits(b)
|
||||||
|
}
|
||||||
|
return append(b, c[0:cl]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DecompressBloomBits(bits CompVector) BitVector {
|
||||||
|
if len(bits) == SectionSize/8 {
|
||||||
|
// make a copy so that output is always detached from input
|
||||||
|
return bvCopy(BitVector(bits))
|
||||||
|
}
|
||||||
|
dc, ofs := decompressBits(bits, SectionSize/8)
|
||||||
|
if ofs != len(bits) {
|
||||||
|
panic(nil)
|
||||||
|
}
|
||||||
|
return dc
|
||||||
|
}
|
||||||
|
|
||||||
|
func decompressBits(bits []byte, targetLen int) ([]byte, int) {
|
||||||
|
lb := len(bits)
|
||||||
|
dc := make([]byte, targetLen)
|
||||||
|
if lb == 0 {
|
||||||
|
return dc, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
l := targetLen / 8
|
||||||
|
var (
|
||||||
|
b []byte
|
||||||
|
ofs int
|
||||||
|
)
|
||||||
|
if l == 1 {
|
||||||
|
b = bits[0:1]
|
||||||
|
ofs = 1
|
||||||
|
} else {
|
||||||
|
b, ofs = decompressBits(bits, l)
|
||||||
|
}
|
||||||
|
for i, _ := range dc {
|
||||||
|
if b[i/8]&(1<<byte(7-i%8)) != 0 {
|
||||||
|
if ofs == lb {
|
||||||
|
panic(nil)
|
||||||
|
}
|
||||||
|
dc[i] = bits[ofs]
|
||||||
|
ofs++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dc, ofs
|
||||||
|
}
|
||||||
|
|
||||||
|
const BloomLength = 2048
|
||||||
|
|
||||||
|
type BloomBitsCreator struct {
|
||||||
|
blooms [BloomLength][SectionSize / 8]byte
|
||||||
|
bitIdx uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BloomBitsCreator) AddHeaderBloom(bloom types.Bloom) {
|
||||||
|
if b.bitIdx >= SectionSize {
|
||||||
|
panic("too many header blooms added")
|
||||||
|
}
|
||||||
|
|
||||||
|
byteIdx := b.bitIdx / 8
|
||||||
|
bitMask := byte(1) << byte(7-b.bitIdx%8)
|
||||||
|
for bloomBitIdx, _ := range b.blooms {
|
||||||
|
bloomByteIdx := BloomLength/8 - 1 - bloomBitIdx/8
|
||||||
|
bloomBitMask := byte(1) << byte(bloomBitIdx%8)
|
||||||
|
if (bloom[bloomByteIdx] & bloomBitMask) != 0 {
|
||||||
|
b.blooms[bloomBitIdx][byteIdx] |= bitMask
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.bitIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *BloomBitsCreator) GetBitVector(idx uint) BitVector {
|
||||||
|
if b.bitIdx != SectionSize {
|
||||||
|
panic("not enough header blooms added")
|
||||||
|
}
|
||||||
|
|
||||||
|
return BitVector(b.blooms[idx][:])
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
|
|
@ -71,6 +72,9 @@ var (
|
||||||
|
|
||||||
preimageCounter = metrics.NewCounter("db/preimage/total")
|
preimageCounter = metrics.NewCounter("db/preimage/total")
|
||||||
preimageHitCounter = metrics.NewCounter("db/preimage/hits")
|
preimageHitCounter = metrics.NewCounter("db/preimage/hits")
|
||||||
|
|
||||||
|
bloomBitsPrefix = []byte("bloomBits-")
|
||||||
|
bloomBitsAvailKey = []byte("bloomBitsAvailable")
|
||||||
)
|
)
|
||||||
|
|
||||||
// encodeBlockNumber encodes a block number as big endian uint64
|
// encodeBlockNumber encodes a block number as big endian uint64
|
||||||
|
|
@ -699,3 +703,52 @@ func FindCommonAncestor(db ethdb.Database, a, b *types.Header) *types.Header {
|
||||||
}
|
}
|
||||||
return a
|
return a
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBloomBits(db ethdb.Database, bitIdx, sectionIdx uint64) (bloombits.CompVector, error) {
|
||||||
|
var encKey [10]byte
|
||||||
|
binary.BigEndian.PutUint16(encKey[0:2], uint16(bitIdx))
|
||||||
|
binary.BigEndian.PutUint64(encKey[2:10], sectionIdx)
|
||||||
|
key := append(bloomBitsPrefix, encKey[:]...)
|
||||||
|
bloomBits, err := db.Get(key)
|
||||||
|
return bloombits.CompVector(bloomBits), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func StoreBloomBits(db ethdb.Database, bitIdx, sectionIdx uint64, bloomBits bloombits.CompVector) {
|
||||||
|
var encKey [10]byte
|
||||||
|
binary.BigEndian.PutUint16(encKey[0:2], uint16(bitIdx))
|
||||||
|
binary.BigEndian.PutUint64(encKey[2:10], sectionIdx)
|
||||||
|
key := append(bloomBitsPrefix, encKey[:]...)
|
||||||
|
db.Put(key, bloomBits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetBloomBitsAvailable(db ethdb.Database) uint64 {
|
||||||
|
data, _ := db.Get(bloomBitsAvailKey)
|
||||||
|
if len(data) == 8 {
|
||||||
|
return binary.BigEndian.Uint64(data[:])
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func StoreBloomBitsAvailable(db ethdb.Database, cnt uint64) {
|
||||||
|
var data [8]byte
|
||||||
|
binary.BigEndian.PutUint64(data[:], cnt)
|
||||||
|
db.Put(bloomBitsAvailKey, data[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeBloomBitsSection(db ethdb.Database, sectionIdx uint64) error {
|
||||||
|
bc := &bloombits.BloomBitsCreator{}
|
||||||
|
for i := sectionIdx * bloombits.SectionSize; i < (sectionIdx+1)*bloombits.SectionSize; i++ {
|
||||||
|
hash := GetCanonicalHash(db, i)
|
||||||
|
header := GetHeader(db, hash, i)
|
||||||
|
if header == nil {
|
||||||
|
glog.V(logger.Error).Infof("Error creating bloomBits section #%d: header #%d not found", sectionIdx, i)
|
||||||
|
return errors.New("Header not found")
|
||||||
|
}
|
||||||
|
bc.AddHeaderBloom(header.Bloom)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < bloombits.BloomLength; i++ {
|
||||||
|
StoreBloomBits(db, uint64(i), sectionIdx, bloombits.CompressBloomBits(bc.GetBitVector(uint(i))))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,8 @@ func (hc *HeaderChain) GetBlockNumber(hash common.Hash) uint64 {
|
||||||
return number
|
return number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ReorgCallback func(*types.Header)
|
||||||
|
|
||||||
// WriteHeader writes a header into the local chain, given that its parent is
|
// WriteHeader writes a header into the local chain, given that its parent is
|
||||||
// already known. If the total difficulty of the newly inserted header becomes
|
// already known. If the total difficulty of the newly inserted header becomes
|
||||||
// greater than the current known TD, the canonical chain is re-routed.
|
// greater than the current known TD, the canonical chain is re-routed.
|
||||||
|
|
@ -139,7 +141,7 @@ func (hc *HeaderChain) GetBlockNumber(hash common.Hash) uint64 {
|
||||||
// without the real blocks. Hence, writing headers directly should only be done
|
// without the real blocks. Hence, writing headers directly should only be done
|
||||||
// in two scenarios: pure-header mode of operation (light clients), or properly
|
// in two scenarios: pure-header mode of operation (light clients), or properly
|
||||||
// separated header/block phases (non-archive clients).
|
// separated header/block phases (non-archive clients).
|
||||||
func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, err error) {
|
func (hc *HeaderChain) WriteHeader(header *types.Header, reorgCallback ReorgCallback) (status WriteStatus, err error) {
|
||||||
// Cache some values to prevent constant recalculation
|
// Cache some values to prevent constant recalculation
|
||||||
var (
|
var (
|
||||||
hash = header.Hash()
|
hash = header.Hash()
|
||||||
|
|
@ -187,6 +189,10 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
|
||||||
headHeader = hc.GetHeader(headHash, headNumber)
|
headHeader = hc.GetHeader(headHash, headNumber)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if reorgCallback != nil && headNumber < hc.currentHeader.Number.Uint64() {
|
||||||
|
reorgCallback(headHeader)
|
||||||
|
}
|
||||||
|
|
||||||
// Extend the canonical chain with the new header
|
// Extend the canonical chain with the new header
|
||||||
if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil {
|
if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil {
|
||||||
glog.Fatalf("failed to insert header number: %v", err)
|
glog.Fatalf("failed to insert header number: %v", err)
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,19 @@ func LogsBloom(logs []*Log) *big.Int {
|
||||||
return bin
|
return bin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BloomIndexList [3]uint
|
||||||
|
|
||||||
|
func BloomIndexes(b []byte) BloomIndexList {
|
||||||
|
b = crypto.Keccak256(b[:])
|
||||||
|
|
||||||
|
var r [3]uint
|
||||||
|
for i, _ := range r {
|
||||||
|
r[i] = (uint(b[i+i+1]) + (uint(b[i+i]) << 8)) & 2047
|
||||||
|
}
|
||||||
|
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func bloom9(b []byte) *big.Int {
|
func bloom9(b []byte) *big.Int {
|
||||||
b = crypto.Keccak256(b[:])
|
b = crypto.Keccak256(b[:])
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
|
|
@ -199,6 +200,18 @@ func (b *EthApiBackend) AccountManager() *accounts.Manager {
|
||||||
return b.eth.AccountManager()
|
return b.eth.AccountManager()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *EthApiBackend) GetBloomBits(ctx context.Context, bitIdx uint64, sectionIdxList []uint64) ([]bloombits.CompVector, error) {
|
||||||
|
results := make([]bloombits.CompVector, len(sectionIdxList))
|
||||||
|
var err error
|
||||||
|
for i, idx := range sectionIdxList {
|
||||||
|
results[i], err = core.GetBloomBits(b.eth.chainDb, bitIdx, idx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
type EthApiState struct {
|
type EthApiState struct {
|
||||||
state *state.StateDB
|
state *state.StateDB
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
|
@ -36,12 +37,13 @@ type Backend interface {
|
||||||
EventMux() *event.TypeMux
|
EventMux() *event.TypeMux
|
||||||
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
|
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
|
||||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
||||||
|
GetBloomBits(ctx context.Context, bitIdx uint64, sectionIdxList []uint64) ([]bloombits.CompVector, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter can be used to retrieve and filter logs.
|
// Filter can be used to retrieve and filter logs.
|
||||||
type Filter struct {
|
type Filter struct {
|
||||||
backend Backend
|
backend Backend
|
||||||
useMipMap bool
|
useMipMap, useBloomBits bool
|
||||||
|
|
||||||
created time.Time
|
created time.Time
|
||||||
|
|
||||||
|
|
@ -49,6 +51,8 @@ type Filter struct {
|
||||||
begin, end int64
|
begin, end int64
|
||||||
addresses []common.Address
|
addresses []common.Address
|
||||||
topics [][]common.Hash
|
topics [][]common.Hash
|
||||||
|
|
||||||
|
matcher *bloombits.Matcher
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new filter which uses a bloom filter on blocks to figure out whether
|
// New creates a new filter which uses a bloom filter on blocks to figure out whether
|
||||||
|
|
@ -57,9 +61,11 @@ type Filter struct {
|
||||||
// to light clients.
|
// to light clients.
|
||||||
func New(backend Backend, useMipMap bool) *Filter {
|
func New(backend Backend, useMipMap bool) *Filter {
|
||||||
return &Filter{
|
return &Filter{
|
||||||
backend: backend,
|
backend: backend,
|
||||||
useMipMap: useMipMap,
|
useMipMap: useMipMap,
|
||||||
db: backend.ChainDb(),
|
useBloomBits: !useMipMap,
|
||||||
|
db: backend.ChainDb(),
|
||||||
|
matcher: bloombits.NewMatcher(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,11 +85,17 @@ func (f *Filter) SetEndBlock(end int64) {
|
||||||
// in the given addresses.
|
// in the given addresses.
|
||||||
func (f *Filter) SetAddresses(addr []common.Address) {
|
func (f *Filter) SetAddresses(addr []common.Address) {
|
||||||
f.addresses = addr
|
f.addresses = addr
|
||||||
|
if f.useBloomBits {
|
||||||
|
f.matcher.SetAddresses(addr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetTopics matches only logs that have topics matching the given topics.
|
// SetTopics matches only logs that have topics matching the given topics.
|
||||||
func (f *Filter) SetTopics(topics [][]common.Hash) {
|
func (f *Filter) SetTopics(topics [][]common.Hash) {
|
||||||
f.topics = topics
|
f.topics = topics
|
||||||
|
if f.useBloomBits {
|
||||||
|
f.matcher.SetTopics(topics)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FindOnce searches the blockchain for matching log entries, returning
|
// FindOnce searches the blockchain for matching log entries, returning
|
||||||
|
|
@ -167,7 +179,109 @@ func (f *Filter) mipFind(start, end uint64, depth int) (logs []*types.Log, block
|
||||||
return nil, end
|
return nil, end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (f *Filter) serveMatcher(ctx context.Context, stop chan struct{}) chan error {
|
||||||
|
errChn := make(chan error)
|
||||||
|
for i := 0; i < 10; i++ {
|
||||||
|
go func(i int) {
|
||||||
|
for {
|
||||||
|
//fmt.Println(i, "NextRequest")
|
||||||
|
b, s := f.matcher.NextRequest(stop)
|
||||||
|
//fmt.Println(i, "NextRequest ret", b, s)
|
||||||
|
if s == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := f.backend.GetBloomBits(ctx, uint64(b), s)
|
||||||
|
//fmt.Println(i, "GetBloomBits", len(data), err)
|
||||||
|
if err != nil {
|
||||||
|
f.matcher.Deliver(b, s, nil)
|
||||||
|
errChn <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
decomp := make([]bloombits.BitVector, len(data))
|
||||||
|
for i, d := range data {
|
||||||
|
decomp[i] = bloombits.DecompressBloomBits(bloombits.CompVector(d))
|
||||||
|
}
|
||||||
|
//fmt.Println(i, "Deliver")
|
||||||
|
f.matcher.Deliver(b, s, decomp)
|
||||||
|
//fmt.Println(i, "Deliver ret")
|
||||||
|
}
|
||||||
|
}(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return errChn
|
||||||
|
}
|
||||||
|
|
||||||
func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*types.Log, blockNumber uint64, err error) {
|
func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*types.Log, blockNumber uint64, err error) {
|
||||||
|
|
||||||
|
checkBlock := func(i uint64, header *types.Header) (logs []*types.Log, blockNumber uint64, err error) {
|
||||||
|
// Get the logs of the block
|
||||||
|
receipts, err := f.backend.GetReceipts(ctx, header.Hash())
|
||||||
|
if err != nil {
|
||||||
|
return nil, end, err
|
||||||
|
}
|
||||||
|
var unfiltered []*types.Log
|
||||||
|
for _, receipt := range receipts {
|
||||||
|
unfiltered = append(unfiltered, ([]*types.Log)(receipt.Logs)...)
|
||||||
|
}
|
||||||
|
logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
|
||||||
|
if len(logs) > 0 {
|
||||||
|
return logs, i, nil
|
||||||
|
}
|
||||||
|
return nil, i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.useBloomBits {
|
||||||
|
haveBloomBitsBefore := core.GetBloomBitsAvailable(f.db) * bloombits.SectionSize
|
||||||
|
e := end
|
||||||
|
if haveBloomBitsBefore <= e {
|
||||||
|
e = haveBloomBitsBefore - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
stop := make(chan struct{})
|
||||||
|
defer close(stop)
|
||||||
|
//fmt.Println("GetMatches")
|
||||||
|
matches := f.matcher.GetMatches(start, e, stop)
|
||||||
|
//fmt.Println("GetMatches ret")
|
||||||
|
errChn := f.serveMatcher(ctx, stop)
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case i, ok := <-matches:
|
||||||
|
if !ok {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
|
||||||
|
blockNumber := rpc.BlockNumber(i)
|
||||||
|
header, err := f.backend.HeaderByNumber(ctx, blockNumber)
|
||||||
|
if header == nil || err != nil {
|
||||||
|
return logs, end, err
|
||||||
|
}
|
||||||
|
|
||||||
|
l, b, e := checkBlock(i, header)
|
||||||
|
|
||||||
|
//fmt.Println("match", i, f.bloomFilter(header.Bloom), len(l))
|
||||||
|
/*for i := 0; i < 16; i++ {
|
||||||
|
fmt.Println(header.Bloom[i*16 : i*16+16])
|
||||||
|
}*/
|
||||||
|
|
||||||
|
if l != nil || e != nil {
|
||||||
|
return l, b, e
|
||||||
|
}
|
||||||
|
case err := <-errChn:
|
||||||
|
return logs, end, err
|
||||||
|
case <-ctx.Done():
|
||||||
|
return nil, end, ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if end < haveBloomBitsBefore {
|
||||||
|
return logs, end, nil
|
||||||
|
} else {
|
||||||
|
start = haveBloomBitsBefore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for i := start; i <= end; i++ {
|
for i := start; i <= end; i++ {
|
||||||
blockNumber := rpc.BlockNumber(i)
|
blockNumber := rpc.BlockNumber(i)
|
||||||
header, err := f.backend.HeaderByNumber(ctx, blockNumber)
|
header, err := f.backend.HeaderByNumber(ctx, blockNumber)
|
||||||
|
|
@ -178,18 +292,9 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*types.
|
||||||
// Use bloom filtering to see if this block is interesting given the
|
// Use bloom filtering to see if this block is interesting given the
|
||||||
// current parameters
|
// current parameters
|
||||||
if f.bloomFilter(header.Bloom) {
|
if f.bloomFilter(header.Bloom) {
|
||||||
// Get the logs of the block
|
l, b, e := checkBlock(i, header)
|
||||||
receipts, err := f.backend.GetReceipts(ctx, header.Hash())
|
if l != nil || e != nil {
|
||||||
if err != nil {
|
return l, b, e
|
||||||
return nil, end, err
|
|
||||||
}
|
|
||||||
var unfiltered []*types.Log
|
|
||||||
for _, receipt := range receipts {
|
|
||||||
unfiltered = append(unfiltered, ([]*types.Log)(receipt.Logs)...)
|
|
||||||
}
|
|
||||||
logs = filterLogs(unfiltered, nil, nil, f.addresses, f.topics)
|
|
||||||
if len(logs) > 0 {
|
|
||||||
return logs, uint64(blockNumber), nil
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts"
|
"github.com/ethereum/go-ethereum/accounts"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
|
|
@ -153,3 +154,7 @@ func (b *LesApiBackend) EventMux() *event.TypeMux {
|
||||||
func (b *LesApiBackend) AccountManager() *accounts.Manager {
|
func (b *LesApiBackend) AccountManager() *accounts.Manager {
|
||||||
return b.eth.accountManager
|
return b.eth.accountManager
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *LesApiBackend) GetBloomBits(ctx context.Context, bitIdx uint64, sectionIdxList []uint64) ([]bloombits.CompVector, error) {
|
||||||
|
return light.GetBloomBits(ctx, b.eth.odr, bitIdx, sectionIdxList)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -130,6 +130,7 @@ func (peer *ServerNode) recalcBLE(time mclock.AbsTime) {
|
||||||
const safetyMargin = time.Millisecond * 200
|
const safetyMargin = time.Millisecond * 200
|
||||||
|
|
||||||
func (peer *ServerNode) canSend(maxCost uint64) time.Duration {
|
func (peer *ServerNode) canSend(maxCost uint64) time.Duration {
|
||||||
|
peer.recalcBLE(mclock.Now())
|
||||||
maxCost += uint64(safetyMargin) * peer.params.MinRecharge / uint64(fcTimeConst)
|
maxCost += uint64(safetyMargin) * peer.params.MinRecharge / uint64(fcTimeConst)
|
||||||
if maxCost > peer.params.BufLimit {
|
if maxCost > peer.params.BufLimit {
|
||||||
maxCost = peer.params.BufLimit
|
maxCost = peer.params.BufLimit
|
||||||
|
|
@ -204,13 +205,11 @@ func (peer *ServerNode) SendRequest(reqID, maxCost uint64) {
|
||||||
peer.lock.Lock()
|
peer.lock.Lock()
|
||||||
}
|
}
|
||||||
|
|
||||||
peer.recalcBLE(mclock.Now())
|
|
||||||
wait := peer.canSend(maxCost)
|
wait := peer.canSend(maxCost)
|
||||||
for wait > 0 {
|
for wait > 0 {
|
||||||
peer.lock.Unlock()
|
peer.lock.Unlock()
|
||||||
time.Sleep(wait)
|
time.Sleep(wait)
|
||||||
peer.lock.Lock()
|
peer.lock.Lock()
|
||||||
peer.recalcBLE(mclock.Now())
|
|
||||||
wait = peer.canSend(maxCost)
|
wait = peer.canSend(maxCost)
|
||||||
}
|
}
|
||||||
peer.assignedRequest = 0
|
peer.assignedRequest = 0
|
||||||
|
|
|
||||||
200
les/handler.go
200
les/handler.go
|
|
@ -18,6 +18,7 @@
|
||||||
package les
|
package les
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
@ -28,12 +29,14 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/event"
|
"github.com/ethereum/go-ethereum/event"
|
||||||
|
"github.com/ethereum/go-ethereum/light"
|
||||||
"github.com/ethereum/go-ethereum/logger"
|
"github.com/ethereum/go-ethereum/logger"
|
||||||
"github.com/ethereum/go-ethereum/logger/glog"
|
"github.com/ethereum/go-ethereum/logger/glog"
|
||||||
"github.com/ethereum/go-ethereum/p2p"
|
"github.com/ethereum/go-ethereum/p2p"
|
||||||
|
|
@ -57,6 +60,7 @@ const (
|
||||||
MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request
|
MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request
|
||||||
MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
||||||
MaxHeaderProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
MaxHeaderProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
||||||
|
MaxBloomBitsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request
|
||||||
MaxTxSend = 64 // Amount of transactions to be send per request
|
MaxTxSend = 64 // Amount of transactions to be send per request
|
||||||
|
|
||||||
disableClientRemovePeer = false
|
disableClientRemovePeer = false
|
||||||
|
|
@ -121,6 +125,11 @@ type ProtocolManager struct {
|
||||||
syncing bool
|
syncing bool
|
||||||
syncDone chan struct{}
|
syncDone chan struct{}
|
||||||
|
|
||||||
|
bloomBitsUpdateChn chan uint64
|
||||||
|
bloomBitsMu sync.Mutex
|
||||||
|
bloomBitsCalcValid bool
|
||||||
|
bloomBitsCalcIdx uint64
|
||||||
|
|
||||||
// wait group is used for graceful shutdowns during downloading
|
// wait group is used for graceful shutdowns during downloading
|
||||||
// and processing
|
// and processing
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
@ -131,19 +140,20 @@ type ProtocolManager struct {
|
||||||
func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) {
|
func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, networkId int, mux *event.TypeMux, pow pow.PoW, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay) (*ProtocolManager, error) {
|
||||||
// Create the protocol manager with the base fields
|
// Create the protocol manager with the base fields
|
||||||
manager := &ProtocolManager{
|
manager := &ProtocolManager{
|
||||||
lightSync: lightSync,
|
lightSync: lightSync,
|
||||||
eventMux: mux,
|
eventMux: mux,
|
||||||
blockchain: blockchain,
|
blockchain: blockchain,
|
||||||
chainConfig: chainConfig,
|
chainConfig: chainConfig,
|
||||||
chainDb: chainDb,
|
chainDb: chainDb,
|
||||||
networkId: networkId,
|
networkId: networkId,
|
||||||
txpool: txpool,
|
txpool: txpool,
|
||||||
txrelay: txrelay,
|
txrelay: txrelay,
|
||||||
odr: odr,
|
odr: odr,
|
||||||
peers: newPeerSet(),
|
peers: newPeerSet(),
|
||||||
newPeerCh: make(chan *peer),
|
newPeerCh: make(chan *peer),
|
||||||
quitSync: make(chan struct{}),
|
quitSync: make(chan struct{}),
|
||||||
noMorePeers: make(chan struct{}),
|
noMorePeers: make(chan struct{}),
|
||||||
|
bloomBitsUpdateChn: make(chan uint64, 100),
|
||||||
}
|
}
|
||||||
// Initiate a sub-protocol for every implemented version we can handle
|
// Initiate a sub-protocol for every implemented version we can handle
|
||||||
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
|
manager.SubProtocols = make([]p2p.Protocol, 0, len(ProtocolVersions))
|
||||||
|
|
@ -203,6 +213,9 @@ func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, network
|
||||||
manager.downloader = downloader.New(downloader.LightSync, chainDb, manager.eventMux, blockchain.HasHeader, nil, blockchain.GetHeaderByHash,
|
manager.downloader = downloader.New(downloader.LightSync, chainDb, manager.eventMux, blockchain.HasHeader, nil, blockchain.GetHeaderByHash,
|
||||||
nil, blockchain.CurrentHeader, nil, nil, nil, blockchain.GetTdByHash,
|
nil, blockchain.CurrentHeader, nil, nil, nil, blockchain.GetTdByHash,
|
||||||
blockchain.InsertHeaderChain, nil, nil, blockchain.Rollback, removePeer)
|
blockchain.InsertHeaderChain, nil, nil, blockchain.Rollback, removePeer)
|
||||||
|
|
||||||
|
blockchain.(*light.LightChain).AddNewHeadCallback(manager.newHeadCallback)
|
||||||
|
go manager.bloomBitsUpdateLoop()
|
||||||
}
|
}
|
||||||
|
|
||||||
if odr != nil {
|
if odr != nil {
|
||||||
|
|
@ -220,7 +233,83 @@ func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, network
|
||||||
return manager, nil
|
return manager, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pm *ProtocolManager) bloomBitsUpdateLoop() {
|
||||||
|
tryUpdate := make(chan struct{}, 1)
|
||||||
|
updating := false
|
||||||
|
var targetSectionCnt uint64
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-pm.quitSync:
|
||||||
|
return
|
||||||
|
case targetSectionCnt = <-pm.bloomBitsUpdateChn:
|
||||||
|
if !updating {
|
||||||
|
updating = true
|
||||||
|
tryUpdate <- struct{}{}
|
||||||
|
}
|
||||||
|
case <-tryUpdate:
|
||||||
|
pm.bloomBitsMu.Lock()
|
||||||
|
sectionIdx := core.GetBloomBitsAvailable(pm.chainDb)
|
||||||
|
if targetSectionCnt > sectionIdx {
|
||||||
|
pm.bloomBitsCalcValid = true
|
||||||
|
pm.bloomBitsCalcIdx = sectionIdx
|
||||||
|
|
||||||
|
pm.bloomBitsMu.Unlock()
|
||||||
|
err := core.MakeBloomBitsSection(pm.chainDb, sectionIdx)
|
||||||
|
pm.bloomBitsMu.Lock()
|
||||||
|
|
||||||
|
if err == nil && pm.bloomBitsCalcValid {
|
||||||
|
glog.V(logger.Info).Infof("Stored bloomBits section #%d", sectionIdx)
|
||||||
|
sectionIdx++
|
||||||
|
core.StoreBloomBitsAvailable(pm.chainDb, sectionIdx)
|
||||||
|
} else {
|
||||||
|
// unsuccessful bloomBits calculation may happen because of a reorg
|
||||||
|
glog.V(logger.Info).Infof("Error calculating bloomBits section #%d: %v valid: %v", sectionIdx, err, pm.bloomBitsCalcValid)
|
||||||
|
}
|
||||||
|
pm.bloomBitsCalcValid = false
|
||||||
|
}
|
||||||
|
pm.bloomBitsMu.Unlock()
|
||||||
|
|
||||||
|
if targetSectionCnt > sectionIdx {
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Millisecond * 100)
|
||||||
|
tryUpdate <- struct{}{}
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
updating = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const bloomBitsConfirmations = 200
|
||||||
|
|
||||||
|
func (pm *ProtocolManager) newHeadCallback(head *types.Header, rollback bool) {
|
||||||
|
pm.bloomBitsMu.Lock()
|
||||||
|
defer pm.bloomBitsMu.Unlock()
|
||||||
|
|
||||||
|
headNum := head.Number.Uint64()
|
||||||
|
rbSectionCnt := headNum / bloombits.SectionSize
|
||||||
|
var newSectionCnt uint64
|
||||||
|
if headNum >= bloomBitsConfirmations-1 {
|
||||||
|
newSectionCnt = (headNum + 1 - bloomBitsConfirmations) / bloombits.SectionSize
|
||||||
|
}
|
||||||
|
lastSectionCnt := core.GetBloomBitsAvailable(pm.chainDb)
|
||||||
|
|
||||||
|
if rbSectionCnt <= pm.bloomBitsCalcIdx {
|
||||||
|
pm.bloomBitsCalcValid = false
|
||||||
|
}
|
||||||
|
if rbSectionCnt < lastSectionCnt {
|
||||||
|
core.StoreBloomBitsAvailable(pm.chainDb, rbSectionCnt)
|
||||||
|
pm.bloomBitsUpdateChn <- rbSectionCnt
|
||||||
|
}
|
||||||
|
if newSectionCnt > lastSectionCnt {
|
||||||
|
pm.bloomBitsUpdateChn <- newSectionCnt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (pm *ProtocolManager) removePeer(id string) {
|
func (pm *ProtocolManager) removePeer(id string) {
|
||||||
|
fmt.Println("removePeer")
|
||||||
// Short circuit if the peer was already removed
|
// Short circuit if the peer was already removed
|
||||||
peer := pm.peers.Peer(id)
|
peer := pm.peers.Peer(id)
|
||||||
if peer == nil {
|
if peer == nil {
|
||||||
|
|
@ -396,7 +485,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsMsg, SendTxMsg, GetHeaderProofsMsg}
|
var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsMsg, SendTxMsg, GetHeaderProofsMsg, GetBloomBitsMsg}
|
||||||
|
|
||||||
// handleMsg is invoked whenever an inbound message is received from a remote
|
// handleMsg is invoked whenever an inbound message is received from a remote
|
||||||
// peer. The remote connection is torn down upon returning any error.
|
// peer. The remote connection is torn down upon returning any error.
|
||||||
|
|
@ -863,6 +952,89 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||||
Obj: resp.Data,
|
Obj: resp.Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case GetBloomBitsMsg:
|
||||||
|
glog.V(logger.Debug).Infof("<=== GetBloomBitsMsg from peer %v", p.id)
|
||||||
|
// Decode the retrieval message
|
||||||
|
var req struct {
|
||||||
|
ReqID uint64
|
||||||
|
Reqs []BloomReq
|
||||||
|
}
|
||||||
|
if err := msg.Decode(&req); err != nil {
|
||||||
|
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||||
|
}
|
||||||
|
// Gather state data until the fetch or network limits is reached
|
||||||
|
var (
|
||||||
|
byteCnt int
|
||||||
|
proofs []BloomResp
|
||||||
|
)
|
||||||
|
reqCnt := len(req.Reqs)
|
||||||
|
if reject(uint64(reqCnt), MaxBloomBitsFetch) {
|
||||||
|
return errResp(ErrRequestRejected, "")
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
lastRoot common.Hash
|
||||||
|
tr *trie.Trie
|
||||||
|
lastProof []rlp.RawValue
|
||||||
|
)
|
||||||
|
for _, req := range req.Reqs {
|
||||||
|
if byteCnt >= softResponseLimit {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if root := getChtRoot(pm.chainDb, req.ChtNum); root != (common.Hash{}) {
|
||||||
|
if root != lastRoot {
|
||||||
|
tr, _ = trie.New(root, pm.chainDb)
|
||||||
|
lastRoot = root
|
||||||
|
}
|
||||||
|
if tr != nil {
|
||||||
|
var encNumber [10]byte
|
||||||
|
binary.BigEndian.PutUint16(encNumber[0:2], uint16(req.BitIdx))
|
||||||
|
binary.BigEndian.PutUint64(encNumber[2:10], req.SectionIdx)
|
||||||
|
proof := tr.Prove(append(bloomBitsTriePrefix, encNumber[:]...))
|
||||||
|
if lastProof != nil {
|
||||||
|
fullProof := make([]rlp.RawValue, len(proof))
|
||||||
|
copy(fullProof, proof)
|
||||||
|
loop:
|
||||||
|
for i, data := range proof {
|
||||||
|
if i < len(lastProof) && bytes.Equal(lastProof[i], data) {
|
||||||
|
proof[i] = []byte{0}
|
||||||
|
} else {
|
||||||
|
break loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastProof = fullProof
|
||||||
|
} else {
|
||||||
|
lastProof = proof
|
||||||
|
}
|
||||||
|
proofs = append(proofs, BloomResp{Proof: proof})
|
||||||
|
byteCnt += len(proof)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost)
|
||||||
|
pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost)
|
||||||
|
return p.SendBloomBits(req.ReqID, bv, proofs)
|
||||||
|
|
||||||
|
case BloomBitsMsg:
|
||||||
|
if pm.odr == nil {
|
||||||
|
return errResp(ErrUnexpectedResponse, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
glog.V(logger.Debug).Infof("<=== BloomBitsMsg from peer %v", p.id)
|
||||||
|
var resp struct {
|
||||||
|
ReqID, BV uint64
|
||||||
|
Data []BloomResp
|
||||||
|
}
|
||||||
|
if err := msg.Decode(&resp); err != nil {
|
||||||
|
return errResp(ErrDecode, "msg %v: %v", msg, err)
|
||||||
|
}
|
||||||
|
p.fcServer.GotReply(resp.ReqID, resp.BV)
|
||||||
|
deliverMsg = &Msg{
|
||||||
|
MsgType: MsgBloomBits,
|
||||||
|
ReqID: resp.ReqID,
|
||||||
|
Obj: resp.Data,
|
||||||
|
}
|
||||||
|
|
||||||
case SendTxMsg:
|
case SendTxMsg:
|
||||||
if pm.txpool == nil {
|
if pm.txpool == nil {
|
||||||
return errResp(ErrUnexpectedResponse, "")
|
return errResp(ErrUnexpectedResponse, "")
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,7 @@ const (
|
||||||
MsgReceipts
|
MsgReceipts
|
||||||
MsgProofs
|
MsgProofs
|
||||||
MsgHeaderProofs
|
MsgHeaderProofs
|
||||||
|
MsgBloomBits
|
||||||
)
|
)
|
||||||
|
|
||||||
// Msg encodes a LES message that delivers reply data for a request
|
// Msg encodes a LES message that delivers reply data for a request
|
||||||
|
|
@ -189,12 +190,14 @@ func (self *LesOdr) networkRequest(ctx context.Context, lreq LesOdrRequest) erro
|
||||||
for {
|
for {
|
||||||
var p *peer
|
var p *peer
|
||||||
if self.serverPool != nil {
|
if self.serverPool != nil {
|
||||||
|
//fmt.Println("waiting for selection")
|
||||||
p = self.serverPool.selectPeerWait(reqID, func(p *peer) (bool, time.Duration) {
|
p = self.serverPool.selectPeerWait(reqID, func(p *peer) (bool, time.Duration) {
|
||||||
if _, ok := exclude[p]; ok || !lreq.CanSend(p) {
|
if _, ok := exclude[p]; ok || !lreq.CanSend(p) {
|
||||||
return false, 0
|
return false, 0
|
||||||
}
|
}
|
||||||
return true, p.fcServer.CanSend(lreq.GetCost(p))
|
return true, p.fcServer.CanSend(lreq.GetCost(p))
|
||||||
}, ctx.Done())
|
}, ctx.Done())
|
||||||
|
//fmt.Println("selected", p)
|
||||||
}
|
}
|
||||||
if p == nil {
|
if p == nil {
|
||||||
select {
|
select {
|
||||||
|
|
@ -203,6 +206,8 @@ func (self *LesOdr) networkRequest(ctx context.Context, lreq LesOdrRequest) erro
|
||||||
case <-req.answered:
|
case <-req.answered:
|
||||||
return nil
|
return nil
|
||||||
case <-time.After(retryPeers):
|
case <-time.After(retryPeers):
|
||||||
|
// exclude = make(map[*peer]struct{}) ?
|
||||||
|
//fmt.Println("retryPeers")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
exclude[p] = struct{}{}
|
exclude[p] = struct{}{}
|
||||||
|
|
@ -213,7 +218,9 @@ func (self *LesOdr) networkRequest(ctx context.Context, lreq LesOdrRequest) erro
|
||||||
req.lock.Unlock()
|
req.lock.Unlock()
|
||||||
reqWg.Add(1)
|
reqWg.Add(1)
|
||||||
cost := lreq.GetCost(p)
|
cost := lreq.GetCost(p)
|
||||||
|
//fmt.Println("waiting to send")
|
||||||
p.fcServer.SendRequest(reqID, cost)
|
p.fcServer.SendRequest(reqID, cost)
|
||||||
|
//fmt.Println("sending")
|
||||||
go self.requestPeer(req, p, delivered, timeout, reqWg)
|
go self.requestPeer(req, p, delivered, timeout, reqWg)
|
||||||
lreq.Request(reqID, p)
|
lreq.Request(reqID, p)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,8 @@ func LesRequest(req light.OdrRequest) LesOdrRequest {
|
||||||
return (*CodeRequest)(r)
|
return (*CodeRequest)(r)
|
||||||
case *light.ChtRequest:
|
case *light.ChtRequest:
|
||||||
return (*ChtRequest)(r)
|
return (*ChtRequest)(r)
|
||||||
|
case *light.BloomRequest:
|
||||||
|
return (*BloomRequest)(r)
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -190,12 +192,12 @@ func (self *TrieRequest) CanSend(peer *peer) bool {
|
||||||
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||||
func (self *TrieRequest) Request(reqID uint64, peer *peer) error {
|
func (self *TrieRequest) Request(reqID uint64, peer *peer) error {
|
||||||
glog.V(logger.Debug).Infof("ODR: requesting trie root %08x key %08x from peer %v", self.Id.Root[:4], self.Key[:4], peer.id)
|
glog.V(logger.Debug).Infof("ODR: requesting trie root %08x key %08x from peer %v", self.Id.Root[:4], self.Key[:4], peer.id)
|
||||||
req := &ProofReq{
|
req := ProofReq{
|
||||||
BHash: self.Id.BlockHash,
|
BHash: self.Id.BlockHash,
|
||||||
AccKey: self.Id.AccKey,
|
AccKey: self.Id.AccKey,
|
||||||
Key: self.Key,
|
Key: self.Key,
|
||||||
}
|
}
|
||||||
return peer.RequestProofs(reqID, self.GetCost(peer), []*ProofReq{req})
|
return peer.RequestProofs(reqID, self.GetCost(peer), []ProofReq{req})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid processes an ODR request reply message from the LES network
|
// Valid processes an ODR request reply message from the LES network
|
||||||
|
|
@ -245,11 +247,11 @@ func (self *CodeRequest) CanSend(peer *peer) bool {
|
||||||
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||||
func (self *CodeRequest) Request(reqID uint64, peer *peer) error {
|
func (self *CodeRequest) Request(reqID uint64, peer *peer) error {
|
||||||
glog.V(logger.Debug).Infof("ODR: requesting node data for hash %08x from peer %v", self.Hash[:4], peer.id)
|
glog.V(logger.Debug).Infof("ODR: requesting node data for hash %08x from peer %v", self.Hash[:4], peer.id)
|
||||||
req := &CodeReq{
|
req := CodeReq{
|
||||||
BHash: self.Id.BlockHash,
|
BHash: self.Id.BlockHash,
|
||||||
AccKey: self.Id.AccKey,
|
AccKey: self.Id.AccKey,
|
||||||
}
|
}
|
||||||
return peer.RequestCode(reqID, self.GetCost(peer), []*CodeReq{req})
|
return peer.RequestCode(reqID, self.GetCost(peer), []CodeReq{req})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid processes an ODR request reply message from the LES network
|
// Valid processes an ODR request reply message from the LES network
|
||||||
|
|
@ -305,11 +307,11 @@ func (self *ChtRequest) CanSend(peer *peer) bool {
|
||||||
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||||
func (self *ChtRequest) Request(reqID uint64, peer *peer) error {
|
func (self *ChtRequest) Request(reqID uint64, peer *peer) error {
|
||||||
glog.V(logger.Debug).Infof("ODR: requesting CHT #%d block #%d from peer %v", self.ChtNum, self.BlockNum, peer.id)
|
glog.V(logger.Debug).Infof("ODR: requesting CHT #%d block #%d from peer %v", self.ChtNum, self.BlockNum, peer.id)
|
||||||
req := &ChtReq{
|
req := ChtReq{
|
||||||
ChtNum: self.ChtNum,
|
ChtNum: self.ChtNum,
|
||||||
BlockNum: self.BlockNum,
|
BlockNum: self.BlockNum,
|
||||||
}
|
}
|
||||||
return peer.RequestHeaderProofs(reqID, self.GetCost(peer), []*ChtReq{req})
|
return peer.RequestHeaderProofs(reqID, self.GetCost(peer), []ChtReq{req})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Valid processes an ODR request reply message from the LES network
|
// Valid processes an ODR request reply message from the LES network
|
||||||
|
|
@ -351,3 +353,90 @@ func (self *ChtRequest) Valid(db ethdb.Database, msg *Msg) bool {
|
||||||
glog.V(logger.Debug).Infof("ODR: validation successful")
|
glog.V(logger.Debug).Infof("ODR: validation successful")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BloomReq struct {
|
||||||
|
ChtNum, BitIdx, SectionIdx, FromLevel uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type BloomResp struct {
|
||||||
|
Proof []rlp.RawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface
|
||||||
|
type BloomRequest light.BloomRequest
|
||||||
|
|
||||||
|
// GetCost returns the cost of the given ODR request according to the serving
|
||||||
|
// peer's cost table (implementation of LesOdrRequest)
|
||||||
|
func (self *BloomRequest) GetCost(peer *peer) uint64 {
|
||||||
|
return peer.GetRequestCost(GetBloomBitsMsg, len(self.SectionIdxList))
|
||||||
|
}
|
||||||
|
|
||||||
|
// CanSend tells if a certain peer is suitable for serving the given request
|
||||||
|
func (self *BloomRequest) CanSend(peer *peer) bool {
|
||||||
|
peer.lock.RLock()
|
||||||
|
defer peer.lock.RUnlock()
|
||||||
|
|
||||||
|
return self.ChtNum <= (peer.headInfo.Number-light.ChtConfirmations)/light.ChtFrequency
|
||||||
|
}
|
||||||
|
|
||||||
|
// Request sends an ODR request to the LES network (implementation of LesOdrRequest)
|
||||||
|
func (self *BloomRequest) Request(reqID uint64, peer *peer) error {
|
||||||
|
glog.V(logger.Debug).Infof("ODR: requesting CHT #%d bloom bit #%d section #%d from peer %v", self.ChtNum, self.BitIdx, self.SectionIdxList[0], peer.id)
|
||||||
|
reqs := make([]BloomReq, len(self.SectionIdxList))
|
||||||
|
for i, sectionIdx := range self.SectionIdxList {
|
||||||
|
reqs[i] = BloomReq{
|
||||||
|
ChtNum: self.ChtNum,
|
||||||
|
BitIdx: self.BitIdx,
|
||||||
|
SectionIdx: sectionIdx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return peer.RequestBloomBits(reqID, self.GetCost(peer), reqs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid processes an ODR request reply message from the LES network
|
||||||
|
// returns true and stores results in memory if the message was a valid reply
|
||||||
|
// to the request (implementation of LesOdrRequest)
|
||||||
|
func (self *BloomRequest) Valid(db ethdb.Database, msg *Msg) bool {
|
||||||
|
glog.V(logger.Debug).Infof("ODR: validating CHT #%d bloom bit #%d section #%d", self.ChtNum, self.BitIdx, self.SectionIdxList[0])
|
||||||
|
|
||||||
|
if msg.MsgType != MsgBloomBits {
|
||||||
|
glog.V(logger.Debug).Infof("ODR: invalid message type")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
proofs := msg.Obj.([]BloomResp)
|
||||||
|
if len(proofs) != len(self.SectionIdxList) {
|
||||||
|
glog.V(logger.Debug).Infof("ODR: invalid number of entries: %d", len(proofs))
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
self.Proofs = make([][]rlp.RawValue, len(self.SectionIdxList))
|
||||||
|
self.BloomBits = make([][]byte, len(self.SectionIdxList))
|
||||||
|
|
||||||
|
var encNumber [10]byte
|
||||||
|
binary.BigEndian.PutUint16(encNumber[0:2], uint16(self.BitIdx))
|
||||||
|
var lastProof []rlp.RawValue
|
||||||
|
//fmt.Println("validating")
|
||||||
|
for i, proof := range proofs {
|
||||||
|
//fmt.Println("section", self.SectionIdxList[i], "proof len", len(proof.Proof))
|
||||||
|
for i, data := range proof.Proof {
|
||||||
|
if len(data) == 1 && data[0] == 0 {
|
||||||
|
if i < len(lastProof) {
|
||||||
|
//fmt.Println("copying", i)
|
||||||
|
proof.Proof[i] = lastProof[i]
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastProof = proof.Proof
|
||||||
|
binary.BigEndian.PutUint64(encNumber[2:10], self.SectionIdxList[i])
|
||||||
|
value, err := trie.VerifyProof(self.ChtRoot, append(bloomBitsTriePrefix, encNumber[:]...), proof.Proof)
|
||||||
|
if err != nil {
|
||||||
|
glog.V(logger.Debug).Infof("ODR: CHT merkle proof verification error: %v", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
self.Proofs[i] = proof.Proof
|
||||||
|
self.BloomBits[i] = value
|
||||||
|
}
|
||||||
|
glog.V(logger.Debug).Infof("ODR: validation successful")
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
||||||
17
les/peer.go
17
les/peer.go
|
|
@ -193,6 +193,11 @@ func (p *peer) SendHeaderProofs(reqID, bv uint64, proofs []ChtResp) error {
|
||||||
return sendResponse(p.rw, HeaderProofsMsg, reqID, bv, proofs)
|
return sendResponse(p.rw, HeaderProofsMsg, reqID, bv, proofs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendBloomBits sends a batch of bloom proofs, corresponding to the ones requested.
|
||||||
|
func (p *peer) SendBloomBits(reqID, bv uint64, proofs []BloomResp) error {
|
||||||
|
return sendResponse(p.rw, BloomBitsMsg, reqID, bv, proofs)
|
||||||
|
}
|
||||||
|
|
||||||
// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the
|
// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the
|
||||||
// specified header query, based on the hash of an origin block.
|
// specified header query, based on the hash of an origin block.
|
||||||
func (p *peer) RequestHeadersByHash(reqID, cost uint64, origin common.Hash, amount int, skip int, reverse bool) error {
|
func (p *peer) RequestHeadersByHash(reqID, cost uint64, origin common.Hash, amount int, skip int, reverse bool) error {
|
||||||
|
|
@ -216,7 +221,7 @@ func (p *peer) RequestBodies(reqID, cost uint64, hashes []common.Hash) error {
|
||||||
|
|
||||||
// RequestCode fetches a batch of arbitrary data from a node's known state
|
// RequestCode fetches a batch of arbitrary data from a node's known state
|
||||||
// data, corresponding to the specified hashes.
|
// data, corresponding to the specified hashes.
|
||||||
func (p *peer) RequestCode(reqID, cost uint64, reqs []*CodeReq) error {
|
func (p *peer) RequestCode(reqID, cost uint64, reqs []CodeReq) error {
|
||||||
glog.V(logger.Debug).Infof("%v fetching %v state data", p, len(reqs))
|
glog.V(logger.Debug).Infof("%v fetching %v state data", p, len(reqs))
|
||||||
return sendRequest(p.rw, GetCodeMsg, reqID, cost, reqs)
|
return sendRequest(p.rw, GetCodeMsg, reqID, cost, reqs)
|
||||||
}
|
}
|
||||||
|
|
@ -228,17 +233,23 @@ func (p *peer) RequestReceipts(reqID, cost uint64, hashes []common.Hash) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestProofs fetches a batch of merkle proofs from a remote node.
|
// RequestProofs fetches a batch of merkle proofs from a remote node.
|
||||||
func (p *peer) RequestProofs(reqID, cost uint64, reqs []*ProofReq) error {
|
func (p *peer) RequestProofs(reqID, cost uint64, reqs []ProofReq) error {
|
||||||
glog.V(logger.Debug).Infof("%v fetching %v proofs", p, len(reqs))
|
glog.V(logger.Debug).Infof("%v fetching %v proofs", p, len(reqs))
|
||||||
return sendRequest(p.rw, GetProofsMsg, reqID, cost, reqs)
|
return sendRequest(p.rw, GetProofsMsg, reqID, cost, reqs)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestHeaderProofs fetches a batch of header merkle proofs from a remote node.
|
// RequestHeaderProofs fetches a batch of header merkle proofs from a remote node.
|
||||||
func (p *peer) RequestHeaderProofs(reqID, cost uint64, reqs []*ChtReq) error {
|
func (p *peer) RequestHeaderProofs(reqID, cost uint64, reqs []ChtReq) error {
|
||||||
glog.V(logger.Debug).Infof("%v fetching %v header proofs", p, len(reqs))
|
glog.V(logger.Debug).Infof("%v fetching %v header proofs", p, len(reqs))
|
||||||
return sendRequest(p.rw, GetHeaderProofsMsg, reqID, cost, reqs)
|
return sendRequest(p.rw, GetHeaderProofsMsg, reqID, cost, reqs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestBloomBits fetches a batch of bloom merkle proofs from a remote node.
|
||||||
|
func (p *peer) RequestBloomBits(reqID, cost uint64, reqs []BloomReq) error {
|
||||||
|
glog.V(logger.Debug).Infof("%v fetching %v bloom proofs", p, len(reqs))
|
||||||
|
return sendRequest(p.rw, GetBloomBitsMsg, reqID, cost, reqs)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *peer) SendTxs(cost uint64, txs types.Transactions) error {
|
func (p *peer) SendTxs(cost uint64, txs types.Transactions) error {
|
||||||
glog.V(logger.Debug).Infof("%v relaying %v txs", p, len(txs))
|
glog.V(logger.Debug).Infof("%v relaying %v txs", p, len(txs))
|
||||||
reqID := getNextReqID()
|
reqID := getNextReqID()
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ const (
|
||||||
var ProtocolVersions = []uint{lpv1}
|
var ProtocolVersions = []uint{lpv1}
|
||||||
|
|
||||||
// Number of implemented message corresponding to different protocol versions.
|
// Number of implemented message corresponding to different protocol versions.
|
||||||
var ProtocolLengths = []uint64{15}
|
var ProtocolLengths = []uint64{17}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
NetworkId = 1
|
NetworkId = 1
|
||||||
|
|
@ -61,6 +61,8 @@ const (
|
||||||
SendTxMsg = 0x0c
|
SendTxMsg = 0x0c
|
||||||
GetHeaderProofsMsg = 0x0d
|
GetHeaderProofsMsg = 0x0d
|
||||||
HeaderProofsMsg = 0x0e
|
HeaderProofsMsg = 0x0e
|
||||||
|
GetBloomBitsMsg = 0x0f
|
||||||
|
BloomBitsMsg = 0x10
|
||||||
)
|
)
|
||||||
|
|
||||||
type errCode int
|
type errCode int
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/eth"
|
"github.com/ethereum/go-ethereum/eth"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
|
@ -311,7 +312,7 @@ func (pm *ProtocolManager) blockLoop() {
|
||||||
more := makeCht(pm.chainDb)
|
more := makeCht(pm.chainDb)
|
||||||
mu.Unlock()
|
mu.Unlock()
|
||||||
if more {
|
if more {
|
||||||
time.Sleep(time.Millisecond * 10)
|
time.Sleep(time.Millisecond * 100)
|
||||||
newCht <- struct{}{}
|
newCht <- struct{}{}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -325,8 +326,8 @@ func (pm *ProtocolManager) blockLoop() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
lastChtKey = []byte("LastChtNumber") // chtNum (uint64 big endian)
|
lastChtKey = []byte("LastChtNumber6") // chtNum (uint64 big endian)
|
||||||
chtPrefix = []byte("cht") // chtPrefix + chtNum (uint64 big endian) -> trie root hash
|
chtPrefix = []byte("cht") // chtPrefix + chtNum (uint64 big endian) -> trie root hash
|
||||||
)
|
)
|
||||||
|
|
||||||
func getChtRoot(db ethdb.Database, num uint64) common.Hash {
|
func getChtRoot(db ethdb.Database, num uint64) common.Hash {
|
||||||
|
|
@ -342,6 +343,8 @@ func storeChtRoot(db ethdb.Database, num uint64, root common.Hash) {
|
||||||
db.Put(append(chtPrefix, encNumber[:]...), root[:])
|
db.Put(append(chtPrefix, encNumber[:]...), root[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var bloomBitsTriePrefix = []byte("bloom")
|
||||||
|
|
||||||
func makeCht(db ethdb.Database) bool {
|
func makeCht(db ethdb.Database) bool {
|
||||||
headHash := core.GetHeadBlockHash(db)
|
headHash := core.GetHeadBlockHash(db)
|
||||||
headNum := core.GetBlockNumber(db, headHash)
|
headNum := core.GetBlockNumber(db, headHash)
|
||||||
|
|
@ -372,31 +375,63 @@ func makeCht(db ethdb.Database) bool {
|
||||||
t, _ = trie.New(common.Hash{}, db)
|
t, _ = trie.New(common.Hash{}, db)
|
||||||
}
|
}
|
||||||
|
|
||||||
for num := lastChtNum * light.ChtFrequency; num < (lastChtNum+1)*light.ChtFrequency; num++ {
|
var compSize, decompSize uint64
|
||||||
hash := core.GetCanonicalHash(db, num)
|
loop:
|
||||||
if hash == (common.Hash{}) {
|
for newChtNum > lastChtNum {
|
||||||
panic("Canonical hash not found")
|
bloomBitsCreator := &bloombits.BloomBitsCreator{}
|
||||||
|
|
||||||
|
for num := lastChtNum * light.ChtFrequency; num < (lastChtNum+1)*light.ChtFrequency; num++ {
|
||||||
|
|
||||||
|
hash := core.GetCanonicalHash(db, num)
|
||||||
|
if hash == (common.Hash{}) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
td := core.GetTd(db, hash, num)
|
||||||
|
if td == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
var encNumber [8]byte
|
||||||
|
binary.BigEndian.PutUint64(encNumber[:], num)
|
||||||
|
var node light.ChtNode
|
||||||
|
node.Hash = hash
|
||||||
|
node.Td = td
|
||||||
|
data, _ := rlp.EncodeToBytes(node)
|
||||||
|
t.Update(encNumber[:], data)
|
||||||
|
|
||||||
|
header := core.GetHeader(db, hash, num)
|
||||||
|
if header == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
bloomBitsCreator.AddHeaderBloom(header.Bloom)
|
||||||
}
|
}
|
||||||
td := core.GetTd(db, hash, num)
|
|
||||||
if td == nil {
|
for i := uint(0); i < bloombits.BloomLength; i++ {
|
||||||
panic("TD not found")
|
var encKey [10]byte
|
||||||
|
binary.BigEndian.PutUint16(encKey[0:2], uint16(i))
|
||||||
|
binary.BigEndian.PutUint64(encKey[2:10], lastChtNum)
|
||||||
|
key := append(bloomBitsTriePrefix, encKey[:]...)
|
||||||
|
data := bloombits.CompressBloomBits(bloomBitsCreator.GetBitVector(i))
|
||||||
|
decompSize += bloombits.SectionSize / 8
|
||||||
|
compSize += uint64(len(data))
|
||||||
|
if len(data) > 0 {
|
||||||
|
t.Update(key, data)
|
||||||
|
} else {
|
||||||
|
t.Delete(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lastChtNum++
|
||||||
|
|
||||||
|
if lastChtNum%16 == 0 {
|
||||||
|
break loop
|
||||||
}
|
}
|
||||||
var encNumber [8]byte
|
|
||||||
binary.BigEndian.PutUint64(encNumber[:], num)
|
|
||||||
var node light.ChtNode
|
|
||||||
node.Hash = hash
|
|
||||||
node.Td = td
|
|
||||||
data, _ := rlp.EncodeToBytes(node)
|
|
||||||
t.Update(encNumber[:], data)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
root, err := t.Commit()
|
root, err := t.Commit()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lastChtNum = 0
|
lastChtNum = 0
|
||||||
} else {
|
} else {
|
||||||
lastChtNum++
|
glog.V(logger.Info).Infof("Storing CHT #%d root hash: %064x compression ratio: %f", lastChtNum, root, float64(compSize)/float64(decompSize))
|
||||||
|
|
||||||
glog.V(logger.Detail).Infof("cht: %d %064x", lastChtNum, root)
|
|
||||||
|
|
||||||
storeChtRoot(db, lastChtNum, root)
|
storeChtRoot(db, lastChtNum, root)
|
||||||
var data [8]byte
|
var data [8]byte
|
||||||
|
|
|
||||||
|
|
@ -333,7 +333,7 @@ func (pool *serverPool) selectPeer(reqID uint64, canSend func(*peer) (bool, time
|
||||||
func (pool *serverPool) selectPeerWait(reqID uint64, canSend func(*peer) (bool, time.Duration), abort <-chan struct{}) *peer {
|
func (pool *serverPool) selectPeerWait(reqID uint64, canSend func(*peer) (bool, time.Duration), abort <-chan struct{}) *peer {
|
||||||
for {
|
for {
|
||||||
peer, wait, locked := pool.selectPeer(reqID, canSend)
|
peer, wait, locked := pool.selectPeer(reqID, canSend)
|
||||||
if locked {
|
if locked || peer == nil {
|
||||||
return peer
|
return peer
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ type LightChain struct {
|
||||||
procInterrupt int32 // interrupt signaler for block processing
|
procInterrupt int32 // interrupt signaler for block processing
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
|
||||||
|
newHeadCallback newHeadCallback
|
||||||
|
|
||||||
pow pow.PoW
|
pow pow.PoW
|
||||||
validator core.HeaderValidator
|
validator core.HeaderValidator
|
||||||
}
|
}
|
||||||
|
|
@ -102,14 +104,15 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block")
|
glog.V(logger.Info).Infoln("WARNING: Wrote default ethereum genesis block")
|
||||||
|
core.StoreBloomBitsAvailable(odr.Database(), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
if bc.genesisBlock.Hash() == (common.Hash{212, 229, 103, 64, 248, 118, 174, 248, 192, 16, 184, 106, 64, 213, 245, 103, 69, 161, 24, 208, 144, 106, 52, 230, 154, 236, 140, 13, 177, 203, 143, 163}) {
|
if bc.genesisBlock.Hash() == (common.Hash{212, 229, 103, 64, 248, 118, 174, 248, 192, 16, 184, 106, 64, 213, 245, 103, 69, 161, 24, 208, 144, 106, 52, 230, 154, 236, 140, 13, 177, 203, 143, 163}) {
|
||||||
// add trusted CHT
|
// add trusted CHT
|
||||||
if config.DAOForkSupport {
|
if config.DAOForkSupport {
|
||||||
WriteTrustedCht(bc.chainDb, TrustedCht{
|
WriteTrustedCht(bc.chainDb, TrustedCht{
|
||||||
Number: 637,
|
Number: 752,
|
||||||
Root: common.HexToHash("01e408d9b1942f05dba1a879f3eaafe34d219edaeb8223fecf1244cc023d3e23"),
|
Root: common.HexToHash("4d2e4d9ce20626b62d6fe7598fffd8f63175c8cc2a60dc4211cf6d88f52a78e8"),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
WriteTrustedCht(bc.chainDb, TrustedCht{
|
WriteTrustedCht(bc.chainDb, TrustedCht{
|
||||||
|
|
@ -145,6 +148,15 @@ func NewLightChain(odr OdrBackend, config *params.ChainConfig, pow pow.PoW, mux
|
||||||
return bc, nil
|
return bc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type newHeadCallback func(*types.Header, bool)
|
||||||
|
|
||||||
|
func (self *LightChain) AddNewHeadCallback(cb newHeadCallback) {
|
||||||
|
self.chainmu.Lock()
|
||||||
|
self.newHeadCallback = cb
|
||||||
|
cb(self.hc.CurrentHeader(), false)
|
||||||
|
self.chainmu.Unlock()
|
||||||
|
}
|
||||||
|
|
||||||
func (self *LightChain) getProcInterrupt() bool {
|
func (self *LightChain) getProcInterrupt() bool {
|
||||||
return atomic.LoadInt32(&self.procInterrupt) == 1
|
return atomic.LoadInt32(&self.procInterrupt) == 1
|
||||||
}
|
}
|
||||||
|
|
@ -355,13 +367,20 @@ func (self *LightChain) Rollback(chain []common.Hash) {
|
||||||
self.mu.Lock()
|
self.mu.Lock()
|
||||||
defer self.mu.Unlock()
|
defer self.mu.Unlock()
|
||||||
|
|
||||||
|
var rollbackHead *types.Header
|
||||||
|
|
||||||
for i := len(chain) - 1; i >= 0; i-- {
|
for i := len(chain) - 1; i >= 0; i-- {
|
||||||
hash := chain[i]
|
hash := chain[i]
|
||||||
|
|
||||||
if head := self.hc.CurrentHeader(); head.Hash() == hash {
|
if head := self.hc.CurrentHeader(); head.Hash() == hash {
|
||||||
self.hc.SetCurrentHeader(self.GetHeader(head.ParentHash, head.Number.Uint64()-1))
|
rollbackHead = self.GetHeader(head.ParentHash, head.Number.Uint64()-1)
|
||||||
|
self.hc.SetCurrentHeader(rollbackHead)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if rollbackHead != nil && self.newHeadCallback != nil {
|
||||||
|
self.newHeadCallback(rollbackHead, true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// postChainEvents iterates over the events generated by a chain insertion and
|
// postChainEvents iterates over the events generated by a chain insertion and
|
||||||
|
|
@ -402,7 +421,11 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
||||||
self.mu.Lock()
|
self.mu.Lock()
|
||||||
defer self.mu.Unlock()
|
defer self.mu.Unlock()
|
||||||
|
|
||||||
status, err := self.hc.WriteHeader(header)
|
status, err := self.hc.WriteHeader(header, func(head *types.Header) {
|
||||||
|
if self.newHeadCallback != nil {
|
||||||
|
self.newHeadCallback(head, true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
switch status {
|
switch status {
|
||||||
case core.CanonStatTy:
|
case core.CanonStatTy:
|
||||||
|
|
@ -424,6 +447,11 @@ func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
i, err := self.hc.InsertHeaderChain(chain, checkFreq, whFunc)
|
i, err := self.hc.InsertHeaderChain(chain, checkFreq, whFunc)
|
||||||
|
|
||||||
|
if self.newHeadCallback != nil {
|
||||||
|
self.newHeadCallback(self.hc.CurrentHeader(), false)
|
||||||
|
}
|
||||||
|
|
||||||
go self.postChainEvents(events)
|
go self.postChainEvents(events)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
20
light/odr.go
20
light/odr.go
|
|
@ -138,7 +138,7 @@ func (req *ReceiptsRequest) StoreResult(db ethdb.Database) {
|
||||||
core.WriteBlockReceipts(db, req.Hash, req.Number, req.Receipts)
|
core.WriteBlockReceipts(db, req.Hash, req.Number, req.Receipts)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TrieRequest is the ODR request type for state/storage trie entries
|
// ChtRequest is the ODR request type for retrieving old headers from a CHT structure
|
||||||
type ChtRequest struct {
|
type ChtRequest struct {
|
||||||
OdrRequest
|
OdrRequest
|
||||||
ChtNum, BlockNum uint64
|
ChtNum, BlockNum uint64
|
||||||
|
|
@ -155,5 +155,21 @@ func (req *ChtRequest) StoreResult(db ethdb.Database) {
|
||||||
hash, num := req.Header.Hash(), req.Header.Number.Uint64()
|
hash, num := req.Header.Hash(), req.Header.Number.Uint64()
|
||||||
core.WriteTd(db, hash, num, req.Td)
|
core.WriteTd(db, hash, num, req.Td)
|
||||||
core.WriteCanonicalHash(db, hash, num)
|
core.WriteCanonicalHash(db, hash, num)
|
||||||
//storeProof(db, req.Proof)
|
}
|
||||||
|
|
||||||
|
// BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure
|
||||||
|
type BloomRequest struct {
|
||||||
|
OdrRequest
|
||||||
|
ChtNum, BitIdx uint64
|
||||||
|
SectionIdxList []uint64
|
||||||
|
ChtRoot common.Hash
|
||||||
|
BloomBits [][]byte
|
||||||
|
Proofs [][]rlp.RawValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreResult stores the retrieved data in local database
|
||||||
|
func (req *BloomRequest) StoreResult(db ethdb.Database) {
|
||||||
|
for i, sectionIdx := range req.SectionIdxList {
|
||||||
|
core.StoreBloomBits(db, req.BitIdx, sectionIdx, req.BloomBits[i])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ import (
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
|
@ -37,10 +38,12 @@ var sha3_nil = crypto.Keccak256Hash(nil)
|
||||||
var (
|
var (
|
||||||
ErrNoTrustedCht = errors.New("No trusted canonical hash trie")
|
ErrNoTrustedCht = errors.New("No trusted canonical hash trie")
|
||||||
ErrNoHeader = errors.New("Header not found")
|
ErrNoHeader = errors.New("Header not found")
|
||||||
|
trustedChtKey = []byte("TrustedCHT")
|
||||||
|
)
|
||||||
|
|
||||||
ChtFrequency = uint64(4096)
|
const (
|
||||||
ChtConfirmations = uint64(2048)
|
ChtFrequency = 4096
|
||||||
trustedChtKey = []byte("TrustedCHT")
|
ChtConfirmations = 2048
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChtNode struct {
|
type ChtNode struct {
|
||||||
|
|
@ -65,6 +68,10 @@ func GetTrustedCht(db ethdb.Database) TrustedCht {
|
||||||
func WriteTrustedCht(db ethdb.Database, cht TrustedCht) {
|
func WriteTrustedCht(db ethdb.Database, cht TrustedCht) {
|
||||||
data, _ := rlp.EncodeToBytes(cht)
|
data, _ := rlp.EncodeToBytes(cht)
|
||||||
db.Put(trustedChtKey, data)
|
db.Put(trustedChtKey, data)
|
||||||
|
b := cht.Number * ChtFrequency / bloombits.SectionSize
|
||||||
|
if core.GetBloomBitsAvailable(db) < b {
|
||||||
|
core.StoreBloomBitsAvailable(db, b)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteTrustedCht(db ethdb.Database) {
|
func DeleteTrustedCht(db ethdb.Database) {
|
||||||
|
|
@ -185,3 +192,38 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
|
||||||
return r.Receipts, nil
|
return r.Receipts, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint64, sectionIdxList []uint64) ([]bloombits.CompVector, error) {
|
||||||
|
result := make([]bloombits.CompVector, len(sectionIdxList))
|
||||||
|
var (
|
||||||
|
reqList []uint64
|
||||||
|
reqIdx []int
|
||||||
|
)
|
||||||
|
cht := GetTrustedCht(odr.Database())
|
||||||
|
|
||||||
|
for i, sectionIdx := range sectionIdxList {
|
||||||
|
bloomBits, err := core.GetBloomBits(odr.Database(), bitIdx, sectionIdx)
|
||||||
|
if err == nil {
|
||||||
|
result[i] = bloomBits
|
||||||
|
} else {
|
||||||
|
if sectionIdx >= cht.Number {
|
||||||
|
return nil, ErrNoTrustedCht
|
||||||
|
}
|
||||||
|
reqList = append(reqList, sectionIdx)
|
||||||
|
reqIdx = append(reqIdx, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if reqList == nil {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r := &BloomRequest{ChtRoot: cht.Root, ChtNum: cht.Number, BitIdx: bitIdx, SectionIdxList: reqList}
|
||||||
|
if err := odr.Retrieve(ctx, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else {
|
||||||
|
for i, idx := range reqIdx {
|
||||||
|
result[idx] = r.BloomBits[i]
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue