github.com/humaniq/go-ethereum@v1.6.8-0.20171225131628-061223a13848/les/server.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // Package les implements the Light Ethereum Subprotocol.
    18  package les
    19  
    20  import (
    21  	"crypto/ecdsa"
    22  	"encoding/binary"
    23  	"fmt"
    24  	"math"
    25  	"sync"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/core"
    29  	"github.com/ethereum/go-ethereum/core/types"
    30  	"github.com/ethereum/go-ethereum/eth"
    31  	"github.com/ethereum/go-ethereum/ethdb"
    32  	"github.com/ethereum/go-ethereum/les/flowcontrol"
    33  	"github.com/ethereum/go-ethereum/light"
    34  	"github.com/ethereum/go-ethereum/log"
    35  	"github.com/ethereum/go-ethereum/p2p"
    36  	"github.com/ethereum/go-ethereum/p2p/discv5"
    37  	"github.com/ethereum/go-ethereum/rlp"
    38  )
    39  
    40  type LesServer struct {
    41  	protocolManager *ProtocolManager
    42  	fcManager       *flowcontrol.ClientManager // nil if our node is client only
    43  	fcCostStats     *requestCostStats
    44  	defParams       *flowcontrol.ServerParams
    45  	lesTopics       []discv5.Topic
    46  	privateKey      *ecdsa.PrivateKey
    47  	quitSync        chan struct{}
    48  
    49  	chtIndexer, bloomTrieIndexer *core.ChainIndexer
    50  }
    51  
    52  func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
    53  	quitSync := make(chan struct{})
    54  	pm, err := NewProtocolManager(eth.BlockChain().Config(), false, ServerProtocolVersions, config.NetworkId, eth.EventMux(), eth.Engine(), newPeerSet(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil, quitSync, new(sync.WaitGroup))
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  
    59  	lesTopics := make([]discv5.Topic, len(ServerProtocolVersions))
    60  	for i, pv := range ServerProtocolVersions {
    61  		lesTopics[i] = lesTopic(eth.BlockChain().Genesis().Hash(), pv)
    62  	}
    63  
    64  	srv := &LesServer{
    65  		protocolManager:  pm,
    66  		quitSync:         quitSync,
    67  		lesTopics:        lesTopics,
    68  		chtIndexer:       light.NewChtIndexer(eth.ChainDb(), false),
    69  		bloomTrieIndexer: light.NewBloomTrieIndexer(eth.ChainDb(), false),
    70  	}
    71  	logger := log.New()
    72  
    73  	chtV1SectionCount, _, _ := srv.chtIndexer.Sections() // indexer still uses LES/1 4k section size for backwards server compatibility
    74  	chtV2SectionCount := chtV1SectionCount / (light.ChtFrequency / light.ChtV1Frequency)
    75  	if chtV2SectionCount != 0 {
    76  		// convert to LES/2 section
    77  		chtLastSection := chtV2SectionCount - 1
    78  		// convert last LES/2 section index back to LES/1 index for chtIndexer.SectionHead
    79  		chtLastSectionV1 := (chtLastSection+1)*(light.ChtFrequency/light.ChtV1Frequency) - 1
    80  		chtSectionHead := srv.chtIndexer.SectionHead(chtLastSectionV1)
    81  		chtRoot := light.GetChtV2Root(pm.chainDb, chtLastSection, chtSectionHead)
    82  		logger.Info("CHT", "section", chtLastSection, "sectionHead", fmt.Sprintf("%064x", chtSectionHead), "root", fmt.Sprintf("%064x", chtRoot))
    83  	}
    84  
    85  	bloomTrieSectionCount, _, _ := srv.bloomTrieIndexer.Sections()
    86  	if bloomTrieSectionCount != 0 {
    87  		bloomTrieLastSection := bloomTrieSectionCount - 1
    88  		bloomTrieSectionHead := srv.bloomTrieIndexer.SectionHead(bloomTrieLastSection)
    89  		bloomTrieRoot := light.GetBloomTrieRoot(pm.chainDb, bloomTrieLastSection, bloomTrieSectionHead)
    90  		logger.Info("BloomTrie", "section", bloomTrieLastSection, "sectionHead", fmt.Sprintf("%064x", bloomTrieSectionHead), "root", fmt.Sprintf("%064x", bloomTrieRoot))
    91  	}
    92  
    93  	srv.chtIndexer.Start(eth.BlockChain())
    94  	pm.server = srv
    95  
    96  	srv.defParams = &flowcontrol.ServerParams{
    97  		BufLimit:    300000000,
    98  		MinRecharge: 50000,
    99  	}
   100  	srv.fcManager = flowcontrol.NewClientManager(uint64(config.LightServ), 10, 1000000000)
   101  	srv.fcCostStats = newCostStats(eth.ChainDb())
   102  	return srv, nil
   103  }
   104  
   105  func (s *LesServer) Protocols() []p2p.Protocol {
   106  	return s.protocolManager.SubProtocols
   107  }
   108  
   109  // Start starts the LES server
   110  func (s *LesServer) Start(srvr *p2p.Server) {
   111  	s.protocolManager.Start()
   112  	for _, topic := range s.lesTopics {
   113  		topic := topic
   114  		go func() {
   115  			logger := log.New("topic", topic)
   116  			logger.Info("Starting topic registration")
   117  			defer logger.Info("Terminated topic registration")
   118  
   119  			srvr.DiscV5.RegisterTopic(topic, s.quitSync)
   120  		}()
   121  	}
   122  	s.privateKey = srvr.PrivateKey
   123  	s.protocolManager.blockLoop()
   124  }
   125  
   126  func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) {
   127  	bloomIndexer.AddChildIndexer(s.bloomTrieIndexer)
   128  }
   129  
   130  // Stop stops the LES service
   131  func (s *LesServer) Stop() {
   132  	s.chtIndexer.Close()
   133  	// bloom trie indexer is closed by parent bloombits indexer
   134  	s.fcCostStats.store()
   135  	s.fcManager.Stop()
   136  	go func() {
   137  		<-s.protocolManager.noMorePeers
   138  	}()
   139  	s.protocolManager.Stop()
   140  }
   141  
   142  type requestCosts struct {
   143  	baseCost, reqCost uint64
   144  }
   145  
   146  type requestCostTable map[uint64]*requestCosts
   147  
   148  type RequestCostList []struct {
   149  	MsgCode, BaseCost, ReqCost uint64
   150  }
   151  
   152  func (list RequestCostList) decode() requestCostTable {
   153  	table := make(requestCostTable)
   154  	for _, e := range list {
   155  		table[e.MsgCode] = &requestCosts{
   156  			baseCost: e.BaseCost,
   157  			reqCost:  e.ReqCost,
   158  		}
   159  	}
   160  	return table
   161  }
   162  
   163  type linReg struct {
   164  	sumX, sumY, sumXX, sumXY float64
   165  	cnt                      uint64
   166  }
   167  
   168  const linRegMaxCnt = 100000
   169  
   170  func (l *linReg) add(x, y float64) {
   171  	if l.cnt >= linRegMaxCnt {
   172  		sub := float64(l.cnt+1-linRegMaxCnt) / linRegMaxCnt
   173  		l.sumX -= l.sumX * sub
   174  		l.sumY -= l.sumY * sub
   175  		l.sumXX -= l.sumXX * sub
   176  		l.sumXY -= l.sumXY * sub
   177  		l.cnt = linRegMaxCnt - 1
   178  	}
   179  	l.cnt++
   180  	l.sumX += x
   181  	l.sumY += y
   182  	l.sumXX += x * x
   183  	l.sumXY += x * y
   184  }
   185  
   186  func (l *linReg) calc() (b, m float64) {
   187  	if l.cnt == 0 {
   188  		return 0, 0
   189  	}
   190  	cnt := float64(l.cnt)
   191  	d := cnt*l.sumXX - l.sumX*l.sumX
   192  	if d < 0.001 {
   193  		return l.sumY / cnt, 0
   194  	}
   195  	m = (cnt*l.sumXY - l.sumX*l.sumY) / d
   196  	b = (l.sumY / cnt) - (m * l.sumX / cnt)
   197  	return b, m
   198  }
   199  
   200  func (l *linReg) toBytes() []byte {
   201  	var arr [40]byte
   202  	binary.BigEndian.PutUint64(arr[0:8], math.Float64bits(l.sumX))
   203  	binary.BigEndian.PutUint64(arr[8:16], math.Float64bits(l.sumY))
   204  	binary.BigEndian.PutUint64(arr[16:24], math.Float64bits(l.sumXX))
   205  	binary.BigEndian.PutUint64(arr[24:32], math.Float64bits(l.sumXY))
   206  	binary.BigEndian.PutUint64(arr[32:40], l.cnt)
   207  	return arr[:]
   208  }
   209  
   210  func linRegFromBytes(data []byte) *linReg {
   211  	if len(data) != 40 {
   212  		return nil
   213  	}
   214  	l := &linReg{}
   215  	l.sumX = math.Float64frombits(binary.BigEndian.Uint64(data[0:8]))
   216  	l.sumY = math.Float64frombits(binary.BigEndian.Uint64(data[8:16]))
   217  	l.sumXX = math.Float64frombits(binary.BigEndian.Uint64(data[16:24]))
   218  	l.sumXY = math.Float64frombits(binary.BigEndian.Uint64(data[24:32]))
   219  	l.cnt = binary.BigEndian.Uint64(data[32:40])
   220  	return l
   221  }
   222  
   223  type requestCostStats struct {
   224  	lock  sync.RWMutex
   225  	db    ethdb.Database
   226  	stats map[uint64]*linReg
   227  }
   228  
   229  type requestCostStatsRlp []struct {
   230  	MsgCode uint64
   231  	Data    []byte
   232  }
   233  
   234  var rcStatsKey = []byte("_requestCostStats")
   235  
   236  func newCostStats(db ethdb.Database) *requestCostStats {
   237  	stats := make(map[uint64]*linReg)
   238  	for _, code := range reqList {
   239  		stats[code] = &linReg{cnt: 100}
   240  	}
   241  
   242  	if db != nil {
   243  		data, err := db.Get(rcStatsKey)
   244  		var statsRlp requestCostStatsRlp
   245  		if err == nil {
   246  			err = rlp.DecodeBytes(data, &statsRlp)
   247  		}
   248  		if err == nil {
   249  			for _, r := range statsRlp {
   250  				if stats[r.MsgCode] != nil {
   251  					if l := linRegFromBytes(r.Data); l != nil {
   252  						stats[r.MsgCode] = l
   253  					}
   254  				}
   255  			}
   256  		}
   257  	}
   258  
   259  	return &requestCostStats{
   260  		db:    db,
   261  		stats: stats,
   262  	}
   263  }
   264  
   265  func (s *requestCostStats) store() {
   266  	s.lock.Lock()
   267  	defer s.lock.Unlock()
   268  
   269  	statsRlp := make(requestCostStatsRlp, len(reqList))
   270  	for i, code := range reqList {
   271  		statsRlp[i].MsgCode = code
   272  		statsRlp[i].Data = s.stats[code].toBytes()
   273  	}
   274  
   275  	if data, err := rlp.EncodeToBytes(statsRlp); err == nil {
   276  		s.db.Put(rcStatsKey, data)
   277  	}
   278  }
   279  
   280  func (s *requestCostStats) getCurrentList() RequestCostList {
   281  	s.lock.Lock()
   282  	defer s.lock.Unlock()
   283  
   284  	list := make(RequestCostList, len(reqList))
   285  	//fmt.Println("RequestCostList")
   286  	for idx, code := range reqList {
   287  		b, m := s.stats[code].calc()
   288  		//fmt.Println(code, s.stats[code].cnt, b/1000000, m/1000000)
   289  		if m < 0 {
   290  			b += m
   291  			m = 0
   292  		}
   293  		if b < 0 {
   294  			b = 0
   295  		}
   296  
   297  		list[idx].MsgCode = code
   298  		list[idx].BaseCost = uint64(b * 2)
   299  		list[idx].ReqCost = uint64(m * 2)
   300  	}
   301  	return list
   302  }
   303  
   304  func (s *requestCostStats) update(msgCode, reqCnt, cost uint64) {
   305  	s.lock.Lock()
   306  	defer s.lock.Unlock()
   307  
   308  	c, ok := s.stats[msgCode]
   309  	if !ok || reqCnt == 0 {
   310  		return
   311  	}
   312  	c.add(float64(reqCnt), float64(cost))
   313  }
   314  
   315  func (pm *ProtocolManager) blockLoop() {
   316  	pm.wg.Add(1)
   317  	headCh := make(chan core.ChainHeadEvent, 10)
   318  	headSub := pm.blockchain.SubscribeChainHeadEvent(headCh)
   319  	go func() {
   320  		var lastHead *types.Header
   321  		lastBroadcastTd := common.Big0
   322  		for {
   323  			select {
   324  			case ev := <-headCh:
   325  				peers := pm.peers.AllPeers()
   326  				if len(peers) > 0 {
   327  					header := ev.Block.Header()
   328  					hash := header.Hash()
   329  					number := header.Number.Uint64()
   330  					td := core.GetTd(pm.chainDb, hash, number)
   331  					if td != nil && td.Cmp(lastBroadcastTd) > 0 {
   332  						var reorg uint64
   333  						if lastHead != nil {
   334  							reorg = lastHead.Number.Uint64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64()
   335  						}
   336  						lastHead = header
   337  						lastBroadcastTd = td
   338  
   339  						log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg)
   340  
   341  						announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}
   342  						var (
   343  							signed         bool
   344  							signedAnnounce announceData
   345  						)
   346  
   347  						for _, p := range peers {
   348  							switch p.announceType {
   349  
   350  							case announceTypeSimple:
   351  								select {
   352  								case p.announceChn <- announce:
   353  								default:
   354  									pm.removePeer(p.id)
   355  								}
   356  
   357  							case announceTypeSigned:
   358  								if !signed {
   359  									signedAnnounce = announce
   360  									signedAnnounce.sign(pm.server.privateKey)
   361  									signed = true
   362  								}
   363  
   364  								select {
   365  								case p.announceChn <- signedAnnounce:
   366  								default:
   367  									pm.removePeer(p.id)
   368  								}
   369  							}
   370  						}
   371  					}
   372  				}
   373  			case <-pm.quitSync:
   374  				headSub.Unsubscribe()
   375  				pm.wg.Done()
   376  				return
   377  			}
   378  		}
   379  	}()
   380  }