github.com/myafeier/go-ethereum@v1.6.8-0.20170719123245-3e0dbe0eaa72/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  	"encoding/binary"
    22  	"math"
    23  	"sync"
    24  	"time"
    25  
    26  	"github.com/ethereum/go-ethereum/common"
    27  	"github.com/ethereum/go-ethereum/core"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/eth"
    30  	"github.com/ethereum/go-ethereum/ethdb"
    31  	"github.com/ethereum/go-ethereum/les/flowcontrol"
    32  	"github.com/ethereum/go-ethereum/light"
    33  	"github.com/ethereum/go-ethereum/log"
    34  	"github.com/ethereum/go-ethereum/p2p"
    35  	"github.com/ethereum/go-ethereum/p2p/discv5"
    36  	"github.com/ethereum/go-ethereum/rlp"
    37  	"github.com/ethereum/go-ethereum/trie"
    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  	lesTopic        discv5.Topic
    46  	quitSync        chan struct{}
    47  	stopped         bool
    48  }
    49  
    50  func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) {
    51  	quitSync := make(chan struct{})
    52  	pm, err := NewProtocolManager(eth.BlockChain().Config(), false, config.NetworkId, eth.EventMux(), eth.Engine(), newPeerSet(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil, quitSync, new(sync.WaitGroup))
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  	pm.blockLoop()
    57  
    58  	srv := &LesServer{
    59  		protocolManager: pm,
    60  		quitSync:        quitSync,
    61  		lesTopic:        lesTopic(eth.BlockChain().Genesis().Hash()),
    62  	}
    63  	pm.server = srv
    64  
    65  	srv.defParams = &flowcontrol.ServerParams{
    66  		BufLimit:    300000000,
    67  		MinRecharge: 50000,
    68  	}
    69  	srv.fcManager = flowcontrol.NewClientManager(uint64(config.LightServ), 10, 1000000000)
    70  	srv.fcCostStats = newCostStats(eth.ChainDb())
    71  	return srv, nil
    72  }
    73  
    74  func (s *LesServer) Protocols() []p2p.Protocol {
    75  	return s.protocolManager.SubProtocols
    76  }
    77  
    78  // Start starts the LES server
    79  func (s *LesServer) Start(srvr *p2p.Server) {
    80  	s.protocolManager.Start()
    81  	go func() {
    82  		logger := log.New("topic", s.lesTopic)
    83  		logger.Info("Starting topic registration")
    84  		defer logger.Info("Terminated topic registration")
    85  
    86  		srvr.DiscV5.RegisterTopic(s.lesTopic, s.quitSync)
    87  	}()
    88  }
    89  
    90  // Stop stops the LES service
    91  func (s *LesServer) Stop() {
    92  	s.fcCostStats.store()
    93  	s.fcManager.Stop()
    94  	go func() {
    95  		<-s.protocolManager.noMorePeers
    96  	}()
    97  	s.protocolManager.Stop()
    98  }
    99  
   100  type requestCosts struct {
   101  	baseCost, reqCost uint64
   102  }
   103  
   104  type requestCostTable map[uint64]*requestCosts
   105  
   106  type RequestCostList []struct {
   107  	MsgCode, BaseCost, ReqCost uint64
   108  }
   109  
   110  func (list RequestCostList) decode() requestCostTable {
   111  	table := make(requestCostTable)
   112  	for _, e := range list {
   113  		table[e.MsgCode] = &requestCosts{
   114  			baseCost: e.BaseCost,
   115  			reqCost:  e.ReqCost,
   116  		}
   117  	}
   118  	return table
   119  }
   120  
   121  func (table requestCostTable) encode() RequestCostList {
   122  	list := make(RequestCostList, len(table))
   123  	for idx, code := range reqList {
   124  		list[idx].MsgCode = code
   125  		list[idx].BaseCost = table[code].baseCost
   126  		list[idx].ReqCost = table[code].reqCost
   127  	}
   128  	return list
   129  }
   130  
   131  type linReg struct {
   132  	sumX, sumY, sumXX, sumXY float64
   133  	cnt                      uint64
   134  }
   135  
   136  const linRegMaxCnt = 100000
   137  
   138  func (l *linReg) add(x, y float64) {
   139  	if l.cnt >= linRegMaxCnt {
   140  		sub := float64(l.cnt+1-linRegMaxCnt) / linRegMaxCnt
   141  		l.sumX -= l.sumX * sub
   142  		l.sumY -= l.sumY * sub
   143  		l.sumXX -= l.sumXX * sub
   144  		l.sumXY -= l.sumXY * sub
   145  		l.cnt = linRegMaxCnt - 1
   146  	}
   147  	l.cnt++
   148  	l.sumX += x
   149  	l.sumY += y
   150  	l.sumXX += x * x
   151  	l.sumXY += x * y
   152  }
   153  
   154  func (l *linReg) calc() (b, m float64) {
   155  	if l.cnt == 0 {
   156  		return 0, 0
   157  	}
   158  	cnt := float64(l.cnt)
   159  	d := cnt*l.sumXX - l.sumX*l.sumX
   160  	if d < 0.001 {
   161  		return l.sumY / cnt, 0
   162  	}
   163  	m = (cnt*l.sumXY - l.sumX*l.sumY) / d
   164  	b = (l.sumY / cnt) - (m * l.sumX / cnt)
   165  	return b, m
   166  }
   167  
   168  func (l *linReg) toBytes() []byte {
   169  	var arr [40]byte
   170  	binary.BigEndian.PutUint64(arr[0:8], math.Float64bits(l.sumX))
   171  	binary.BigEndian.PutUint64(arr[8:16], math.Float64bits(l.sumY))
   172  	binary.BigEndian.PutUint64(arr[16:24], math.Float64bits(l.sumXX))
   173  	binary.BigEndian.PutUint64(arr[24:32], math.Float64bits(l.sumXY))
   174  	binary.BigEndian.PutUint64(arr[32:40], l.cnt)
   175  	return arr[:]
   176  }
   177  
   178  func linRegFromBytes(data []byte) *linReg {
   179  	if len(data) != 40 {
   180  		return nil
   181  	}
   182  	l := &linReg{}
   183  	l.sumX = math.Float64frombits(binary.BigEndian.Uint64(data[0:8]))
   184  	l.sumY = math.Float64frombits(binary.BigEndian.Uint64(data[8:16]))
   185  	l.sumXX = math.Float64frombits(binary.BigEndian.Uint64(data[16:24]))
   186  	l.sumXY = math.Float64frombits(binary.BigEndian.Uint64(data[24:32]))
   187  	l.cnt = binary.BigEndian.Uint64(data[32:40])
   188  	return l
   189  }
   190  
   191  type requestCostStats struct {
   192  	lock  sync.RWMutex
   193  	db    ethdb.Database
   194  	stats map[uint64]*linReg
   195  }
   196  
   197  type requestCostStatsRlp []struct {
   198  	MsgCode uint64
   199  	Data    []byte
   200  }
   201  
   202  var rcStatsKey = []byte("_requestCostStats")
   203  
   204  func newCostStats(db ethdb.Database) *requestCostStats {
   205  	stats := make(map[uint64]*linReg)
   206  	for _, code := range reqList {
   207  		stats[code] = &linReg{cnt: 100}
   208  	}
   209  
   210  	if db != nil {
   211  		data, err := db.Get(rcStatsKey)
   212  		var statsRlp requestCostStatsRlp
   213  		if err == nil {
   214  			err = rlp.DecodeBytes(data, &statsRlp)
   215  		}
   216  		if err == nil {
   217  			for _, r := range statsRlp {
   218  				if stats[r.MsgCode] != nil {
   219  					if l := linRegFromBytes(r.Data); l != nil {
   220  						stats[r.MsgCode] = l
   221  					}
   222  				}
   223  			}
   224  		}
   225  	}
   226  
   227  	return &requestCostStats{
   228  		db:    db,
   229  		stats: stats,
   230  	}
   231  }
   232  
   233  func (s *requestCostStats) store() {
   234  	s.lock.Lock()
   235  	defer s.lock.Unlock()
   236  
   237  	statsRlp := make(requestCostStatsRlp, len(reqList))
   238  	for i, code := range reqList {
   239  		statsRlp[i].MsgCode = code
   240  		statsRlp[i].Data = s.stats[code].toBytes()
   241  	}
   242  
   243  	if data, err := rlp.EncodeToBytes(statsRlp); err == nil {
   244  		s.db.Put(rcStatsKey, data)
   245  	}
   246  }
   247  
   248  func (s *requestCostStats) getCurrentList() RequestCostList {
   249  	s.lock.Lock()
   250  	defer s.lock.Unlock()
   251  
   252  	list := make(RequestCostList, len(reqList))
   253  	//fmt.Println("RequestCostList")
   254  	for idx, code := range reqList {
   255  		b, m := s.stats[code].calc()
   256  		//fmt.Println(code, s.stats[code].cnt, b/1000000, m/1000000)
   257  		if m < 0 {
   258  			b += m
   259  			m = 0
   260  		}
   261  		if b < 0 {
   262  			b = 0
   263  		}
   264  
   265  		list[idx].MsgCode = code
   266  		list[idx].BaseCost = uint64(b * 2)
   267  		list[idx].ReqCost = uint64(m * 2)
   268  	}
   269  	return list
   270  }
   271  
   272  func (s *requestCostStats) update(msgCode, reqCnt, cost uint64) {
   273  	s.lock.Lock()
   274  	defer s.lock.Unlock()
   275  
   276  	c, ok := s.stats[msgCode]
   277  	if !ok || reqCnt == 0 {
   278  		return
   279  	}
   280  	c.add(float64(reqCnt), float64(cost))
   281  }
   282  
   283  func (pm *ProtocolManager) blockLoop() {
   284  	pm.wg.Add(1)
   285  	sub := pm.eventMux.Subscribe(core.ChainHeadEvent{})
   286  	newCht := make(chan struct{}, 10)
   287  	newCht <- struct{}{}
   288  	go func() {
   289  		var mu sync.Mutex
   290  		var lastHead *types.Header
   291  		lastBroadcastTd := common.Big0
   292  		for {
   293  			select {
   294  			case ev := <-sub.Chan():
   295  				peers := pm.peers.AllPeers()
   296  				if len(peers) > 0 {
   297  					header := ev.Data.(core.ChainHeadEvent).Block.Header()
   298  					hash := header.Hash()
   299  					number := header.Number.Uint64()
   300  					td := core.GetTd(pm.chainDb, hash, number)
   301  					if td != nil && td.Cmp(lastBroadcastTd) > 0 {
   302  						var reorg uint64
   303  						if lastHead != nil {
   304  							reorg = lastHead.Number.Uint64() - core.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64()
   305  						}
   306  						lastHead = header
   307  						lastBroadcastTd = td
   308  
   309  						log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg)
   310  
   311  						announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg}
   312  						for _, p := range peers {
   313  							select {
   314  							case p.announceChn <- announce:
   315  							default:
   316  								pm.removePeer(p.id)
   317  							}
   318  						}
   319  					}
   320  				}
   321  				newCht <- struct{}{}
   322  			case <-newCht:
   323  				go func() {
   324  					mu.Lock()
   325  					more := makeCht(pm.chainDb)
   326  					mu.Unlock()
   327  					if more {
   328  						time.Sleep(time.Millisecond * 10)
   329  						newCht <- struct{}{}
   330  					}
   331  				}()
   332  			case <-pm.quitSync:
   333  				sub.Unsubscribe()
   334  				pm.wg.Done()
   335  				return
   336  			}
   337  		}
   338  	}()
   339  }
   340  
   341  var (
   342  	lastChtKey = []byte("LastChtNumber") // chtNum (uint64 big endian)
   343  	chtPrefix  = []byte("cht")           // chtPrefix + chtNum (uint64 big endian) -> trie root hash
   344  )
   345  
   346  func getChtRoot(db ethdb.Database, num uint64) common.Hash {
   347  	var encNumber [8]byte
   348  	binary.BigEndian.PutUint64(encNumber[:], num)
   349  	data, _ := db.Get(append(chtPrefix, encNumber[:]...))
   350  	return common.BytesToHash(data)
   351  }
   352  
   353  func storeChtRoot(db ethdb.Database, num uint64, root common.Hash) {
   354  	var encNumber [8]byte
   355  	binary.BigEndian.PutUint64(encNumber[:], num)
   356  	db.Put(append(chtPrefix, encNumber[:]...), root[:])
   357  }
   358  
   359  func makeCht(db ethdb.Database) bool {
   360  	headHash := core.GetHeadBlockHash(db)
   361  	headNum := core.GetBlockNumber(db, headHash)
   362  
   363  	var newChtNum uint64
   364  	if headNum > light.ChtConfirmations {
   365  		newChtNum = (headNum - light.ChtConfirmations) / light.ChtFrequency
   366  	}
   367  
   368  	var lastChtNum uint64
   369  	data, _ := db.Get(lastChtKey)
   370  	if len(data) == 8 {
   371  		lastChtNum = binary.BigEndian.Uint64(data[:])
   372  	}
   373  	if newChtNum <= lastChtNum {
   374  		return false
   375  	}
   376  
   377  	var t *trie.Trie
   378  	if lastChtNum > 0 {
   379  		var err error
   380  		t, err = trie.New(getChtRoot(db, lastChtNum), db)
   381  		if err != nil {
   382  			lastChtNum = 0
   383  		}
   384  	}
   385  	if lastChtNum == 0 {
   386  		t, _ = trie.New(common.Hash{}, db)
   387  	}
   388  
   389  	for num := lastChtNum * light.ChtFrequency; num < (lastChtNum+1)*light.ChtFrequency; num++ {
   390  		hash := core.GetCanonicalHash(db, num)
   391  		if hash == (common.Hash{}) {
   392  			panic("Canonical hash not found")
   393  		}
   394  		td := core.GetTd(db, hash, num)
   395  		if td == nil {
   396  			panic("TD not found")
   397  		}
   398  		var encNumber [8]byte
   399  		binary.BigEndian.PutUint64(encNumber[:], num)
   400  		var node light.ChtNode
   401  		node.Hash = hash
   402  		node.Td = td
   403  		data, _ := rlp.EncodeToBytes(node)
   404  		t.Update(encNumber[:], data)
   405  	}
   406  
   407  	root, err := t.Commit()
   408  	if err != nil {
   409  		lastChtNum = 0
   410  	} else {
   411  		lastChtNum++
   412  
   413  		log.Trace("Generated CHT", "number", lastChtNum, "root", root.Hex())
   414  
   415  		storeChtRoot(db, lastChtNum, root)
   416  		var data [8]byte
   417  		binary.BigEndian.PutUint64(data[:], lastChtNum)
   418  		db.Put(lastChtKey, data[:])
   419  	}
   420  
   421  	return newChtNum > lastChtNum
   422  }