@@ -18,15 +18,292 @@ package les
1818
1919import (
2020 "errors"
21+ "fmt"
22+ "math"
23+ "time"
2124
2225 "github.com/ethereum/go-ethereum/common/hexutil"
26+ "github.com/ethereum/go-ethereum/common/mclock"
27+ "github.com/ethereum/go-ethereum/p2p/enode"
2328)
2429
2530var (
26- errNoCheckpoint = errors .New ("no local checkpoint provided" )
27- errNotActivated = errors .New ("checkpoint registrar is not activated" )
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" )
2836)
2937
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 .getPosBalance (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+ // UpdateBalance 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 ) UpdateBalance (id enode.ID , value int64 , meta string ) (map [string ]uint64 , error ) {
176+ oldBalance , newBalance , err := api .server .clientPool .updateBalance (id , value , meta )
177+ m := make (map [string ]uint64 )
178+ m ["old" ] = oldBalance
179+ m ["new" ] = newBalance
180+ return m , err
181+ }
182+
183+ // SetClientParams sets client parameters for all clients listed in the ids list
184+ // or all connected clients if the list is empty
185+ func (api * PrivateLightServerAPI ) SetClientParams (ids []enode.ID , params map [string ]interface {}) error {
186+ return api .server .clientPool .forClients (ids , func (client * clientInfo , id enode.ID ) error {
187+ if client != nil {
188+ update , err := api .setParams (params , client , nil , nil )
189+ if update {
190+ client .updatePriceFactors ()
191+ }
192+ return err
193+ } else {
194+ return fmt .Errorf ("client %064x is not connected" , id [:])
195+ }
196+ })
197+ }
198+
199+ // SetDefaultParams sets the default parameters applicable to clients connected in the future
200+ func (api * PrivateLightServerAPI ) SetDefaultParams (params map [string ]interface {}) error {
201+ update , err := api .setParams (params , nil , & api .defaultPosFactors , & api .defaultNegFactors )
202+ if update {
203+ api .server .clientPool .setDefaultFactors (api .defaultPosFactors , api .defaultNegFactors )
204+ }
205+ return err
206+ }
207+
208+ // Benchmark runs a request performance benchmark with a given set of measurement setups
209+ // in multiple passes specified by passCount. The measurement time for each setup in each
210+ // pass is specified in milliseconds by length.
211+ //
212+ // Note: measurement time is adjusted for each pass depending on the previous ones.
213+ // Therefore a controlled total measurement time is achievable in multiple passes.
214+ func (api * PrivateLightServerAPI ) Benchmark (setups []map [string ]interface {}, passCount , length int ) ([]map [string ]interface {}, error ) {
215+ benchmarks := make ([]requestBenchmark , len (setups ))
216+ for i , setup := range setups {
217+ if t , ok := setup ["type" ].(string ); ok {
218+ getInt := func (field string , def int ) int {
219+ if value , ok := setup [field ].(float64 ); ok {
220+ return int (value )
221+ }
222+ return def
223+ }
224+ getBool := func (field string , def bool ) bool {
225+ if value , ok := setup [field ].(bool ); ok {
226+ return value
227+ }
228+ return def
229+ }
230+ switch t {
231+ case "header" :
232+ benchmarks [i ] = & benchmarkBlockHeaders {
233+ amount : getInt ("amount" , 1 ),
234+ skip : getInt ("skip" , 1 ),
235+ byHash : getBool ("byHash" , false ),
236+ reverse : getBool ("reverse" , false ),
237+ }
238+ case "body" :
239+ benchmarks [i ] = & benchmarkBodiesOrReceipts {receipts : false }
240+ case "receipts" :
241+ benchmarks [i ] = & benchmarkBodiesOrReceipts {receipts : true }
242+ case "proof" :
243+ benchmarks [i ] = & benchmarkProofsOrCode {code : false }
244+ case "code" :
245+ benchmarks [i ] = & benchmarkProofsOrCode {code : true }
246+ case "cht" :
247+ benchmarks [i ] = & benchmarkHelperTrie {
248+ bloom : false ,
249+ reqCount : getInt ("amount" , 1 ),
250+ }
251+ case "bloom" :
252+ benchmarks [i ] = & benchmarkHelperTrie {
253+ bloom : true ,
254+ reqCount : getInt ("amount" , 1 ),
255+ }
256+ case "txSend" :
257+ benchmarks [i ] = & benchmarkTxSend {}
258+ case "txStatus" :
259+ benchmarks [i ] = & benchmarkTxStatus {}
260+ default :
261+ return nil , errUnknownBenchmarkType
262+ }
263+ } else {
264+ return nil , errUnknownBenchmarkType
265+ }
266+ }
267+ rs := api .server .handler .runBenchmark (benchmarks , passCount , time .Millisecond * time .Duration (length ))
268+ result := make ([]map [string ]interface {}, len (setups ))
269+ for i , r := range rs {
270+ res := make (map [string ]interface {})
271+ if r .err == nil {
272+ res ["totalCount" ] = r .totalCount
273+ res ["avgTime" ] = r .avgTime
274+ res ["maxInSize" ] = r .maxInSize
275+ res ["maxOutSize" ] = r .maxOutSize
276+ } else {
277+ res ["error" ] = r .err .Error ()
278+ }
279+ result [i ] = res
280+ }
281+ return result , nil
282+ }
283+
284+ // PrivateDebugAPI provides an API to debug LES light server functionality.
285+ type PrivateDebugAPI struct {
286+ server * LesServer
287+ }
288+
289+ // NewPrivateDebugAPI creates a new LES light server debug API.
290+ func NewPrivateDebugAPI (server * LesServer ) * PrivateDebugAPI {
291+ return & PrivateDebugAPI {
292+ server : server ,
293+ }
294+ }
295+
296+ // FreezeClient forces a temporary client freeze which normally happens when the server is overloaded
297+ func (api * PrivateDebugAPI ) FreezeClient (id enode.ID ) error {
298+ return api .server .clientPool .forClients ([]enode.ID {id }, func (c * clientInfo , id enode.ID ) error {
299+ if c == nil {
300+ return fmt .Errorf ("client %064x is not connected" , id [:])
301+ }
302+ c .peer .freezeClient ()
303+ return nil
304+ })
305+ }
306+
30307// PrivateLightAPI provides an API to access the LES light server or light client.
31308type PrivateLightAPI struct {
32309 backend * lesCommons
0 commit comments