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