github.com/zhiqiangxu/go-ethereum@v1.9.16-0.20210824055606-be91cfdebc48/les/api.go (about)

     1  // Copyright 2019 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
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"math"
    23  	"time"
    24  
    25  	"github.com/zhiqiangxu/go-ethereum/common/hexutil"
    26  	"github.com/zhiqiangxu/go-ethereum/common/mclock"
    27  	"github.com/zhiqiangxu/go-ethereum/p2p/enode"
    28  )
    29  
    30  var (
    31  	errNoCheckpoint         = errors.New("no local checkpoint provided")
    32  	errNotActivated         = errors.New("checkpoint registrar is not activated")
    33  	errUnknownBenchmarkType = errors.New("unknown benchmark type")
    34  	errBalanceOverflow      = errors.New("balance overflow")
    35  	errNoPriority           = errors.New("priority too low to raise capacity")
    36  )
    37  
    38  const maxBalance = math.MaxInt64
    39  
    40  // PrivateLightServerAPI provides an API to access the LES light server.
    41  type PrivateLightServerAPI struct {
    42  	server                               *LesServer
    43  	defaultPosFactors, defaultNegFactors priceFactors
    44  }
    45  
    46  // NewPrivateLightServerAPI creates a new LES light server API.
    47  func NewPrivateLightServerAPI(server *LesServer) *PrivateLightServerAPI {
    48  	return &PrivateLightServerAPI{
    49  		server:            server,
    50  		defaultPosFactors: server.clientPool.defaultPosFactors,
    51  		defaultNegFactors: server.clientPool.defaultNegFactors,
    52  	}
    53  }
    54  
    55  // ServerInfo returns global server parameters
    56  func (api *PrivateLightServerAPI) ServerInfo() map[string]interface{} {
    57  	res := make(map[string]interface{})
    58  	res["minimumCapacity"] = api.server.minCapacity
    59  	res["maximumCapacity"] = api.server.maxCapacity
    60  	res["freeClientCapacity"] = api.server.freeCapacity
    61  	res["totalCapacity"], res["totalConnectedCapacity"], res["priorityConnectedCapacity"] = api.server.clientPool.capacityInfo()
    62  	return res
    63  }
    64  
    65  // ClientInfo returns information about clients listed in the ids list or matching the given tags
    66  func (api *PrivateLightServerAPI) ClientInfo(ids []enode.ID) map[enode.ID]map[string]interface{} {
    67  	res := make(map[enode.ID]map[string]interface{})
    68  	api.server.clientPool.forClients(ids, func(client *clientInfo, id enode.ID) error {
    69  		res[id] = api.clientInfo(client, id)
    70  		return nil
    71  	})
    72  	return res
    73  }
    74  
    75  // PriorityClientInfo returns information about clients with a positive balance
    76  // in the given ID range (stop excluded). If stop is null then the iterator stops
    77  // only at the end of the ID space. MaxCount limits the number of results returned.
    78  // If maxCount limit is applied but there are more potential results then the ID
    79  // of the next potential result is included in the map with an empty structure
    80  // assigned to it.
    81  func (api *PrivateLightServerAPI) PriorityClientInfo(start, stop enode.ID, maxCount int) map[enode.ID]map[string]interface{} {
    82  	res := make(map[enode.ID]map[string]interface{})
    83  	ids := api.server.clientPool.ndb.getPosBalanceIDs(start, stop, maxCount+1)
    84  	if len(ids) > maxCount {
    85  		res[ids[maxCount]] = make(map[string]interface{})
    86  		ids = ids[:maxCount]
    87  	}
    88  	if len(ids) != 0 {
    89  		api.server.clientPool.forClients(ids, func(client *clientInfo, id enode.ID) error {
    90  			res[id] = api.clientInfo(client, id)
    91  			return nil
    92  		})
    93  	}
    94  	return res
    95  }
    96  
    97  // clientInfo creates a client info data structure
    98  func (api *PrivateLightServerAPI) clientInfo(c *clientInfo, id enode.ID) map[string]interface{} {
    99  	info := make(map[string]interface{})
   100  	if c != nil {
   101  		now := mclock.Now()
   102  		info["isConnected"] = true
   103  		info["connectionTime"] = float64(now-c.connectedAt) / float64(time.Second)
   104  		info["capacity"] = c.capacity
   105  		pb, nb := c.balanceTracker.getBalance(now)
   106  		info["pricing/balance"], info["pricing/negBalance"] = pb, nb
   107  		info["pricing/balanceMeta"] = c.balanceMetaInfo
   108  		info["priority"] = pb != 0
   109  	} else {
   110  		info["isConnected"] = false
   111  		pb := api.server.clientPool.ndb.getOrNewPB(id)
   112  		info["pricing/balance"], info["pricing/balanceMeta"] = pb.value, pb.meta
   113  		info["priority"] = pb.value != 0
   114  	}
   115  	return info
   116  }
   117  
   118  // setParams either sets the given parameters for a single connected client (if specified)
   119  // or the default parameters applicable to clients connected in the future
   120  func (api *PrivateLightServerAPI) setParams(params map[string]interface{}, client *clientInfo, posFactors, negFactors *priceFactors) (updateFactors bool, err error) {
   121  	defParams := client == nil
   122  	if !defParams {
   123  		posFactors, negFactors = &client.posFactors, &client.negFactors
   124  	}
   125  	for name, value := range params {
   126  		errValue := func() error {
   127  			return fmt.Errorf("invalid value for parameter '%s'", name)
   128  		}
   129  		setFactor := func(v *float64) {
   130  			if val, ok := value.(float64); ok && val >= 0 {
   131  				*v = val / float64(time.Second)
   132  				updateFactors = true
   133  			} else {
   134  				err = errValue()
   135  			}
   136  		}
   137  
   138  		switch {
   139  		case name == "pricing/timeFactor":
   140  			setFactor(&posFactors.timeFactor)
   141  		case name == "pricing/capacityFactor":
   142  			setFactor(&posFactors.capacityFactor)
   143  		case name == "pricing/requestCostFactor":
   144  			setFactor(&posFactors.requestFactor)
   145  		case name == "pricing/negative/timeFactor":
   146  			setFactor(&negFactors.timeFactor)
   147  		case name == "pricing/negative/capacityFactor":
   148  			setFactor(&negFactors.capacityFactor)
   149  		case name == "pricing/negative/requestCostFactor":
   150  			setFactor(&negFactors.requestFactor)
   151  		case !defParams && name == "capacity":
   152  			if capacity, ok := value.(float64); ok && uint64(capacity) >= api.server.minCapacity {
   153  				err = api.server.clientPool.setCapacity(client, uint64(capacity))
   154  				// Don't have to call factor update explicitly. It's already done
   155  				// in setCapacity function.
   156  			} else {
   157  				err = errValue()
   158  			}
   159  		default:
   160  			if defParams {
   161  				err = fmt.Errorf("invalid default parameter '%s'", name)
   162  			} else {
   163  				err = fmt.Errorf("invalid client parameter '%s'", name)
   164  			}
   165  		}
   166  		if err != nil {
   167  			return
   168  		}
   169  	}
   170  	return
   171  }
   172  
   173  // AddBalance updates the balance of a client (either overwrites it or adds to it).
   174  // It also updates the balance meta info string.
   175  func (api *PrivateLightServerAPI) AddBalance(id enode.ID, value int64, meta string) ([2]uint64, error) {
   176  	oldBalance, newBalance, err := api.server.clientPool.addBalance(id, value, meta)
   177  	return [2]uint64{oldBalance, newBalance}, err
   178  }
   179  
   180  // SetClientParams sets client parameters for all clients listed in the ids list
   181  // or all connected clients if the list is empty
   182  func (api *PrivateLightServerAPI) SetClientParams(ids []enode.ID, params map[string]interface{}) error {
   183  	return api.server.clientPool.forClients(ids, func(client *clientInfo, id enode.ID) error {
   184  		if client != nil {
   185  			update, err := api.setParams(params, client, nil, nil)
   186  			if update {
   187  				client.updatePriceFactors()
   188  			}
   189  			return err
   190  		} else {
   191  			return fmt.Errorf("client %064x is not connected", id[:])
   192  		}
   193  	})
   194  }
   195  
   196  // SetDefaultParams sets the default parameters applicable to clients connected in the future
   197  func (api *PrivateLightServerAPI) SetDefaultParams(params map[string]interface{}) error {
   198  	update, err := api.setParams(params, nil, &api.defaultPosFactors, &api.defaultNegFactors)
   199  	if update {
   200  		api.server.clientPool.setDefaultFactors(api.defaultPosFactors, api.defaultNegFactors)
   201  	}
   202  	return err
   203  }
   204  
   205  // Benchmark runs a request performance benchmark with a given set of measurement setups
   206  // in multiple passes specified by passCount. The measurement time for each setup in each
   207  // pass is specified in milliseconds by length.
   208  //
   209  // Note: measurement time is adjusted for each pass depending on the previous ones.
   210  // Therefore a controlled total measurement time is achievable in multiple passes.
   211  func (api *PrivateLightServerAPI) Benchmark(setups []map[string]interface{}, passCount, length int) ([]map[string]interface{}, error) {
   212  	benchmarks := make([]requestBenchmark, len(setups))
   213  	for i, setup := range setups {
   214  		if t, ok := setup["type"].(string); ok {
   215  			getInt := func(field string, def int) int {
   216  				if value, ok := setup[field].(float64); ok {
   217  					return int(value)
   218  				}
   219  				return def
   220  			}
   221  			getBool := func(field string, def bool) bool {
   222  				if value, ok := setup[field].(bool); ok {
   223  					return value
   224  				}
   225  				return def
   226  			}
   227  			switch t {
   228  			case "header":
   229  				benchmarks[i] = &benchmarkBlockHeaders{
   230  					amount:  getInt("amount", 1),
   231  					skip:    getInt("skip", 1),
   232  					byHash:  getBool("byHash", false),
   233  					reverse: getBool("reverse", false),
   234  				}
   235  			case "body":
   236  				benchmarks[i] = &benchmarkBodiesOrReceipts{receipts: false}
   237  			case "receipts":
   238  				benchmarks[i] = &benchmarkBodiesOrReceipts{receipts: true}
   239  			case "proof":
   240  				benchmarks[i] = &benchmarkProofsOrCode{code: false}
   241  			case "code":
   242  				benchmarks[i] = &benchmarkProofsOrCode{code: true}
   243  			case "cht":
   244  				benchmarks[i] = &benchmarkHelperTrie{
   245  					bloom:    false,
   246  					reqCount: getInt("amount", 1),
   247  				}
   248  			case "bloom":
   249  				benchmarks[i] = &benchmarkHelperTrie{
   250  					bloom:    true,
   251  					reqCount: getInt("amount", 1),
   252  				}
   253  			case "txSend":
   254  				benchmarks[i] = &benchmarkTxSend{}
   255  			case "txStatus":
   256  				benchmarks[i] = &benchmarkTxStatus{}
   257  			default:
   258  				return nil, errUnknownBenchmarkType
   259  			}
   260  		} else {
   261  			return nil, errUnknownBenchmarkType
   262  		}
   263  	}
   264  	rs := api.server.handler.runBenchmark(benchmarks, passCount, time.Millisecond*time.Duration(length))
   265  	result := make([]map[string]interface{}, len(setups))
   266  	for i, r := range rs {
   267  		res := make(map[string]interface{})
   268  		if r.err == nil {
   269  			res["totalCount"] = r.totalCount
   270  			res["avgTime"] = r.avgTime
   271  			res["maxInSize"] = r.maxInSize
   272  			res["maxOutSize"] = r.maxOutSize
   273  		} else {
   274  			res["error"] = r.err.Error()
   275  		}
   276  		result[i] = res
   277  	}
   278  	return result, nil
   279  }
   280  
   281  // PrivateDebugAPI provides an API to debug LES light server functionality.
   282  type PrivateDebugAPI struct {
   283  	server *LesServer
   284  }
   285  
   286  // NewPrivateDebugAPI creates a new LES light server debug API.
   287  func NewPrivateDebugAPI(server *LesServer) *PrivateDebugAPI {
   288  	return &PrivateDebugAPI{
   289  		server: server,
   290  	}
   291  }
   292  
   293  // FreezeClient forces a temporary client freeze which normally happens when the server is overloaded
   294  func (api *PrivateDebugAPI) FreezeClient(id enode.ID) error {
   295  	return api.server.clientPool.forClients([]enode.ID{id}, func(c *clientInfo, id enode.ID) error {
   296  		if c == nil {
   297  			return fmt.Errorf("client %064x is not connected", id[:])
   298  		}
   299  		c.peer.freezeClient()
   300  		return nil
   301  	})
   302  }
   303  
   304  // PrivateLightAPI provides an API to access the LES light server or light client.
   305  type PrivateLightAPI struct {
   306  	backend *lesCommons
   307  }
   308  
   309  // NewPrivateLightAPI creates a new LES service API.
   310  func NewPrivateLightAPI(backend *lesCommons) *PrivateLightAPI {
   311  	return &PrivateLightAPI{backend: backend}
   312  }
   313  
   314  // LatestCheckpoint returns the latest local checkpoint package.
   315  //
   316  // The checkpoint package consists of 4 strings:
   317  //   result[0], hex encoded latest section index
   318  //   result[1], 32 bytes hex encoded latest section head hash
   319  //   result[2], 32 bytes hex encoded latest section canonical hash trie root hash
   320  //   result[3], 32 bytes hex encoded latest section bloom trie root hash
   321  func (api *PrivateLightAPI) LatestCheckpoint() ([4]string, error) {
   322  	var res [4]string
   323  	cp := api.backend.latestLocalCheckpoint()
   324  	if cp.Empty() {
   325  		return res, errNoCheckpoint
   326  	}
   327  	res[0] = hexutil.EncodeUint64(cp.SectionIndex)
   328  	res[1], res[2], res[3] = cp.SectionHead.Hex(), cp.CHTRoot.Hex(), cp.BloomRoot.Hex()
   329  	return res, nil
   330  }
   331  
   332  // GetLocalCheckpoint returns the specific local checkpoint package.
   333  //
   334  // The checkpoint package consists of 3 strings:
   335  //   result[0], 32 bytes hex encoded latest section head hash
   336  //   result[1], 32 bytes hex encoded latest section canonical hash trie root hash
   337  //   result[2], 32 bytes hex encoded latest section bloom trie root hash
   338  func (api *PrivateLightAPI) GetCheckpoint(index uint64) ([3]string, error) {
   339  	var res [3]string
   340  	cp := api.backend.localCheckpoint(index)
   341  	if cp.Empty() {
   342  		return res, errNoCheckpoint
   343  	}
   344  	res[0], res[1], res[2] = cp.SectionHead.Hex(), cp.CHTRoot.Hex(), cp.BloomRoot.Hex()
   345  	return res, nil
   346  }
   347  
   348  // GetCheckpointContractAddress returns the contract contract address in hex format.
   349  func (api *PrivateLightAPI) GetCheckpointContractAddress() (string, error) {
   350  	if api.backend.oracle == nil {
   351  		return "", errNotActivated
   352  	}
   353  	return api.backend.oracle.Contract().ContractAddr().Hex(), nil
   354  }