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