github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/les/server.go (about)

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