https://gitlab.com/tezos/tezos
Tip revision: 84371e2ff2616cd7d8e301380d3eba41ef41dce5 authored by Hantang Sun on 15 August 2023, 16:01:59 UTC
EVM:added script to measure TPS for ERC-20
EVM:added script to measure TPS for ERC-20
Tip revision: 84371e2
operation_selection.ml
(*****************************************************************************)
(* *)
(* Open Source License *)
(* Copyright (c) 2021 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 Protocol
open Alpha_context
open Operation_pool
module Events = Baking_events.Selection
let quota = Main.validation_passes
let consensus_quota = Stdlib.List.nth quota Operation_repr.consensus_pass
let votes_quota = Stdlib.List.nth quota Operation_repr.voting_pass
let anonymous_quota = Stdlib.List.nth quota Operation_repr.anonymous_pass
let managers_quota = Stdlib.List.nth quota Operation_repr.manager_pass
type prioritized_manager = {
op : Prioritized_operation.t;
size : int;
fee : Tez.t;
gas : Fixed_point_repr.integral_tag Gas.Arith.t;
weight : Q.t;
source : public_key_hash;
counter : Manager_counter.t;
}
module PrioritizedManagerSet = Set.Make (struct
type t = prioritized_manager
(* We order the operations by their weights except if they belong
to the same manager, if they do, we order them by their
counter. *)
let compare {source; counter; weight; op; _}
{source = source'; counter = counter'; weight = weight'; op = op'; _} =
(* Be careful with the [compare] *)
let cmp_src = Signature.Public_key_hash.compare source source' in
if cmp_src = 0 then
(* we want the smallest counter first *)
let c = Manager_counter.compare counter counter' in
if c <> 0 then c
else
let c = Prioritized_operation.compare_priority op' op in
if c <> 0 then c else Q.compare weight' weight
(* if same counter, biggest weight first *)
else
let c = Prioritized_operation.compare_priority op' op in
if c <> 0 then c
else
(* We want the biggest weight first *)
let c = Q.compare weight' weight in
if c <> 0 then c else cmp_src
end)
(* Note: This weight is also used by the plugin and the prevalidator to sort
operations in the pending mempool.
See {!Tezos_protocol_plugin_alpha.Plugin.Mempool.weight_manager_operation}. *)
let prioritize_manager ~max_size ~hard_gas_limit_per_block ~minimal_fees
~minimal_nanotez_per_gas_unit ~minimal_nanotez_per_byte operation =
let open Result_syntax in
let op = Operation_pool.Prioritized_operation.packed operation in
let {protocol_data = Operation_data {contents; _}; _} = op in
let open Operation in
let l = to_list (Contents_list contents) in
List.fold_left_e
(fun ((first_source, first_counter, total_fee, total_gas) as acc) ->
function
| Contents (Manager_operation {source; counter; fee; gas_limit; _}) ->
let* total_fee =
Environment.wrap_tzresult @@ Tez.(total_fee +? fee)
in
(* There is only one unique source per packed transaction *)
let first_source = Option.value ~default:source first_source in
(* We only care about the first counter *)
let first_counter = Option.value ~default:counter first_counter in
return
( Some first_source,
Some first_counter,
total_fee,
Gas.Arith.add total_gas gas_limit )
| _ -> return acc)
(None, None, Tez.zero, Gas.Arith.zero)
l
|> function
| Ok (Some source, Some counter, fee, gas) ->
if Tez.(fee < minimal_fees) then None
else
let size =
Data_encoding.Binary.length
Operation.encoding_with_legacy_attestation_name
op
in
let size_f = Q.of_int size in
let gas_f = Q.of_bigint (Gas.Arith.integral_to_z gas) in
let fee_f = Q.of_int64 (Tez.to_mutez fee) in
let size_ratio = Q.(size_f / Q.of_int max_size) in
let gas_ratio =
Q.(
gas_f
/ Q.of_bigint (Gas.Arith.integral_to_z hard_gas_limit_per_block))
in
let weight = Q.(fee_f / max size_ratio gas_ratio) in
let fees_in_nanotez =
Q.mul (Q.of_int64 (Tez.to_mutez fee)) (Q.of_int 1000)
in
let enough_fees_for_gas =
let minimal_fees_in_nanotez =
Q.mul
minimal_nanotez_per_gas_unit
(Q.of_bigint @@ Gas.Arith.integral_to_z gas)
in
Q.compare minimal_fees_in_nanotez fees_in_nanotez <= 0
in
let enough_fees_for_size =
let minimal_fees_in_nanotez =
Q.mul minimal_nanotez_per_byte (Q.of_int size)
in
Q.compare minimal_fees_in_nanotez fees_in_nanotez <= 0
in
if enough_fees_for_size && enough_fees_for_gas then
Some {op = operation; size; weight; fee; gas; source; counter}
else None
| _ -> None
let prioritize_managers ~hard_gas_limit_per_block ~minimal_fees
~minimal_nanotez_per_gas_unit ~minimal_nanotez_per_byte managers =
Prioritized_operation_set.fold
(fun op acc ->
match
prioritize_manager
~max_size:managers_quota.max_size
~hard_gas_limit_per_block
~minimal_fees
~minimal_nanotez_per_gas_unit
~minimal_nanotez_per_byte
op
with
| None -> acc
| Some w_op -> PrioritizedManagerSet.add w_op acc)
managers
PrioritizedManagerSet.empty
(** Simulation *)
type simulation_result = {
validation_result : Tezos_protocol_environment.validation_result option;
block_header_metadata : block_header_metadata option;
operations : packed_operation list list;
operations_hash : Operation_list_list_hash.t;
}
let validate_operation inc op =
let open Lwt_syntax in
let* result = Baking_simulator.add_operation inc op in
match result with
| Error errs ->
let* () =
Events.(emit invalid_operation_filtered) (Operation.hash_packed op, errs)
in
return_none
| Ok (resulting_state, None) ->
(* No receipt if force_apply is not set *)
return_some resulting_state
| Ok (resulting_state, Some receipt) -> (
(* Check that the metadata are serializable/deserializable *)
let encoding_result =
let enc = Protocol.operation_receipt_encoding in
Option.bind
(Data_encoding.Binary.to_bytes_opt enc receipt)
(Data_encoding.Binary.of_bytes_opt enc)
in
match encoding_result with
| None ->
let* () =
Events.(emit cannot_serialize_operation_metadata)
(Operation.hash_packed op)
in
return_none
| Some _b -> return_some resulting_state)
let filter_valid_operations_up_to_quota inc (ops, quota) =
let open Lwt_syntax in
let {Tezos_protocol_environment.max_size; max_op} = quota in
let exception Full of (Baking_simulator.incremental * packed_operation list)
in
try
let* inc, _, _, l =
List.fold_left_s
(fun (inc, curr_size, nb_ops, acc) op ->
let op_size =
Data_encoding.Binary.length
Alpha_context.Operation.encoding_with_legacy_attestation_name
op
in
let new_size = curr_size + op_size in
if new_size > max_size then return (inc, curr_size, nb_ops, acc)
else (
Option.iter
(fun max_op ->
if max_op = nb_ops + 1 then raise (Full (inc, acc)))
max_op ;
let* inc'_opt = validate_operation inc op in
match inc'_opt with
| None -> return (inc, curr_size, nb_ops, acc)
| Some inc' -> return (inc', new_size, nb_ops + 1, op :: acc)))
(inc, 0, 0, [])
ops
in
return (inc, List.rev l)
with Full (inc, l) -> return (inc, List.rev l)
let filter_operations_with_simulation initial_inc fees_config
~hard_gas_limit_per_block {consensus; votes; anonymous; managers} =
let open Lwt_result_syntax in
let {
Baking_configuration.minimal_fees;
minimal_nanotez_per_gas_unit;
minimal_nanotez_per_byte;
} =
fees_config
in
let*! inc, consensus =
filter_valid_operations_up_to_quota
initial_inc
(Prioritized_operation_set.operations consensus, consensus_quota)
in
let*! inc, votes =
filter_valid_operations_up_to_quota
inc
(Prioritized_operation_set.operations votes, votes_quota)
in
let*! inc, anonymous =
filter_valid_operations_up_to_quota
inc
(Prioritized_operation_set.operations anonymous, anonymous_quota)
in
(* Sort the managers *)
let prioritized_managers =
prioritize_managers
~hard_gas_limit_per_block
~minimal_fees
~minimal_nanotez_per_gas_unit
~minimal_nanotez_per_byte
managers
in
let*! inc, managers =
filter_valid_operations_up_to_quota
inc
( PrioritizedManagerSet.elements prioritized_managers
|> List.map (fun {op; _} -> Prioritized_operation.packed op),
managers_quota )
in
let operations = [consensus; votes; anonymous; managers] in
let operations_hash =
Operation_list_list_hash.compute
(List.map
(fun sl ->
Operation_list_hash.compute (List.map Operation.hash_packed sl))
operations)
in
let inc = {inc with header = {inc.header with operations_hash}} in
let* result = Baking_simulator.finalize_construction inc in
match result with
| Some (validation_result, block_header_metadata) ->
return
{
validation_result = Some validation_result;
block_header_metadata = Some block_header_metadata;
operations;
operations_hash;
}
| None ->
return
{
validation_result = None;
block_header_metadata = None;
operations;
operations_hash;
}
let filter_valid_operations_up_to_quota_without_simulation (ops, quota) =
let {Tezos_protocol_environment.max_size; max_op} = quota in
let exception Full of packed_operation list in
try
List.fold_left
(fun (curr_size, nb_ops, acc) op ->
let op_size =
Data_encoding.Binary.length
Alpha_context.Operation.encoding_with_legacy_attestation_name
op
in
let new_size = curr_size + op_size in
if new_size > max_size then (curr_size, nb_ops, acc)
else (
Option.iter
(fun max_op -> if max_op = nb_ops + 1 then raise (Full acc))
max_op ;
(new_size, nb_ops + 1, op :: acc)))
(0, 0, [])
ops
|> fun (_, _, l) -> List.rev l
with Full l -> List.rev l
let filter_operations_without_simulation fees_config ~hard_gas_limit_per_block
{consensus; votes; anonymous; managers} =
let consensus =
filter_valid_operations_up_to_quota_without_simulation
(Prioritized_operation_set.operations consensus, consensus_quota)
in
let votes =
filter_valid_operations_up_to_quota_without_simulation
(Prioritized_operation_set.operations votes, votes_quota)
in
let anonymous =
filter_valid_operations_up_to_quota_without_simulation
(Prioritized_operation_set.operations anonymous, anonymous_quota)
in
let {
Baking_configuration.minimal_fees;
minimal_nanotez_per_gas_unit;
minimal_nanotez_per_byte;
} =
fees_config
in
(* Sort the managers *)
let prioritized_managers =
prioritize_managers
~hard_gas_limit_per_block
~minimal_fees
~minimal_nanotez_per_gas_unit
~minimal_nanotez_per_byte
managers
in
let managers =
filter_valid_operations_up_to_quota_without_simulation
( PrioritizedManagerSet.elements prioritized_managers
|> List.map (fun {op; _} -> Prioritized_operation.packed op),
managers_quota )
in
let operations = [consensus; votes; anonymous; managers] in
operations
let filter_consensus_operations_only inc
({consensus; votes; anonymous; managers} as ordered_pool) =
let open Lwt_result_syntax in
let*! incremental, filtered_consensus =
filter_valid_operations_up_to_quota inc (consensus, consensus_quota)
in
let payload = Operation_pool.payload_of_ordered_pool ordered_pool in
let* incremental =
List.fold_left_es
(fun inc op ->
let* inc, _ = Baking_simulator.add_operation inc op in
return inc)
incremental
(List.flatten [votes; anonymous; managers])
in
let filtered_pool =
Operation_pool.ordered_pool_of_payload
~consensus_operations:filtered_consensus
payload
in
return (incremental, filtered_pool)