2019-09-05 17:21:01 +04:00
|
|
|
(*****************************************************************************)
|
|
|
|
(* *)
|
|
|
|
(* Open Source License *)
|
|
|
|
(* Copyright (c) 2018 Dynamic Ledger Solutions, Inc. <contact@tezos.com> *)
|
|
|
|
(* *)
|
|
|
|
(* Permission is hereby granted, free of charge, to any person obtaining a *)
|
|
|
|
(* copy of this software and associated documentation files (the "Software"),*)
|
|
|
|
(* to deal in the Software without restriction, including without limitation *)
|
|
|
|
(* the rights to use, copy, modify, merge, publish, distribute, sublicense, *)
|
|
|
|
(* and/or sell copies of the Software, and to permit persons to whom the *)
|
|
|
|
(* Software is furnished to do so, subject to the following conditions: *)
|
|
|
|
(* *)
|
|
|
|
(* The above copyright notice and this permission notice shall be included *)
|
|
|
|
(* in all copies or substantial portions of the Software. *)
|
|
|
|
(* *)
|
|
|
|
(* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*)
|
|
|
|
(* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *)
|
|
|
|
(* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *)
|
|
|
|
(* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*)
|
|
|
|
(* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *)
|
|
|
|
(* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *)
|
|
|
|
(* DEALINGS IN THE SOFTWARE. *)
|
|
|
|
(* *)
|
|
|
|
(*****************************************************************************)
|
|
|
|
|
|
|
|
open Alpha_context
|
|
|
|
|
|
|
|
type info = {
|
2020-02-12 20:40:17 +04:00
|
|
|
balance : Tez.t;
|
|
|
|
frozen_balance : Tez.t;
|
|
|
|
frozen_balance_by_cycle : Delegate.frozen_balance Cycle.Map.t;
|
|
|
|
staking_balance : Tez.t;
|
|
|
|
delegated_contracts : Contract_repr.t list;
|
|
|
|
delegated_balance : Tez.t;
|
|
|
|
deactivated : bool;
|
|
|
|
grace_period : Cycle.t;
|
2019-09-05 17:21:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
let info_encoding =
|
|
|
|
let open Data_encoding in
|
|
|
|
conv
|
2020-02-12 20:40:17 +04:00
|
|
|
(fun { balance;
|
|
|
|
frozen_balance;
|
|
|
|
frozen_balance_by_cycle;
|
|
|
|
staking_balance;
|
|
|
|
delegated_contracts;
|
|
|
|
delegated_balance;
|
|
|
|
deactivated;
|
|
|
|
grace_period } ->
|
|
|
|
( balance,
|
|
|
|
frozen_balance,
|
|
|
|
frozen_balance_by_cycle,
|
|
|
|
staking_balance,
|
|
|
|
delegated_contracts,
|
|
|
|
delegated_balance,
|
|
|
|
deactivated,
|
|
|
|
grace_period ))
|
|
|
|
(fun ( balance,
|
|
|
|
frozen_balance,
|
|
|
|
frozen_balance_by_cycle,
|
|
|
|
staking_balance,
|
|
|
|
delegated_contracts,
|
|
|
|
delegated_balance,
|
|
|
|
deactivated,
|
|
|
|
grace_period ) ->
|
|
|
|
{
|
|
|
|
balance;
|
|
|
|
frozen_balance;
|
|
|
|
frozen_balance_by_cycle;
|
|
|
|
staking_balance;
|
|
|
|
delegated_contracts;
|
|
|
|
delegated_balance;
|
|
|
|
deactivated;
|
|
|
|
grace_period;
|
|
|
|
})
|
2019-09-05 17:21:01 +04:00
|
|
|
(obj8
|
|
|
|
(req "balance" Tez.encoding)
|
|
|
|
(req "frozen_balance" Tez.encoding)
|
|
|
|
(req "frozen_balance_by_cycle" Delegate.frozen_balance_by_cycle_encoding)
|
|
|
|
(req "staking_balance" Tez.encoding)
|
2019-10-17 13:45:27 +04:00
|
|
|
(req "delegated_contracts" (list Contract_repr.encoding))
|
2019-09-05 17:21:01 +04:00
|
|
|
(req "delegated_balance" Tez.encoding)
|
|
|
|
(req "deactivated" bool)
|
|
|
|
(req "grace_period" Cycle.encoding))
|
|
|
|
|
|
|
|
module S = struct
|
|
|
|
let path = RPC_path.(open_root / "context" / "delegates")
|
|
|
|
|
|
|
|
open Data_encoding
|
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
type list_query = {active : bool; inactive : bool}
|
|
|
|
|
|
|
|
let list_query : list_query RPC_query.t =
|
2019-09-05 17:21:01 +04:00
|
|
|
let open RPC_query in
|
2020-02-12 20:40:17 +04:00
|
|
|
query (fun active inactive -> {active; inactive})
|
2019-09-05 17:21:01 +04:00
|
|
|
|+ flag "active" (fun t -> t.active)
|
|
|
|
|+ flag "inactive" (fun t -> t.inactive)
|
|
|
|
|> seal
|
|
|
|
|
|
|
|
let list_delegate =
|
|
|
|
RPC_service.get_service
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:"Lists all registered delegates."
|
|
|
|
~query:list_query
|
|
|
|
~output:(list Signature.Public_key_hash.encoding)
|
2019-09-05 17:21:01 +04:00
|
|
|
path
|
|
|
|
|
|
|
|
let path = RPC_path.(path /: Signature.Public_key_hash.rpc_arg)
|
|
|
|
|
|
|
|
let info =
|
|
|
|
RPC_service.get_service
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:"Everything about a delegate."
|
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:info_encoding
|
2019-09-05 17:21:01 +04:00
|
|
|
path
|
|
|
|
|
|
|
|
let balance =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
2020-02-12 20:40:17 +04:00
|
|
|
"Returns the full balance of a given delegate, including the frozen \
|
|
|
|
balances."
|
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:Tez.encoding
|
2019-09-05 17:21:01 +04:00
|
|
|
RPC_path.(path / "balance")
|
|
|
|
|
|
|
|
let frozen_balance =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
2020-02-12 20:40:17 +04:00
|
|
|
"Returns the total frozen balances of a given delegate, this includes \
|
|
|
|
the frozen deposits, rewards and fees."
|
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:Tez.encoding
|
2019-09-05 17:21:01 +04:00
|
|
|
RPC_path.(path / "frozen_balance")
|
|
|
|
|
|
|
|
let frozen_balance_by_cycle =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
2020-02-12 20:40:17 +04:00
|
|
|
"Returns the frozen balances of a given delegate, indexed by the \
|
|
|
|
cycle by which it will be unfrozen"
|
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:Delegate.frozen_balance_by_cycle_encoding
|
2019-09-05 17:21:01 +04:00
|
|
|
RPC_path.(path / "frozen_balance_by_cycle")
|
|
|
|
|
|
|
|
let staking_balance =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
|
|
|
"Returns the total amount of tokens delegated to a given delegate. \
|
2020-02-12 20:40:17 +04:00
|
|
|
This includes the balances of all the contracts that delegate to it, \
|
|
|
|
but also the balance of the delegate itself and its frozen fees and \
|
|
|
|
deposits. The rewards do not count in the delegated balance until \
|
|
|
|
they are unfrozen."
|
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:Tez.encoding
|
2019-09-05 17:21:01 +04:00
|
|
|
RPC_path.(path / "staking_balance")
|
|
|
|
|
|
|
|
let delegated_contracts =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
|
|
|
"Returns the list of contracts that delegate to a given delegate."
|
2020-02-12 20:40:17 +04:00
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:(list Contract_repr.encoding)
|
2019-09-05 17:21:01 +04:00
|
|
|
RPC_path.(path / "delegated_contracts")
|
|
|
|
|
|
|
|
let delegated_balance =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
2020-02-12 20:40:17 +04:00
|
|
|
"Returns the balances of all the contracts that delegate to a given \
|
|
|
|
delegate. This excludes the delegate's own balance and its frozen \
|
|
|
|
balances."
|
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:Tez.encoding
|
2019-09-05 17:21:01 +04:00
|
|
|
RPC_path.(path / "delegated_balance")
|
|
|
|
|
|
|
|
let deactivated =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
|
|
|
"Tells whether the delegate is currently tagged as deactivated or not."
|
2020-02-12 20:40:17 +04:00
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:bool
|
2019-09-05 17:21:01 +04:00
|
|
|
RPC_path.(path / "deactivated")
|
|
|
|
|
|
|
|
let grace_period =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
|
|
|
"Returns the cycle by the end of which the delegate might be \
|
2020-02-12 20:40:17 +04:00
|
|
|
deactivated if she fails to execute any delegate action. A \
|
|
|
|
deactivated delegate might be reactivated (without loosing any \
|
|
|
|
rolls) by simply re-registering as a delegate. For deactivated \
|
|
|
|
delegates, this value contains the cycle by which they were \
|
|
|
|
deactivated."
|
|
|
|
~query:RPC_query.empty
|
|
|
|
~output:Cycle.encoding
|
2019-09-05 17:21:01 +04:00
|
|
|
RPC_path.(path / "grace_period")
|
|
|
|
end
|
|
|
|
|
|
|
|
let register () =
|
|
|
|
let open Services_registration in
|
2020-02-12 20:40:17 +04:00
|
|
|
register0 S.list_delegate (fun ctxt q () ->
|
|
|
|
Delegate.list ctxt
|
|
|
|
>>= fun delegates ->
|
|
|
|
if q.active && q.inactive then return delegates
|
|
|
|
else if q.active then
|
|
|
|
filter_map_s
|
|
|
|
(fun pkh ->
|
|
|
|
Delegate.deactivated ctxt pkh
|
|
|
|
>>=? function true -> return_none | false -> return_some pkh)
|
|
|
|
delegates
|
|
|
|
else if q.inactive then
|
|
|
|
filter_map_s
|
|
|
|
(fun pkh ->
|
|
|
|
Delegate.deactivated ctxt pkh
|
|
|
|
>>=? function false -> return_none | true -> return_some pkh)
|
|
|
|
delegates
|
|
|
|
else return_nil) ;
|
|
|
|
register1 S.info (fun ctxt pkh () () ->
|
|
|
|
Delegate.full_balance ctxt pkh
|
|
|
|
>>=? fun balance ->
|
|
|
|
Delegate.frozen_balance ctxt pkh
|
|
|
|
>>=? fun frozen_balance ->
|
|
|
|
Delegate.frozen_balance_by_cycle ctxt pkh
|
|
|
|
>>= fun frozen_balance_by_cycle ->
|
|
|
|
Delegate.staking_balance ctxt pkh
|
|
|
|
>>=? fun staking_balance ->
|
|
|
|
Delegate.delegated_contracts ctxt pkh
|
|
|
|
>>= fun delegated_contracts ->
|
|
|
|
Delegate.delegated_balance ctxt pkh
|
|
|
|
>>=? fun delegated_balance ->
|
|
|
|
Delegate.deactivated ctxt pkh
|
|
|
|
>>=? fun deactivated ->
|
|
|
|
Delegate.grace_period ctxt pkh
|
|
|
|
>>=? fun grace_period ->
|
|
|
|
return
|
|
|
|
{
|
|
|
|
balance;
|
|
|
|
frozen_balance;
|
|
|
|
frozen_balance_by_cycle;
|
|
|
|
staking_balance;
|
|
|
|
delegated_contracts;
|
|
|
|
delegated_balance;
|
|
|
|
deactivated;
|
|
|
|
grace_period;
|
|
|
|
}) ;
|
|
|
|
register1 S.balance (fun ctxt pkh () () -> Delegate.full_balance ctxt pkh) ;
|
|
|
|
register1 S.frozen_balance (fun ctxt pkh () () ->
|
|
|
|
Delegate.frozen_balance ctxt pkh) ;
|
|
|
|
register1 S.frozen_balance_by_cycle (fun ctxt pkh () () ->
|
|
|
|
Delegate.frozen_balance_by_cycle ctxt pkh >>= return) ;
|
|
|
|
register1 S.staking_balance (fun ctxt pkh () () ->
|
|
|
|
Delegate.staking_balance ctxt pkh) ;
|
|
|
|
register1 S.delegated_contracts (fun ctxt pkh () () ->
|
|
|
|
Delegate.delegated_contracts ctxt pkh >>= return) ;
|
|
|
|
register1 S.delegated_balance (fun ctxt pkh () () ->
|
|
|
|
Delegate.delegated_balance ctxt pkh) ;
|
|
|
|
register1 S.deactivated (fun ctxt pkh () () -> Delegate.deactivated ctxt pkh) ;
|
|
|
|
register1 S.grace_period (fun ctxt pkh () () ->
|
|
|
|
Delegate.grace_period ctxt pkh)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let list ctxt block ?(active = true) ?(inactive = false) () =
|
2020-02-12 20:40:17 +04:00
|
|
|
RPC_context.make_call0 S.list_delegate ctxt block {active; inactive} ()
|
2019-09-05 17:21:01 +04:00
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let info ctxt block pkh = RPC_context.make_call1 S.info ctxt block pkh () ()
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let balance ctxt block pkh =
|
|
|
|
RPC_context.make_call1 S.balance ctxt block pkh () ()
|
|
|
|
|
|
|
|
let frozen_balance ctxt block pkh =
|
|
|
|
RPC_context.make_call1 S.frozen_balance ctxt block pkh () ()
|
|
|
|
|
|
|
|
let frozen_balance_by_cycle ctxt block pkh =
|
|
|
|
RPC_context.make_call1 S.frozen_balance_by_cycle ctxt block pkh () ()
|
|
|
|
|
|
|
|
let staking_balance ctxt block pkh =
|
|
|
|
RPC_context.make_call1 S.staking_balance ctxt block pkh () ()
|
|
|
|
|
|
|
|
let delegated_contracts ctxt block pkh =
|
|
|
|
RPC_context.make_call1 S.delegated_contracts ctxt block pkh () ()
|
|
|
|
|
|
|
|
let delegated_balance ctxt block pkh =
|
|
|
|
RPC_context.make_call1 S.delegated_balance ctxt block pkh () ()
|
|
|
|
|
|
|
|
let deactivated ctxt block pkh =
|
|
|
|
RPC_context.make_call1 S.deactivated ctxt block pkh () ()
|
|
|
|
|
|
|
|
let grace_period ctxt block pkh =
|
|
|
|
RPC_context.make_call1 S.grace_period ctxt block pkh () ()
|
|
|
|
|
|
|
|
let requested_levels ~default ctxt cycles levels =
|
2020-02-12 20:40:17 +04:00
|
|
|
match (levels, cycles) with
|
|
|
|
| ([], []) ->
|
2019-09-05 17:21:01 +04:00
|
|
|
return [default]
|
2020-02-12 20:40:17 +04:00
|
|
|
| (levels, cycles) ->
|
2019-09-05 17:21:01 +04:00
|
|
|
(* explicitly fail when requested levels or cycle are in the past...
|
|
|
|
or too far in the future... *)
|
|
|
|
let levels =
|
|
|
|
List.sort_uniq
|
|
|
|
Level.compare
|
2020-02-12 20:40:17 +04:00
|
|
|
(List.concat
|
|
|
|
( List.map (Level.from_raw ctxt) levels
|
|
|
|
:: List.map (Level.levels_in_cycle ctxt) cycles ))
|
|
|
|
in
|
2019-10-17 13:45:27 +04:00
|
|
|
map_s
|
2019-09-05 17:21:01 +04:00
|
|
|
(fun level ->
|
2020-02-12 20:40:17 +04:00
|
|
|
let current_level = Level.current ctxt in
|
|
|
|
if Level.(level <= current_level) then return (level, None)
|
|
|
|
else
|
|
|
|
Baking.earlier_predecessor_timestamp ctxt level
|
|
|
|
>>=? fun timestamp -> return (level, Some timestamp))
|
2019-09-05 17:21:01 +04:00
|
|
|
levels
|
|
|
|
|
|
|
|
module Baking_rights = struct
|
|
|
|
type t = {
|
2020-02-12 20:40:17 +04:00
|
|
|
level : Raw_level.t;
|
|
|
|
delegate : Signature.Public_key_hash.t;
|
|
|
|
priority : int;
|
|
|
|
timestamp : Timestamp.t option;
|
2019-09-05 17:21:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
let encoding =
|
|
|
|
let open Data_encoding in
|
|
|
|
conv
|
2020-02-12 20:40:17 +04:00
|
|
|
(fun {level; delegate; priority; timestamp} ->
|
|
|
|
(level, delegate, priority, timestamp))
|
2019-09-05 17:21:01 +04:00
|
|
|
(fun (level, delegate, priority, timestamp) ->
|
2020-02-12 20:40:17 +04:00
|
|
|
{level; delegate; priority; timestamp})
|
2019-09-05 17:21:01 +04:00
|
|
|
(obj4
|
|
|
|
(req "level" Raw_level.encoding)
|
|
|
|
(req "delegate" Signature.Public_key_hash.encoding)
|
|
|
|
(req "priority" uint16)
|
|
|
|
(opt "estimated_time" Timestamp.encoding))
|
|
|
|
|
|
|
|
module S = struct
|
|
|
|
open Data_encoding
|
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let custom_root = RPC_path.(open_root / "helpers" / "baking_rights")
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
type baking_rights_query = {
|
2020-02-12 20:40:17 +04:00
|
|
|
levels : Raw_level.t list;
|
|
|
|
cycles : Cycle.t list;
|
|
|
|
delegates : Signature.Public_key_hash.t list;
|
|
|
|
max_priority : int option;
|
|
|
|
all : bool;
|
2019-09-05 17:21:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
let baking_rights_query =
|
|
|
|
let open RPC_query in
|
|
|
|
query (fun levels cycles delegates max_priority all ->
|
2020-02-12 20:40:17 +04:00
|
|
|
{levels; cycles; delegates; max_priority; all})
|
2019-09-05 17:21:01 +04:00
|
|
|
|+ multi_field "level" Raw_level.rpc_arg (fun t -> t.levels)
|
|
|
|
|+ multi_field "cycle" Cycle.rpc_arg (fun t -> t.cycles)
|
2020-02-12 20:40:17 +04:00
|
|
|
|+ multi_field "delegate" Signature.Public_key_hash.rpc_arg (fun t ->
|
|
|
|
t.delegates)
|
2019-09-05 17:21:01 +04:00
|
|
|
|+ opt_field "max_priority" RPC_arg.int (fun t -> t.max_priority)
|
|
|
|
|+ flag "all" (fun t -> t.all)
|
|
|
|
|> seal
|
|
|
|
|
|
|
|
let baking_rights =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
|
|
|
"Retrieves the list of delegates allowed to bake a block.\n\
|
2020-02-12 20:40:17 +04:00
|
|
|
By default, it gives the best baking priorities for bakers that \
|
|
|
|
have at least one opportunity below the 64th priority for the next \
|
|
|
|
block.\n\
|
|
|
|
Parameters `level` and `cycle` can be used to specify the (valid) \
|
|
|
|
level(s) in the past or future at which the baking rights have to \
|
|
|
|
be returned. Parameter `delegate` can be used to restrict the \
|
|
|
|
results to the given delegates. If parameter `all` is set, all the \
|
|
|
|
baking opportunities for each baker at each level are returned, \
|
|
|
|
instead of just the first one.\n\
|
2019-09-05 17:21:01 +04:00
|
|
|
Returns the list of baking slots. Also returns the minimal \
|
2020-02-12 20:40:17 +04:00
|
|
|
timestamps that correspond to these slots. The timestamps are \
|
|
|
|
omitted for levels in the past, and are only estimates for levels \
|
|
|
|
later that the next block, based on the hypothesis that all \
|
|
|
|
predecessor blocks were baked at the first priority."
|
|
|
|
~query:baking_rights_query
|
|
|
|
~output:(list encoding)
|
2019-09-05 17:21:01 +04:00
|
|
|
custom_root
|
|
|
|
end
|
|
|
|
|
|
|
|
let baking_priorities ctxt max_prio (level, pred_timestamp) =
|
2020-02-12 20:40:17 +04:00
|
|
|
Baking.baking_priorities ctxt level
|
|
|
|
>>=? fun contract_list ->
|
2019-09-05 17:21:01 +04:00
|
|
|
let rec loop l acc priority =
|
2020-02-12 20:40:17 +04:00
|
|
|
if Compare.Int.(priority > max_prio) then return (List.rev acc)
|
2019-09-05 17:21:01 +04:00
|
|
|
else
|
2020-02-12 20:40:17 +04:00
|
|
|
let (Misc.LCons (pk, next)) = l in
|
2019-09-05 17:21:01 +04:00
|
|
|
let delegate = Signature.Public_key.hash pk in
|
2020-02-12 20:40:17 +04:00
|
|
|
( match pred_timestamp with
|
|
|
|
| None ->
|
|
|
|
return_none
|
|
|
|
| Some pred_timestamp ->
|
|
|
|
Baking.minimal_time ctxt priority pred_timestamp
|
|
|
|
>>=? fun t -> return_some t )
|
|
|
|
>>=? fun timestamp ->
|
2019-09-05 17:21:01 +04:00
|
|
|
let acc =
|
2020-02-12 20:40:17 +04:00
|
|
|
{level = level.level; delegate; priority; timestamp} :: acc
|
|
|
|
in
|
|
|
|
next () >>=? fun l -> loop l acc (priority + 1)
|
|
|
|
in
|
2019-09-05 17:21:01 +04:00
|
|
|
loop contract_list [] 0
|
|
|
|
|
|
|
|
let remove_duplicated_delegates rights =
|
2020-02-12 20:40:17 +04:00
|
|
|
List.rev @@ fst
|
|
|
|
@@ List.fold_left
|
|
|
|
(fun (acc, previous) r ->
|
|
|
|
if Signature.Public_key_hash.Set.mem r.delegate previous then
|
|
|
|
(acc, previous)
|
|
|
|
else
|
|
|
|
(r :: acc, Signature.Public_key_hash.Set.add r.delegate previous))
|
|
|
|
([], Signature.Public_key_hash.Set.empty)
|
|
|
|
rights
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let register () =
|
|
|
|
let open Services_registration in
|
2020-02-12 20:40:17 +04:00
|
|
|
register0 S.baking_rights (fun ctxt q () ->
|
|
|
|
requested_levels
|
|
|
|
~default:
|
|
|
|
( Level.succ ctxt (Level.current ctxt),
|
|
|
|
Some (Timestamp.current ctxt) )
|
|
|
|
ctxt
|
|
|
|
q.cycles
|
|
|
|
q.levels
|
|
|
|
>>=? fun levels ->
|
|
|
|
let max_priority =
|
|
|
|
match q.max_priority with None -> 64 | Some max -> max
|
|
|
|
in
|
|
|
|
map_s (baking_priorities ctxt max_priority) levels
|
|
|
|
>>=? fun rights ->
|
|
|
|
let rights =
|
|
|
|
if q.all then rights else List.map remove_duplicated_delegates rights
|
|
|
|
in
|
|
|
|
let rights = List.concat rights in
|
|
|
|
match q.delegates with
|
|
|
|
| [] ->
|
|
|
|
return rights
|
|
|
|
| _ :: _ as delegates ->
|
|
|
|
let is_requested p =
|
|
|
|
List.exists
|
|
|
|
(Signature.Public_key_hash.equal p.delegate)
|
|
|
|
delegates
|
|
|
|
in
|
|
|
|
return (List.filter is_requested rights))
|
|
|
|
|
|
|
|
let get ctxt ?(levels = []) ?(cycles = []) ?(delegates = []) ?(all = false)
|
2019-09-05 17:21:01 +04:00
|
|
|
?max_priority block =
|
2020-02-12 20:40:17 +04:00
|
|
|
RPC_context.make_call0
|
|
|
|
S.baking_rights
|
|
|
|
ctxt
|
|
|
|
block
|
|
|
|
{levels; cycles; delegates; max_priority; all}
|
2019-09-05 17:21:01 +04:00
|
|
|
()
|
|
|
|
end
|
|
|
|
|
|
|
|
module Endorsing_rights = struct
|
|
|
|
type t = {
|
2020-02-12 20:40:17 +04:00
|
|
|
level : Raw_level.t;
|
|
|
|
delegate : Signature.Public_key_hash.t;
|
|
|
|
slots : int list;
|
|
|
|
estimated_time : Time.t option;
|
2019-09-05 17:21:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
let encoding =
|
|
|
|
let open Data_encoding in
|
|
|
|
conv
|
2020-02-12 20:40:17 +04:00
|
|
|
(fun {level; delegate; slots; estimated_time} ->
|
|
|
|
(level, delegate, slots, estimated_time))
|
2019-09-05 17:21:01 +04:00
|
|
|
(fun (level, delegate, slots, estimated_time) ->
|
2020-02-12 20:40:17 +04:00
|
|
|
{level; delegate; slots; estimated_time})
|
2019-09-05 17:21:01 +04:00
|
|
|
(obj4
|
|
|
|
(req "level" Raw_level.encoding)
|
|
|
|
(req "delegate" Signature.Public_key_hash.encoding)
|
|
|
|
(req "slots" (list uint16))
|
|
|
|
(opt "estimated_time" Timestamp.encoding))
|
|
|
|
|
|
|
|
module S = struct
|
|
|
|
open Data_encoding
|
|
|
|
|
2020-02-12 20:40:17 +04:00
|
|
|
let custom_root = RPC_path.(open_root / "helpers" / "endorsing_rights")
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
type endorsing_rights_query = {
|
2020-02-12 20:40:17 +04:00
|
|
|
levels : Raw_level.t list;
|
|
|
|
cycles : Cycle.t list;
|
|
|
|
delegates : Signature.Public_key_hash.t list;
|
2019-09-05 17:21:01 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
let endorsing_rights_query =
|
|
|
|
let open RPC_query in
|
2020-02-12 20:40:17 +04:00
|
|
|
query (fun levels cycles delegates -> {levels; cycles; delegates})
|
2019-09-05 17:21:01 +04:00
|
|
|
|+ multi_field "level" Raw_level.rpc_arg (fun t -> t.levels)
|
|
|
|
|+ multi_field "cycle" Cycle.rpc_arg (fun t -> t.cycles)
|
2020-02-12 20:40:17 +04:00
|
|
|
|+ multi_field "delegate" Signature.Public_key_hash.rpc_arg (fun t ->
|
|
|
|
t.delegates)
|
2019-09-05 17:21:01 +04:00
|
|
|
|> seal
|
|
|
|
|
|
|
|
let endorsing_rights =
|
|
|
|
RPC_service.get_service
|
|
|
|
~description:
|
|
|
|
"Retrieves the delegates allowed to endorse a block.\n\
|
2020-02-12 20:40:17 +04:00
|
|
|
By default, it gives the endorsement slots for delegates that have \
|
|
|
|
at least one in the next block.\n\
|
|
|
|
Parameters `level` and `cycle` can be used to specify the (valid) \
|
|
|
|
level(s) in the past or future at which the endorsement rights \
|
|
|
|
have to be returned. Parameter `delegate` can be used to restrict \
|
|
|
|
the results to the given delegates.\n\
|
|
|
|
Returns the list of endorsement slots. Also returns the minimal \
|
|
|
|
timestamps that correspond to these slots. The timestamps are \
|
|
|
|
omitted for levels in the past, and are only estimates for levels \
|
|
|
|
later that the next block, based on the hypothesis that all \
|
|
|
|
predecessor blocks were baked at the first priority."
|
|
|
|
~query:endorsing_rights_query
|
|
|
|
~output:(list encoding)
|
2019-09-05 17:21:01 +04:00
|
|
|
custom_root
|
|
|
|
end
|
|
|
|
|
|
|
|
let endorsement_slots ctxt (level, estimated_time) =
|
2020-02-12 20:40:17 +04:00
|
|
|
Baking.endorsement_rights ctxt level
|
|
|
|
>>=? fun rights ->
|
2019-09-05 17:21:01 +04:00
|
|
|
return
|
|
|
|
(Signature.Public_key_hash.Map.fold
|
2020-02-12 20:40:17 +04:00
|
|
|
(fun delegate (_, slots, _) acc ->
|
|
|
|
{level = level.level; delegate; slots; estimated_time} :: acc)
|
|
|
|
rights
|
|
|
|
[])
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let register () =
|
|
|
|
let open Services_registration in
|
2020-02-12 20:40:17 +04:00
|
|
|
register0 S.endorsing_rights (fun ctxt q () ->
|
|
|
|
requested_levels
|
|
|
|
~default:(Level.current ctxt, Some (Timestamp.current ctxt))
|
|
|
|
ctxt
|
|
|
|
q.cycles
|
|
|
|
q.levels
|
|
|
|
>>=? fun levels ->
|
|
|
|
map_s (endorsement_slots ctxt) levels
|
|
|
|
>>=? fun rights ->
|
|
|
|
let rights = List.concat rights in
|
|
|
|
match q.delegates with
|
|
|
|
| [] ->
|
|
|
|
return rights
|
|
|
|
| _ :: _ as delegates ->
|
|
|
|
let is_requested p =
|
|
|
|
List.exists
|
|
|
|
(Signature.Public_key_hash.equal p.delegate)
|
|
|
|
delegates
|
|
|
|
in
|
|
|
|
return (List.filter is_requested rights))
|
|
|
|
|
|
|
|
let get ctxt ?(levels = []) ?(cycles = []) ?(delegates = []) block =
|
|
|
|
RPC_context.make_call0
|
|
|
|
S.endorsing_rights
|
|
|
|
ctxt
|
|
|
|
block
|
|
|
|
{levels; cycles; delegates}
|
2019-09-05 17:21:01 +04:00
|
|
|
()
|
|
|
|
end
|
|
|
|
|
2019-10-17 13:45:27 +04:00
|
|
|
module Endorsing_power = struct
|
|
|
|
let endorsing_power ctxt (operation, chain_id) =
|
2020-02-12 20:40:17 +04:00
|
|
|
let (Operation_data data) = operation.protocol_data in
|
2019-10-17 13:45:27 +04:00
|
|
|
match data.contents with
|
2020-02-12 20:40:17 +04:00
|
|
|
| Single (Endorsement _) ->
|
|
|
|
Baking.check_endorsement_rights
|
|
|
|
ctxt
|
|
|
|
chain_id
|
|
|
|
{shell = operation.shell; protocol_data = data}
|
|
|
|
>>=? fun (_, slots, _) -> return (List.length slots)
|
2019-10-17 13:45:27 +04:00
|
|
|
| _ ->
|
|
|
|
failwith "Operation is not an endorsement"
|
|
|
|
|
|
|
|
module S = struct
|
|
|
|
let endorsing_power =
|
|
|
|
let open Data_encoding in
|
|
|
|
RPC_service.post_service
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"Get the endorsing power of an endorsement, that is, the number of \
|
|
|
|
slots that the endorser has"
|
|
|
|
~query:RPC_query.empty
|
|
|
|
~input:
|
|
|
|
(obj2
|
|
|
|
(req "endorsement_operation" Operation.encoding)
|
|
|
|
(req "chain_id" Chain_id.encoding))
|
|
|
|
~output:int31
|
2019-10-17 13:45:27 +04:00
|
|
|
RPC_path.(open_root / "endorsing_power")
|
|
|
|
end
|
|
|
|
|
|
|
|
let register () =
|
|
|
|
let open Services_registration in
|
2020-02-12 20:40:17 +04:00
|
|
|
register0 S.endorsing_power (fun ctxt () (op, chain_id) ->
|
|
|
|
endorsing_power ctxt (op, chain_id))
|
2019-10-17 13:45:27 +04:00
|
|
|
|
|
|
|
let get ctxt block op chain_id =
|
|
|
|
RPC_context.make_call0 S.endorsing_power ctxt block () (op, chain_id)
|
|
|
|
end
|
|
|
|
|
|
|
|
module Required_endorsements = struct
|
|
|
|
let required_endorsements ctxt block_delay =
|
|
|
|
return (Baking.minimum_allowed_endorsements ctxt ~block_delay)
|
|
|
|
|
|
|
|
module S = struct
|
2020-02-12 20:40:17 +04:00
|
|
|
type t = {block_delay : Period.t}
|
2019-10-17 13:45:27 +04:00
|
|
|
|
|
|
|
let required_endorsements_query =
|
|
|
|
let open RPC_query in
|
2020-02-12 20:40:17 +04:00
|
|
|
query (fun block_delay -> {block_delay})
|
|
|
|
|+ field "block_delay" Period.rpc_arg Period.zero (fun t ->
|
|
|
|
t.block_delay)
|
2019-10-17 13:45:27 +04:00
|
|
|
|> seal
|
|
|
|
|
|
|
|
let required_endorsements =
|
|
|
|
let open Data_encoding in
|
|
|
|
RPC_service.get_service
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"Minimum number of endorsements for a block to be valid, given a \
|
|
|
|
delay of the block's timestamp with respect to the minimum time to \
|
|
|
|
bake at the block's priority"
|
|
|
|
~query:required_endorsements_query
|
|
|
|
~output:int31
|
2019-10-17 13:45:27 +04:00
|
|
|
RPC_path.(open_root / "required_endorsements")
|
|
|
|
end
|
|
|
|
|
|
|
|
let register () =
|
|
|
|
let open Services_registration in
|
2020-02-12 20:40:17 +04:00
|
|
|
register0 S.required_endorsements (fun ctxt {block_delay} () ->
|
|
|
|
required_endorsements ctxt block_delay)
|
2019-10-17 13:45:27 +04:00
|
|
|
|
|
|
|
let get ctxt block block_delay =
|
2020-02-12 20:40:17 +04:00
|
|
|
RPC_context.make_call0 S.required_endorsements ctxt block {block_delay} ()
|
2019-10-17 13:45:27 +04:00
|
|
|
end
|
|
|
|
|
|
|
|
module Minimal_valid_time = struct
|
|
|
|
let minimal_valid_time ctxt ~priority ~endorsing_power =
|
2020-02-12 20:40:17 +04:00
|
|
|
Baking.minimal_valid_time ctxt ~priority ~endorsing_power
|
2019-10-17 13:45:27 +04:00
|
|
|
|
|
|
|
module S = struct
|
2020-02-12 20:40:17 +04:00
|
|
|
type t = {priority : int; endorsing_power : int}
|
2019-10-17 13:45:27 +04:00
|
|
|
|
|
|
|
let minimal_valid_time_query =
|
|
|
|
let open RPC_query in
|
2020-02-12 20:40:17 +04:00
|
|
|
query (fun priority endorsing_power -> {priority; endorsing_power})
|
2019-10-17 13:45:27 +04:00
|
|
|
|+ field "priority" RPC_arg.int 0 (fun t -> t.priority)
|
|
|
|
|+ field "endorsing_power" RPC_arg.int 0 (fun t -> t.endorsing_power)
|
|
|
|
|> seal
|
|
|
|
|
|
|
|
let minimal_valid_time =
|
|
|
|
RPC_service.get_service
|
2020-02-12 20:40:17 +04:00
|
|
|
~description:
|
|
|
|
"Minimal valid time for a block given a priority and an endorsing \
|
|
|
|
power."
|
|
|
|
~query:minimal_valid_time_query
|
|
|
|
~output:Time.encoding
|
2019-10-17 13:45:27 +04:00
|
|
|
RPC_path.(open_root / "minimal_valid_time")
|
|
|
|
end
|
|
|
|
|
|
|
|
let register () =
|
|
|
|
let open Services_registration in
|
2020-02-12 20:40:17 +04:00
|
|
|
register0 S.minimal_valid_time (fun ctxt {priority; endorsing_power} () ->
|
|
|
|
minimal_valid_time ctxt ~priority ~endorsing_power)
|
2019-10-17 13:45:27 +04:00
|
|
|
|
|
|
|
let get ctxt block priority endorsing_power =
|
2020-02-12 20:40:17 +04:00
|
|
|
RPC_context.make_call0
|
|
|
|
S.minimal_valid_time
|
|
|
|
ctxt
|
|
|
|
block
|
|
|
|
{priority; endorsing_power}
|
|
|
|
()
|
2019-10-17 13:45:27 +04:00
|
|
|
end
|
|
|
|
|
2019-09-05 17:21:01 +04:00
|
|
|
let register () =
|
|
|
|
register () ;
|
|
|
|
Baking_rights.register () ;
|
2019-10-17 13:45:27 +04:00
|
|
|
Endorsing_rights.register () ;
|
|
|
|
Endorsing_power.register () ;
|
|
|
|
Required_endorsements.register () ;
|
|
|
|
Minimal_valid_time.register ()
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let endorsement_rights ctxt level =
|
2020-02-12 20:40:17 +04:00
|
|
|
Endorsing_rights.endorsement_slots ctxt (level, None)
|
|
|
|
>>=? fun l ->
|
|
|
|
return (List.map (fun {Endorsing_rights.delegate; _} -> delegate) l)
|
2019-09-05 17:21:01 +04:00
|
|
|
|
|
|
|
let baking_rights ctxt max_priority =
|
|
|
|
let max = match max_priority with None -> 64 | Some m -> m in
|
|
|
|
let level = Level.current ctxt in
|
2020-02-12 20:40:17 +04:00
|
|
|
Baking_rights.baking_priorities ctxt max (level, None)
|
|
|
|
>>=? fun l ->
|
|
|
|
return
|
|
|
|
( level.level,
|
|
|
|
List.map
|
|
|
|
(fun {Baking_rights.delegate; timestamp; _} -> (delegate, timestamp))
|
|
|
|
l )
|
2019-10-17 13:45:27 +04:00
|
|
|
|
|
|
|
let endorsing_power ctxt operation =
|
|
|
|
Endorsing_power.endorsing_power ctxt operation
|
|
|
|
|
|
|
|
let required_endorsements ctxt delay =
|
|
|
|
Required_endorsements.required_endorsements ctxt delay
|
|
|
|
|
|
|
|
let minimal_valid_time ctxt priority endorsing_power =
|
|
|
|
Minimal_valid_time.minimal_valid_time ctxt priority endorsing_power
|