| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- // Copyright 2016 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 les implements the Light Ethereum Subprotocol.
- package les
- import (
- "encoding/binary"
- "math"
- "sync"
- "time"
- "github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/ethdb"
- "github.com/ethereum/go-ethereum/les/flowcontrol"
- "github.com/ethereum/go-ethereum/light"
- "github.com/ethereum/go-ethereum/logger"
- "github.com/ethereum/go-ethereum/logger/glog"
- "github.com/ethereum/go-ethereum/p2p"
- "github.com/ethereum/go-ethereum/rlp"
- "github.com/ethereum/go-ethereum/trie"
- )
- type LesServer struct {
- protocolManager *ProtocolManager
- fcManager *flowcontrol.ClientManager // nil if our node is client only
- fcCostStats *requestCostStats
- defParams *flowcontrol.ServerParams
- srvr *p2p.Server
- synced, stopped bool
- lock sync.Mutex
- }
- func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
- pm, err := NewProtocolManager(config.ChainConfig, false, config.NetworkId, eth.EventMux(), eth.Pow(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil)
- if err != nil {
- return nil, err
- }
- pm.blockLoop()
- srv := &LesServer{protocolManager: pm}
- pm.server = srv
- srv.defParams = &flowcontrol.ServerParams{
- BufLimit: 300000000,
- MinRecharge: 50000,
- }
- srv.fcManager = flowcontrol.NewClientManager(uint64(config.LightServ), 10, 1000000000)
- srv.fcCostStats = newCostStats(eth.ChainDb())
- return srv, nil
- }
- func (s *LesServer) Protocols() []p2p.Protocol {
- return s.protocolManager.SubProtocols
- }
- // Start only starts the actual service if the ETH protocol has already been synced,
- // otherwise it will be started by Synced()
- func (s *LesServer) Start(srvr *p2p.Server) {
- s.lock.Lock()
- defer s.lock.Unlock()
- s.srvr = srvr
- if s.synced {
- s.protocolManager.Start(s.srvr)
- }
- }
- // Synced notifies the server that the ETH protocol has been synced and LES service can be started
- func (s *LesServer) Synced() {
- s.lock.Lock()
- defer s.lock.Unlock()
- s.synced = true
- if s.srvr != nil && !s.stopped {
- s.protocolManager.Start(s.srvr)
- }
- }
- // Stop stops the LES service
- func (s *LesServer) Stop() {
- s.lock.Lock()
- defer s.lock.Unlock()
- s.stopped = true
- s.fcCostStats.store()
- s.fcManager.Stop()
- go func() {
- <-s.protocolManager.noMorePeers
- }()
- s.protocolManager.Stop()
- }
- type requestCosts struct {
- baseCost, reqCost uint64
- }
- type requestCostTable map[uint64]*requestCosts
- type RequestCostList []struct {
- MsgCode, BaseCost, ReqCost uint64
- }
- func (list RequestCostList) decode() requestCostTable {
- table := make(requestCostTable)
- for _, e := range list {
- table[e.MsgCode] = &requestCosts{
- baseCost: e.BaseCost,
- reqCost: e.ReqCost,
- }
- }
- return table
- }
- func (table requestCostTable) encode() RequestCostList {
- list := make(RequestCostList, len(table))
- for idx, code := range reqList {
- list[idx].MsgCode = code
- list[idx].BaseCost = table[code].baseCost
- list[idx].ReqCost = table[code].reqCost
- }
- return list
- }
- type linReg struct {
- sumX, sumY, sumXX, sumXY float64
- cnt uint64
- }
- const linRegMaxCnt = 100000
- func (l *linReg) add(x, y float64) {
- if l.cnt >= linRegMaxCnt {
- sub := float64(l.cnt+1-linRegMaxCnt) / linRegMaxCnt
- l.sumX -= l.sumX * sub
- l.sumY -= l.sumY * sub
- l.sumXX -= l.sumXX * sub
- l.sumXY -= l.sumXY * sub
- l.cnt = linRegMaxCnt - 1
- }
- l.cnt++
- l.sumX += x
- l.sumY += y
- l.sumXX += x * x
- l.sumXY += x * y
- }
- func (l *linReg) calc() (b, m float64) {
- if l.cnt == 0 {
- return 0, 0
- }
- cnt := float64(l.cnt)
- d := cnt*l.sumXX - l.sumX*l.sumX
- if d < 0.001 {
- return l.sumY / cnt, 0
- }
- m = (cnt*l.sumXY - l.sumX*l.sumY) / d
- b = (l.sumY / cnt) - (m * l.sumX / cnt)
- return b, m
- }
- func (l *linReg) toBytes() []byte {
- var arr [40]byte
- binary.BigEndian.PutUint64(arr[0:8], math.Float64bits(l.sumX))
- binary.BigEndian.PutUint64(arr[8:16], math.Float64bits(l.sumY))
- binary.BigEndian.PutUint64(arr[16:24], math.Float64bits(l.sumXX))
- binary.BigEndian.PutUint64(arr[24:32], math.Float64bits(l.sumXY))
- binary.BigEndian.PutUint64(arr[32:40], l.cnt)
- return arr[:]
- }
- func linRegFromBytes(data []byte) *linReg {
- if len(data) != 40 {
- return nil
- }
- l := &linReg{}
- l.sumX = math.Float64frombits(binary.BigEndian.Uint64(data[0:8]))
- l.sumY = math.Float64frombits(binary.BigEndian.Uint64(data[8:16]))
- l.sumXX = math.Float64frombits(binary.BigEndian.Uint64(data[16:24]))
- l.sumXY = math.Float64frombits(binary.BigEndian.Uint64(data[24:32]))
- l.cnt = binary.BigEndian.Uint64(data[32:40])
- return l
- }
- type requestCostStats struct {
- lock sync.RWMutex
- db ethdb.Database
- stats map[uint64]*linReg
- }
- type requestCostStatsRlp []struct {
- MsgCode uint64
- Data []byte
- }
- var rcStatsKey = []byte("_requestCostStats")
- func newCostStats(db ethdb.Database) *requestCostStats {
- stats := make(map[uint64]*linReg)
- for _, code := range reqList {
- stats[code] = &linReg{cnt: 100}
- }
- if db != nil {
- data, err := db.Get(rcStatsKey)
- var statsRlp requestCostStatsRlp
- if err == nil {
- err = rlp.DecodeBytes(data, &statsRlp)
- }
- if err == nil {
- for _, r := range statsRlp {
- if stats[r.MsgCode] != nil {
- if l := linRegFromBytes(r.Data); l != nil {
- stats[r.MsgCode] = l
- }
- }
- }
- }
- }
- return &requestCostStats{
- db: db,
- stats: stats,
- }
- }
- func (s *requestCostStats) store() {
- s.lock.Lock()
- defer s.lock.Unlock()
- statsRlp := make(requestCostStatsRlp, len(reqList))
- for i, code := range reqList {
- statsRlp[i].MsgCode = code
- statsRlp[i].Data = s.stats[code].toBytes()
- }
- if data, err := rlp.EncodeToBytes(statsRlp); err == nil {
- s.db.Put(rcStatsKey, data)
- }
- }
- func (s *requestCostStats) getCurrentList() RequestCostList {
- s.lock.Lock()
- defer s.lock.Unlock()
- list := make(RequestCostList, len(reqList))
- //fmt.Println("RequestCostList")
- for idx, code := range reqList {
- b, m := s.stats[code].calc()
- //fmt.Println(code, s.stats[code].cnt, b/1000000, m/1000000)
- if m < 0 {
- b += m
- m = 0
- }
- if b < 0 {
- b = 0
- }
- list[idx].MsgCode = code
- list[idx].BaseCost = uint64(b * 2)
- list[idx].ReqCost = uint64(m * 2)
- }
- return list
- }
- func (s *requestCostStats) update(msgCode, reqCnt, cost uint64) {
- s.lock.Lock()
- defer s.lock.Unlock()
- c, ok := s.stats[msgCode]
- if !ok || reqCnt == 0 {
- return
- }
- c.add(float64(reqCnt), float64(cost))
- }
- func (pm *ProtocolManager) blockLoop() {
- pm.wg.Add(1)
- sub := pm.eventMux.Subscribe(core.ChainHeadEvent{})
- newCht := make(chan struct{}, 10)
- newCht <- struct{}{}
- go func() {
- var mu sync.Mutex
- var lastHead *types.Header
- lastBroadcastTd := common.Big0
- for {
- select {
- case ev := <-sub.Chan():
- peers := pm.peers.AllPeers()
- if len(peers) > 0 {
- header := ev.Data.(core.ChainHeadEvent).Block.Header()
- hash := header.Hash()
- number := header.Number.Uint64()
- td := core.GetTd(pm.chainDb, hash, number)
- if td != nil && td.Cmp(lastBroadcastTd) > 0 {
- var reorg uint64
- if lastHead != nil {
- reorg = lastHead.Number.Uint64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64()
- }
- lastHead = header
- lastBroadcastTd = td
- glog.V(logger.Debug).Infoln("===> ", number, hash, td, reorg)
- announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}
- for _, p := range peers {
- select {
- case p.announceChn <- announce:
- default:
- pm.removePeer(p.id)
- }
- }
- }
- }
- newCht <- struct{}{}
- case <-newCht:
- go func() {
- mu.Lock()
- more := makeCht(pm.chainDb)
- mu.Unlock()
- if more {
- time.Sleep(time.Millisecond * 10)
- newCht <- struct{}{}
- }
- }()
- case <-pm.quitSync:
- sub.Unsubscribe()
- pm.wg.Done()
- return
- }
- }
- }()
- }
- var (
- lastChtKey = []byte("LastChtNumber") // chtNum (uint64 big endian)
- chtPrefix = []byte("cht") // chtPrefix + chtNum (uint64 big endian) -> trie root hash
- )
- func getChtRoot(db ethdb.Database, num uint64) common.Hash {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], num)
- data, _ := db.Get(append(chtPrefix, encNumber[:]...))
- return common.BytesToHash(data)
- }
- func storeChtRoot(db ethdb.Database, num uint64, root common.Hash) {
- var encNumber [8]byte
- binary.BigEndian.PutUint64(encNumber[:], num)
- db.Put(append(chtPrefix, encNumber[:]...), root[:])
- }
- func makeCht(db ethdb.Database) bool {
- headHash := core.GetHeadBlockHash(db)
- headNum := core.GetBlockNumber(db, headHash)
- var newChtNum uint64
- if headNum > light.ChtConfirmations {
- newChtNum = (headNum - light.ChtConfirmations) / light.ChtFrequency
- }
- var lastChtNum uint64
- data, _ := db.Get(lastChtKey)
- if len(data) == 8 {
- lastChtNum = binary.BigEndian.Uint64(data[:])
- }
- if newChtNum <= lastChtNum {
- return false
- }
- var t *trie.Trie
- if lastChtNum > 0 {
- var err error
- t, err = trie.New(getChtRoot(db, lastChtNum), db)
- if err != nil {
- lastChtNum = 0
- }
- }
- if lastChtNum == 0 {
- t, _ = trie.New(common.Hash{}, db)
- }
- for num := lastChtNum * light.ChtFrequency; num < (lastChtNum+1)*light.ChtFrequency; num++ {
- hash := core.GetCanonicalHash(db, num)
- if hash == (common.Hash{}) {
- panic("Canonical hash not found")
- }
- td := core.GetTd(db, hash, num)
- if td == nil {
- panic("TD not found")
- }
- 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()
- if err != nil {
- lastChtNum = 0
- } else {
- lastChtNum++
- glog.V(logger.Detail).Infof("cht: %d %064x", lastChtNum, root)
- storeChtRoot(db, lastChtNum, root)
- var data [8]byte
- binary.BigEndian.PutUint64(data[:], lastChtNum)
- db.Put(lastChtKey, data[:])
- }
- return newChtNum > lastChtNum
- }
|