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