From ccd86db23c1d17a822ea9ae0428a8aa1f46a2ad3 Mon Sep 17 00:00:00 2001 From: Thomas Athorne Date: Thu, 28 Apr 2022 11:44:58 +0100 Subject: [PATCH 1/4] Proto: SCORU: move refutation game logic to repr module --- src/proto_alpha/lib_protocol/TEZOS_PROTOCOL | 4 +- src/proto_alpha/lib_protocol/alpha_context.ml | 1 + .../lib_protocol/alpha_context.mli | 53 ++ src/proto_alpha/lib_protocol/dune.inc | 20 +- .../lib_protocol/sc_rollup_game.ml | 438 ---------------- .../lib_protocol/sc_rollup_game.mli | 270 ---------- .../lib_protocol/sc_rollup_game_repr.ml | 478 ++++++++++++++++++ .../lib_protocol/sc_rollup_game_repr.mli | 312 ++++++++++++ .../lib_protocol/sc_rollup_tick_repr.ml | 3 + .../lib_protocol/sc_rollup_tick_repr.mli | 7 + 10 files changed, 866 insertions(+), 720 deletions(-) delete mode 100644 src/proto_alpha/lib_protocol/sc_rollup_game.ml delete mode 100644 src/proto_alpha/lib_protocol/sc_rollup_game.mli create mode 100644 src/proto_alpha/lib_protocol/sc_rollup_game_repr.ml create mode 100644 src/proto_alpha/lib_protocol/sc_rollup_game_repr.mli diff --git a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL index 6904742f91e8..eb644ef194d0 100644 --- a/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL +++ b/src/proto_alpha/lib_protocol/TEZOS_PROTOCOL @@ -37,6 +37,8 @@ "Sc_rollup_repr", "Skip_list_repr", "Sc_rollup_inbox_repr", + "Sc_rollup_tick_repr", + "Sc_rollup_game_repr", "Seed_repr", "Sampler", "Voting_period_repr", @@ -77,7 +79,6 @@ "Lazy_storage_kind", "Receipt_repr", "Migration_repr", - "Sc_rollup_tick_repr", "Carbonated_map_costs", "Carbonated_map", @@ -183,7 +184,6 @@ "Sc_rollup_management_protocol", "Sc_rollup_operations", "Sc_rollup_PVM_sem", - "Sc_rollup_game", "Sc_rollup_arith", "Sc_rollups", diff --git a/src/proto_alpha/lib_protocol/alpha_context.ml b/src/proto_alpha/lib_protocol/alpha_context.ml index 520c0f00d71c..b963576c8f14 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.ml +++ b/src/proto_alpha/lib_protocol/alpha_context.ml @@ -56,6 +56,7 @@ module Sc_rollup = struct module Tick = Sc_rollup_tick_repr include Sc_rollup_repr module Inbox = Sc_rollup_inbox_repr + module Game = Sc_rollup_game_repr include Sc_rollup_storage end diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 70c9b9413d09..4c8cda50ea2c 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2589,6 +2589,59 @@ module Sc_rollup : sig MerkelizedOperations with type tree = Tree.tree end + module Game : sig + module Proof : sig + type t = + | Computation_step of { + valid : bool; + start : State_hash.t; + stop : State_hash.t; + } + | Input_step of { + valid : bool; + start : State_hash.t; + stop : State_hash.t; + } + | Blocked_step of {valid : bool; start : State_hash.t} + end + + type player = Alice | Bob + + type t = { + turn : player; + inbox_snapshot : Inbox.t; + dissection : (State_hash.t option * Tick.t) list; + } + + val opponent : player -> player + + type step = + | Dissection of (State_hash.t option * Tick.t) list + | Proof of Proof.t + + type refutation = {choice : Tick.t; step : step} + + val pp_refutation : Format.formatter -> refutation -> unit + + type reason = Conflict_resolved | Invalid_move | Timeout + + val pp_reason : Format.formatter -> reason -> unit + + val reason_encoding : reason Data_encoding.t + + type status = Ongoing | Ended of (reason * Staker.t) + + val pp_status : Format.formatter -> status -> unit + + val status_encoding : status Data_encoding.t + + type outcome = {loser : player; reason : reason} + + val pp_outcome : Format.formatter -> outcome -> unit + + val outcome_encoding : outcome Data_encoding.t + end + val rpc_arg : t RPC_arg.t val add_messages : diff --git a/src/proto_alpha/lib_protocol/dune.inc b/src/proto_alpha/lib_protocol/dune.inc index 4c9b30ca4b51..b2cb4c95ff94 100644 --- a/src/proto_alpha/lib_protocol/dune.inc +++ b/src/proto_alpha/lib_protocol/dune.inc @@ -62,6 +62,8 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end sc_rollup_repr.mli sc_rollup_repr.ml skip_list_repr.mli skip_list_repr.ml sc_rollup_inbox_repr.mli sc_rollup_inbox_repr.ml + sc_rollup_tick_repr.mli sc_rollup_tick_repr.ml + sc_rollup_game_repr.mli sc_rollup_game_repr.ml seed_repr.mli seed_repr.ml sampler.mli sampler.ml voting_period_repr.mli voting_period_repr.ml @@ -102,7 +104,6 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end lazy_storage_kind.mli lazy_storage_kind.ml receipt_repr.mli receipt_repr.ml migration_repr.mli migration_repr.ml - sc_rollup_tick_repr.mli sc_rollup_tick_repr.ml carbonated_map_costs.mli carbonated_map_costs.ml carbonated_map.mli carbonated_map.ml raw_context_intf.ml @@ -195,7 +196,6 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end sc_rollup_management_protocol.mli sc_rollup_management_protocol.ml sc_rollup_operations.mli sc_rollup_operations.ml sc_rollup_PVM_sem.ml - sc_rollup_game.mli sc_rollup_game.ml sc_rollup_arith.mli sc_rollup_arith.ml sc_rollups.mli sc_rollups.ml baking.mli baking.ml @@ -252,6 +252,8 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end sc_rollup_repr.mli sc_rollup_repr.ml skip_list_repr.mli skip_list_repr.ml sc_rollup_inbox_repr.mli sc_rollup_inbox_repr.ml + sc_rollup_tick_repr.mli sc_rollup_tick_repr.ml + sc_rollup_game_repr.mli sc_rollup_game_repr.ml seed_repr.mli seed_repr.ml sampler.mli sampler.ml voting_period_repr.mli voting_period_repr.ml @@ -292,7 +294,6 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end lazy_storage_kind.mli lazy_storage_kind.ml receipt_repr.mli receipt_repr.ml migration_repr.mli migration_repr.ml - sc_rollup_tick_repr.mli sc_rollup_tick_repr.ml carbonated_map_costs.mli carbonated_map_costs.ml carbonated_map.mli carbonated_map.ml raw_context_intf.ml @@ -385,7 +386,6 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end sc_rollup_management_protocol.mli sc_rollup_management_protocol.ml sc_rollup_operations.mli sc_rollup_operations.ml sc_rollup_PVM_sem.ml - sc_rollup_game.mli sc_rollup_game.ml sc_rollup_arith.mli sc_rollup_arith.ml sc_rollups.mli sc_rollups.ml baking.mli baking.ml @@ -442,6 +442,8 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end sc_rollup_repr.mli sc_rollup_repr.ml skip_list_repr.mli skip_list_repr.ml sc_rollup_inbox_repr.mli sc_rollup_inbox_repr.ml + sc_rollup_tick_repr.mli sc_rollup_tick_repr.ml + sc_rollup_game_repr.mli sc_rollup_game_repr.ml seed_repr.mli seed_repr.ml sampler.mli sampler.ml voting_period_repr.mli voting_period_repr.ml @@ -482,7 +484,6 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end lazy_storage_kind.mli lazy_storage_kind.ml receipt_repr.mli receipt_repr.ml migration_repr.mli migration_repr.ml - sc_rollup_tick_repr.mli sc_rollup_tick_repr.ml carbonated_map_costs.mli carbonated_map_costs.ml carbonated_map.mli carbonated_map.ml raw_context_intf.ml @@ -575,7 +576,6 @@ module CamlinternalFormatBasics = struct include CamlinternalFormatBasics end sc_rollup_management_protocol.mli sc_rollup_management_protocol.ml sc_rollup_operations.mli sc_rollup_operations.ml sc_rollup_PVM_sem.ml - sc_rollup_game.mli sc_rollup_game.ml sc_rollup_arith.mli sc_rollup_arith.ml sc_rollups.mli sc_rollups.ml baking.mli baking.ml @@ -654,6 +654,8 @@ include Tezos_raw_protocol_alpha.Main Sc_rollup_repr Skip_list_repr Sc_rollup_inbox_repr + Sc_rollup_tick_repr + Sc_rollup_game_repr Seed_repr Sampler Voting_period_repr @@ -694,7 +696,6 @@ include Tezos_raw_protocol_alpha.Main Lazy_storage_kind Receipt_repr Migration_repr - Sc_rollup_tick_repr Carbonated_map_costs Carbonated_map Raw_context_intf @@ -787,7 +788,6 @@ include Tezos_raw_protocol_alpha.Main Sc_rollup_management_protocol Sc_rollup_operations Sc_rollup_PVM_sem - Sc_rollup_game Sc_rollup_arith Sc_rollups Baking @@ -885,6 +885,8 @@ include Tezos_raw_protocol_alpha.Main sc_rollup_repr.mli sc_rollup_repr.ml skip_list_repr.mli skip_list_repr.ml sc_rollup_inbox_repr.mli sc_rollup_inbox_repr.ml + sc_rollup_tick_repr.mli sc_rollup_tick_repr.ml + sc_rollup_game_repr.mli sc_rollup_game_repr.ml seed_repr.mli seed_repr.ml sampler.mli sampler.ml voting_period_repr.mli voting_period_repr.ml @@ -925,7 +927,6 @@ include Tezos_raw_protocol_alpha.Main lazy_storage_kind.mli lazy_storage_kind.ml receipt_repr.mli receipt_repr.ml migration_repr.mli migration_repr.ml - sc_rollup_tick_repr.mli sc_rollup_tick_repr.ml carbonated_map_costs.mli carbonated_map_costs.ml carbonated_map.mli carbonated_map.ml raw_context_intf.ml @@ -1018,7 +1019,6 @@ include Tezos_raw_protocol_alpha.Main sc_rollup_management_protocol.mli sc_rollup_management_protocol.ml sc_rollup_operations.mli sc_rollup_operations.ml sc_rollup_PVM_sem.ml - sc_rollup_game.mli sc_rollup_game.ml sc_rollup_arith.mli sc_rollup_arith.ml sc_rollups.mli sc_rollups.ml baking.mli baking.ml diff --git a/src/proto_alpha/lib_protocol/sc_rollup_game.ml b/src/proto_alpha/lib_protocol/sc_rollup_game.ml deleted file mode 100644 index 8ce62325ae75..000000000000 --- a/src/proto_alpha/lib_protocol/sc_rollup_game.ml +++ /dev/null @@ -1,438 +0,0 @@ -(*****************************************************************************) -(* *) -(* Open Source License *) -(* Copyright (c) 2022 Nomadic Labs *) -(* Copyright (c) 2022 Trili Tech, *) -(* *) -(* 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.Sc_rollup - -module Make (PVM : Sc_rollup_PVM_sem.S) = struct - module PVM = PVM - - type tick = Sc_rollup_tick_repr.t - - module Section = struct - type section = { - section_start_state : PVM.hash; - section_start_at : tick; - section_stop_state : PVM.hash; - section_stop_at : tick; - } - - and dissection = section Sc_rollup_tick_repr.Map.t - - let section_encoding = - let open Data_encoding in - conv - (fun { - section_start_state; - section_start_at; - section_stop_state; - section_stop_at; - } -> - ( section_start_state, - section_start_at, - section_stop_state, - section_stop_at )) - (fun ( section_start_state, - section_start_at, - section_stop_state, - section_stop_at ) -> - { - section_start_state; - section_start_at; - section_stop_state; - section_stop_at; - }) - (obj4 - (req "section_start_state" State_hash.encoding) - (req "section_start_at" Sc_rollup_tick_repr.encoding) - (req "section_stop_state" State_hash.encoding) - (req "section_stop_at" Sc_rollup_tick_repr.encoding)) - - let dissection_encoding = - let open Data_encoding in - let open Sc_rollup_tick_repr in - option - @@ conv - (fun map -> List.of_seq @@ Map.to_seq map) - (fun list -> Map.of_seq @@ List.to_seq list) - (list @@ tup2 encoding section_encoding) - - let empty_dissection = Sc_rollup_tick_repr.Map.empty - - let add_section section dissection = - Sc_rollup_tick_repr.Map.add section.section_start_at section dissection - - let find_section section (dissection : dissection) = - let open Sc_rollup_tick_repr in - Option.bind - (Map.find section.section_start_at dissection) - (fun {section_start_at; section_stop_at; _} -> - if - section_start_at = section.section_start_at - && section_stop_at = section.section_stop_at - then Some section - else None) - - let fold_over_dissection f i dissection = - Sc_rollup_tick_repr.Map.fold f i dissection - - let dissection_cardinal = Sc_rollup_tick_repr.Map.cardinal - - let last_section = Sc_rollup_tick_repr.Map.max_binding_opt - - let pp_section ppf (s : section) = - Format.fprintf - ppf - "( %a ) -- [%a] \n ->\n ( %a ) -- [%a]\n " - State_hash.pp - s.section_start_state - Sc_rollup_tick_repr.pp - s.section_start_at - State_hash.pp - s.section_stop_state - Sc_rollup_tick_repr.pp - s.section_stop_at - - let pp_dissection ppf d = - let open Sc_rollup_tick_repr in - let list = List.of_seq @@ Map.to_seq d in - Format.pp_print_list - ~pp_sep:(fun ppf () -> Format.pp_print_string ppf ";\n\n") - (fun ppf (key, b) -> - Format.fprintf ppf "key = %a \n val = %a\n" pp key pp_section b) - ppf - list - - let pp_optional_dissection d = - Format.pp_print_option - ~none:(fun ppf () -> - Format.pp_print_text ppf "no dissection at the moment") - pp_dissection - d - - let valid_section ({section_start_at; section_stop_at; _} : section) = - Sc_rollup_tick_repr.(section_stop_at > section_start_at) - - let valid_dissection section dissection = - let open Sc_rollup_tick_repr in - let rec traverse last_pos = function - | Seq.Nil -> section.section_stop_at = last_pos - | Seq.Cons ((_, v), kvs) -> - last_pos = v.section_start_at - && valid_section v - && traverse v.section_stop_at (kvs ()) - in - traverse section.section_start_at (Map.to_seq dissection ()) - end - - type player = Committer | Refuter - - let string_of_player = function - | Committer -> "committer" - | Refuter -> "refuter" - - let pp_player ppf player = Format.fprintf ppf "%s" (string_of_player player) - - let player_encoding = - let open Data_encoding in - union - ~tag_size:`Uint8 - [ - case - ~title:"Commiter" - (Tag 0) - string - (function Committer -> Some "committer" | _ -> None) - (fun _ -> Committer); - case - ~title:"Refuter" - (Tag 1) - string - (function Refuter -> Some "refuter" | _ -> None) - (fun _ -> Refuter); - ] - - let opponent = function Committer -> Refuter | Refuter -> Committer - - type t = { - turn : player; - start_state : PVM.hash; - start_at : tick; - player_stop_state : PVM.hash; - opponent_stop_state : PVM.hash; - stop_at : tick; - current_dissection : Section.dissection option; - } - - let encoding = - let open Data_encoding in - conv - (fun { - turn; - start_state; - start_at; - player_stop_state; - opponent_stop_state; - stop_at; - current_dissection; - } -> - ( turn, - start_state, - start_at, - player_stop_state, - opponent_stop_state, - stop_at, - current_dissection )) - (fun ( turn, - start_state, - start_at, - player_stop_state, - opponent_stop_state, - stop_at, - current_dissection ) -> - { - turn; - start_state; - start_at; - player_stop_state; - opponent_stop_state; - stop_at; - current_dissection; - }) - (obj7 - (req "turn" player_encoding) - (req "start_state" State_hash.encoding) - (req "start_at" Sc_rollup_tick_repr.encoding) - (req "player_stop_state" State_hash.encoding) - (req "oponent_stop_state" State_hash.encoding) - (req "stop_at" Sc_rollup_tick_repr.encoding) - (req "current_dissection" Section.dissection_encoding)) - - type conflict_resolution_step = - | Refine of {stop_state : PVM.hash; next_dissection : Section.dissection} - | Conclude of Sc_rollup_PVM_sem.input option * PVM.proof - - type move = - | ConflictInside of { - choice : Section.section; - conflict_resolution_step : conflict_resolution_step; - } - - type commit = Commit of Section.section - - type refutation = RefuteByConflict of conflict_resolution_step - - type reason = InvalidMove | ConflictResolved - - let pp_reason ppf reason = - Format.fprintf - ppf - "%s" - (match reason with - | InvalidMove -> "invalid move" - | ConflictResolved -> "conflict resolved") - - type outcome = {winner : player option; reason : reason} - - let pp_winner winner = - Format.pp_print_option - ~none:(fun ppf () -> Format.pp_print_text ppf "no winner") - pp_player - winner - - let pp_outcome ppf {winner; reason} = - Format.fprintf ppf "%a because of %a" pp_winner winner pp_reason reason - - type move_result = Over of outcome | Ongoing of t - - let pp ppf (g : t) = - Format.fprintf - ppf - "%a @ %a -> %a / %a @ %a [%a] %s playing" - State_hash.pp - g.start_state - Sc_rollup_tick_repr.pp - g.start_at - State_hash.pp - g.player_stop_state - State_hash.pp - g.opponent_stop_state - Sc_rollup_tick_repr.pp - g.stop_at - Section.pp_optional_dissection - g.current_dissection - (match g.turn with Committer -> "committer" | Refuter -> "refuter") - - let pp_move ppf = function - | ConflictInside - { - choice; - conflict_resolution_step = Refine {next_dissection; stop_state}; - } -> - Format.fprintf - ppf - "conflict is inside %a, should end with %a, new dissection = %a" - Section.pp_section - choice - State_hash.pp - stop_state - Section.pp_dissection - next_dissection - | ConflictInside - {choice; conflict_resolution_step = Conclude (input, proof)} -> - let using_optional_input = - match input with - | None -> "" - | Some input -> - Format.sprintf " (using optional input `%s')" input.payload - in - Format.fprintf - ppf - "atomic conflict found inside %a, we can verify that it starts with \ - %a and should end with %a%s" - Section.pp_section - choice - State_hash.pp - (PVM.proof_start_state proof) - State_hash.pp - (PVM.proof_stop_state proof) - using_optional_input - - let conflict_found (game : t) = - Sc_rollup_tick_repr.(Z.equal (distance game.stop_at game.start_at) Z.one) - - let stop_state = function - | Refine {stop_state; _} -> stop_state - | Conclude (_, proof) -> PVM.proof_stop_state proof - - let initial (Commit commit) (refutation : conflict_resolution_step) = - let game = - { - start_state = commit.section_start_state; - start_at = commit.section_start_at; - opponent_stop_state = commit.section_stop_state; - stop_at = commit.section_stop_at; - player_stop_state = stop_state refutation; - current_dissection = None; - turn = Refuter; - } - in - let choice = commit in - let move = ConflictInside {choice; conflict_resolution_step = refutation} in - (game, move) - - let resolve_conflict (game : t) (input, proof) = - assert (conflict_found game) ; - let player = game.turn in - let opponent_state_valid = - State_hash.equal (PVM.proof_stop_state proof) game.opponent_stop_state - in - let over winner = {winner; reason = ConflictResolved} in - PVM.verify_proof ~input proof >>= fun player_state_valid -> - let outcome = - match (player_state_valid, opponent_state_valid) with - | (true, true) -> over @@ Some Committer - | (true, false) -> over @@ Some player - | (false, true) -> over @@ Some (opponent player) - | (false, false) -> over @@ None - in - Lwt.return outcome - - let apply_choice ~(game : t) ~(choice : Section.section) chosen_stop_state = - let section = - match game.current_dissection with - | Some dissection -> Section.find_section choice dissection - | None -> - if State_hash.equal choice.section_start_state game.start_state then - Some choice - else None - in - let game = - match section with - | None -> None - | Some - { - section_start_state; - section_start_at; - section_stop_state; - section_stop_at; - } -> - if State_hash.equal chosen_stop_state section_stop_state then None - else - Some - { - game with - start_state = section_start_state; - start_at = section_start_at; - opponent_stop_state = section_stop_state; - player_stop_state = chosen_stop_state; - stop_at = section_stop_at; - } - in - Lwt.return game - - let apply_dissection ~(game : t) (next_dissection : Section.dissection) = - let current_section : Section.section = - { - section_start_state = game.start_state; - section_start_at = game.start_at; - section_stop_state = game.opponent_stop_state; - section_stop_at = game.stop_at; - } - in - if Section.valid_dissection current_section next_dissection then - Lwt.return @@ Some {game with current_dissection = Some next_dissection} - else Lwt.return None - - let play game (ConflictInside {choice; conflict_resolution_step}) = - let player = game.turn in - - let apply_move () = - match conflict_resolution_step with - | Refine {next_dissection; stop_state} -> ( - apply_choice ~game ~choice stop_state >>= function - | None -> Lwt.return None - | Some game -> ( - apply_dissection ~game next_dissection >>= function - | None -> Lwt.return None - | Some game -> Lwt.return @@ Some (Ongoing game))) - | Conclude (input, proof) -> ( - apply_choice ~game ~choice (PVM.proof_stop_state proof) >>= function - | None -> Lwt.return None - | Some game -> - if - State_hash.equal (PVM.proof_start_state proof) game.start_state - && conflict_found game - then - resolve_conflict game (input, proof) >>= fun x -> - Lwt.return @@ Some (Over x) - else Lwt.return None) - in - apply_move () >>= function - | None -> - Lwt.return - @@ Over {winner = Some (opponent player); reason = InvalidMove} - | Some state -> Lwt.return state -end diff --git a/src/proto_alpha/lib_protocol/sc_rollup_game.mli b/src/proto_alpha/lib_protocol/sc_rollup_game.mli deleted file mode 100644 index 5f21610202ec..000000000000 --- a/src/proto_alpha/lib_protocol/sc_rollup_game.mli +++ /dev/null @@ -1,270 +0,0 @@ -(*****************************************************************************) -(* *) -(* Open Source License *) -(* Copyright (c) 2022 Nomadic Labs *) -(* Copyright (c) 2022 Trili Tech, *) -(* *) -(* 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. *) -(* *) -(*****************************************************************************) - -(** This module provides a refutation game logic for smart contract rollups. - - It is in fact a functor that takes a PVM semantic implemented by a - {!Sc_rollup_PVM_sem.S} module and produces a game logic. - - This game logic is used by the protocol when two commitments are in conflict - to determine which one of the commitments is wrong. - - A commitment characterizes a section of the PVM execution trace. When - two commitments are in conflict, they agree on the first state of the - section and disagree on the last one. The game runs in two steps: first, - the two players alternatively refine the section they disagree on until - they reach an atomic section of length 1, that is a single execution step - of the PVM ; second, the player must provide a Merkle proof that a - valid execution step corresponds to her final state. - - Important invariants - ==================== - - - The committer and the refuter agree on the first state of the current - section and disagree on its final state. If they agree on both then - whoever plays loses by [InvalidMove]. - - Expected properties - =================== - - Honest-committer-wins: - - If a committer has posted a valid commit and has a perfect PVM at hand, - there is a winning strategy which consists in choosing the first section - of the current dissection and in producing a regular dissection until the - conflict is reached. - - Honest-refuter-wins: - - If a refuter has detected an invalid commit and has a perfect PVM at hand, - the same strategy is also winning. - - Here "winning strategy" means that the player actually wins (a draw is not - enough). - - Remarks - ======= - - There are several subtle cornercases: - - - If the refuter and the committer both post only invalid states, the - game may end in a conflict state where both are wrong. By convention, - we decide that these games have no winner. - - - If the refuter and the committer both post only states, the - game never has any conflicts. By convention, - we decide that in this case the commiter wins by [InvalidMove]. - - - If the refuter and the committer both post valid and invalid states, - all outcomes are possible. This means that if a committer wins a - game we have no guarantee that she has posted a valid commit. - - -*) - -module Make : functor (PVM : Sc_rollup_PVM_sem.S) -> sig - module PVM : - Sc_rollup_PVM_sem.S - with type state = PVM.state - and type proof = PVM.proof - and type context = PVM.context - - type tick = Sc_rollup_tick_repr.t - - module Section : sig - type section = { - section_start_state : PVM.hash; - section_start_at : tick; - section_stop_state : PVM.hash; - section_stop_at : tick; - } - - val section_encoding : section Data_encoding.t - - val pp_section : Format.formatter -> section -> unit - - (** A [dissection] is a split of a section in several smaller - sections that form a partition of this section. *) - type dissection - - val dissection_encoding : dissection option Data_encoding.t - - val pp_dissection : Format.formatter -> dissection -> unit - - (** [empty_dissection] is an empty set. *) - val empty_dissection : dissection - - (** [add_section section dissection] contains the sections of - [dissection] plus a new [section]. *) - val add_section : section -> dissection -> dissection - - (** [find_section section dissection] returns [Some section] - if [section] is a member of [dissection]. - Returns [None] otherwise. *) - val find_section : section -> dissection -> section option - - (** [fold_over_dissection f dissection i] traverses [dissection] - from the section of lowest tick to the section of highest ticks - applying [f] to compute the next value of the accumulator, - initially set to [i]. *) - val fold_over_dissection : - (tick -> section -> 'a -> 'a) -> dissection -> 'a -> 'a - - (** [dissection_cardinal dissection] returns the number of - sections in the given [dissection]. *) - val dissection_cardinal : dissection -> int - - (** [last_section dissection] returns [Some (tick, section)] - if the last [section] of [dissection] starts at [tick]. - Returns [None] if [dissection] is empty. *) - val last_section : dissection -> (tick * section) option - - (** A section is valid if its [start_at] tick is smaller than its - [stop_at] tick. *) - val valid_section : section -> bool - - (** [valid_dissection section dissection] returns [true] iff - [dissection] is a partition of [section]. *) - val valid_dissection : section -> dissection -> bool - end - - (** The game has two players. The refuter starts. *) - type player = Committer | Refuter - - val player_encoding : player Data_encoding.t - - val pp_player : Format.formatter -> player -> unit - - (** [opponent player] is the other player. *) - val opponent : player -> player - - (** - - A game state is characterized by: - - - [turn], the player that must provide the next move. - - - [start_state/start_at], the state the two players agree on. - - - [player_stop_state] and [opponent_stop_state] are distinct as - the two players disagree on the final state. - - - [stop_at] is the position of the final state. - - - If the cardinal of [current_dissection] is strictly greater than 1, - the current player must choose one of its section and produce a - dissection of this section. - - - If the cardinal of [current_dissection] is equal to 1, - the game has reached its second phase and the current player - must provide a proof of the execution step at [start_at]. - - Invariants: - ----------- - - [current_dissection] cannot be empty. - - [start_at] is strictly less than [stop_at]. - - [player_stop_state] is distinct from [opponent_stop_state]. - - *) - type t = { - turn : player; - start_state : PVM.hash; - start_at : tick; - player_stop_state : PVM.hash; - opponent_stop_state : PVM.hash; - stop_at : tick; - current_dissection : Section.dissection option; - } - - val encoding : t Data_encoding.t - - val pp : Format.formatter -> t -> unit - - (** The conflict resolution is done in two phases: the refinement - phase composed of refinement steps and a final step, the - conclusion. *) - type conflict_resolution_step = - | Refine of {stop_state : PVM.hash; next_dissection : Section.dissection} - (** Assuming a current [dissection], a [section] has been chosen and - [next_dissection] is a dissection of this section. [stop_state] - is a state which is different from the one exposed in for the - [section] in the current [dissection]. *) - | Conclude of Sc_rollup_PVM_sem.input option * PVM.proof - (** When the current [dissection] contains a single section of - length 1, the conflict tick has been found. The player provides - a [PVM.proof] to demonstrate that her final state is the - correct one. - - If the current state is an input state (see - {!Sc_rollup_PVM_sem}), the player must provide the [input] - received by the PVM. Notice that the validity of this [input] - is externally verified using the {!Sc_rollup_inbox} proofs. *) - - (** A game move is a conflict resolution step performed in a given - section of the PVM execution trace. *) - type move = - | ConflictInside of { - choice : Section.section; - conflict_resolution_step : conflict_resolution_step; - } - - (** To start the game, the committer must have made a section public... *) - type commit = Commit of Section.section - - (** ... and the refuter must have started a dispute. *) - type refutation = RefuteByConflict of conflict_resolution_step - - (** The game can stop for two reasons: *) - type reason = - | InvalidMove - (** One of the player did not respect the game rules, typically - by providing a refinement step which is inconsistent with - the current dissection. *) - | ConflictResolved - (** The game reached the conclusion and the arbiter was able to - determine how the conflict must be resolved in conformance - with the PVM semantics. *) - - val pp_reason : Format.formatter -> reason -> unit - - (** The game can end with no winner if both players have wrong final - states at the conflicting step. *) - type outcome = {winner : player option; reason : reason} - - val pp_outcome : Format.formatter -> outcome -> unit - - (** After one game move, the game can be over or ongoing. *) - type move_result = Over of outcome | Ongoing of t - - val pp_move : Format.formatter -> move -> unit - - (** [initial] is the initial game state from the commit and the - refutation. The first player to play is the refuter, this - function also returns refuter's first move. *) - val initial : commit -> conflict_resolution_step -> t * move - - (** [play game move] returns the result of the given [move] applied - on [game]. *) - val play : t -> move -> move_result Lwt.t -end diff --git a/src/proto_alpha/lib_protocol/sc_rollup_game_repr.ml b/src/proto_alpha/lib_protocol/sc_rollup_game_repr.ml new file mode 100644 index 000000000000..c437a2f567c3 --- /dev/null +++ b/src/proto_alpha/lib_protocol/sc_rollup_game_repr.ml @@ -0,0 +1,478 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2021 Nomadic Labs *) +(* Copyright (c) 2022 Trili Tech, *) +(* *) +(* 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 Sc_rollup_repr + +module Proof = struct + (* TODO #2759: these proof cases are dummy ones for testing. Replace + them with the proper proofs. *) + type t = + | Computation_step of { + valid : bool; + start : State_hash.t; + stop : State_hash.t; + } + | Input_step of {valid : bool; start : State_hash.t; stop : State_hash.t} + | Blocked_step of {valid : bool; start : State_hash.t} + + let encoding = + Data_encoding.( + union + ~tag_size:`Uint8 + [ + case + ~title:"Proof of a normal computation step" + (Tag 0) + (tup3 bool State_hash.encoding State_hash.encoding) + (function + | Computation_step {valid; start; stop} -> + Some (valid, start, stop) + | _ -> None) + (fun (valid, start, stop) -> Computation_step {valid; start; stop}); + case + ~title:"Proof of an input step" + (Tag 1) + (tup3 bool State_hash.encoding State_hash.encoding) + (function + | Input_step {valid; start; stop} -> Some (valid, start, stop) + | _ -> None) + (fun (valid, start, stop) -> Input_step {valid; start; stop}); + case + ~title:"Proof that the PVM is blocked" + (Tag 2) + (tup2 bool State_hash.encoding) + (function + | Blocked_step {valid; start} -> Some (valid, start) | _ -> None) + (fun (valid, start) -> Blocked_step {valid; start}); + ]) + + (* TODO #2759 *) + let pp _ _ = () + + (* TODO #2759 *) + let start p = + match p with + | Computation_step x -> x.start + | Input_step x -> x.start + | Blocked_step x -> x.start + + (* TODO #2759 *) + let stop p = + match p with + | Computation_step x -> Some x.stop + | Input_step x -> Some x.stop + | Blocked_step _ -> None + + (* TODO #2759 *) + let valid p = + match p with + | Computation_step x -> x.valid + | Input_step x -> x.valid + | Blocked_step x -> x.valid +end + +type player = Alice | Bob + +type t = { + turn : player; + inbox_snapshot : Sc_rollup_inbox_repr.t; + dissection : (State_hash.t option * Sc_rollup_tick_repr.t) list; +} + +let player_encoding = + let open Data_encoding in + union + ~tag_size:`Uint8 + [ + case + ~title:"Alice" + (Tag 0) + unit + (function Alice -> Some () | _ -> None) + (fun () -> Alice); + case + ~title:"Bob" + (Tag 1) + unit + (function Bob -> Some () | _ -> None) + (fun () -> Bob); + ] + +let string_of_player = function Alice -> "alice" | Bob -> "bob" + +let pp_player ppf player = Format.fprintf ppf "%s" (string_of_player player) + +let opponent = function Alice -> Bob | Bob -> Alice + +let encoding = + let open Data_encoding in + conv + (fun {turn; inbox_snapshot; dissection} -> + (turn, inbox_snapshot, dissection)) + (fun (turn, inbox_snapshot, dissection) -> + {turn; inbox_snapshot; dissection}) + (obj3 + (req "turn" player_encoding) + (req "inbox_snapshot" Sc_rollup_inbox_repr.encoding) + (req + "dissection" + (list + (tup2 (option State_hash.encoding) Sc_rollup_tick_repr.encoding)))) + +let pp_dissection ppf d = + Format.pp_print_list + ~pp_sep:(fun ppf () -> Format.pp_print_string ppf ";\n") + (fun ppf (state, tick) -> + Format.fprintf + ppf + " %a @ %a" + (Format.pp_print_option State_hash.pp) + state + Sc_rollup_tick_repr.pp + tick) + ppf + d + +let pp ppf game = + Format.fprintf + ppf + "[%a] %a playing; inbox snapshot is %a" + pp_dissection + game.dissection + pp_player + game.turn + Sc_rollup_inbox_repr.pp + game.inbox_snapshot + +module Index = struct + type t = Staker.t * Staker.t + + let encoding = Data_encoding.tup2 Staker.encoding Staker.encoding + + let compare (a, b) (c, d) = + match Staker.compare a c with 0 -> Staker.compare b d | x -> x + + let to_path (a, b) p = Staker.to_b58check a :: Staker.to_b58check b :: p + + let both_of_b58check_opt (a, b) = + Option.bind (Staker.of_b58check_opt b) (fun b_staker -> + Option.bind (Staker.of_b58check_opt a) (fun a_staker -> + Some (a_staker, b_staker))) + + let of_path = function [a; b] -> both_of_b58check_opt (a, b) | _ -> None + + let path_length = 2 + + let rpc_arg = + let descr = + "A pair of stakers that index a smart contract rollup refutation game." + in + let construct (a, b) = + Format.sprintf "%s-%s" (Staker.to_b58check a) (Staker.to_b58check b) + in + let destruct s = + match String.split_on_char '-' s with + | [a; b] -> ( + match both_of_b58check_opt (a, b) with + | Some stakers -> ok stakers + | None -> + Result.error (Format.sprintf "Invalid game index notation %s" s)) + | _ -> Result.error (Format.sprintf "Invalid game index notation %s" s) + in + RPC_arg.make ~descr ~name:"game_index" ~construct ~destruct () + + let normalize (a, b) = + match Staker.compare a b with 1 -> (b, a) | _ -> (a, b) + + let staker stakers player = + let (alice, bob) = normalize stakers in + match player with Alice -> alice | Bob -> bob +end + +let initial inbox ~(parent : Commitment.t) ~(child : Commitment.t) ~refuter + ~defender = + let (alice, _) = Index.normalize (refuter, defender) in + let alice_to_play = Staker.equal alice refuter in + let tick = Sc_rollup_tick_repr.of_number_of_ticks child.number_of_ticks in + { + turn = (if alice_to_play then Alice else Bob); + inbox_snapshot = inbox; + dissection = + [ + (Some parent.compressed_state, Sc_rollup_tick_repr.initial); + (Some child.compressed_state, tick); + (None, Sc_rollup_tick_repr.next tick); + ]; + } + +type step = + | Dissection of (State_hash.t option * Sc_rollup_tick_repr.t) list + | Proof of Proof.t + +let step_encoding = + let open Data_encoding in + union + ~tag_size:`Uint8 + [ + case + ~title:"Dissection" + (Tag 0) + (list (tup2 (option State_hash.encoding) Sc_rollup_tick_repr.encoding)) + (function Dissection d -> Some d | _ -> None) + (fun d -> Dissection d); + case + ~title:"Proof" + (Tag 1) + Proof.encoding + (function Proof p -> Some p | _ -> None) + (fun p -> Proof p); + ] + +let pp_step ppf step = + match step with + | Dissection states -> + Format.fprintf ppf "dissection:\n" ; + Format.pp_print_list + ~pp_sep:(fun ppf () -> Format.pp_print_string ppf ";\n\n") + (fun ppf (hash, t) -> + Format.fprintf + ppf + "tick = %a, state = %a\n" + Sc_rollup_tick_repr.pp + t + (Format.pp_print_option State_hash.pp) + hash) + ppf + states + | Proof proof -> Format.fprintf ppf "proof: %a" Proof.pp proof + +type refutation = {choice : Sc_rollup_tick_repr.t; step : step} + +let pp_refutation ppf refutation = + Format.fprintf + ppf + "Refute at tick %a with %a.\n" + Sc_rollup_tick_repr.pp + refutation.choice + pp_step + refutation.step + +let refutation_encoding = + let open Data_encoding in + conv + (fun {choice; step} -> (choice, step)) + (fun (choice, step) -> {choice; step}) + (obj2 + (req "choice" Sc_rollup_tick_repr.encoding) + (req "step" step_encoding)) + +type reason = Conflict_resolved | Invalid_move | Timeout + +let pp_reason ppf reason = + Format.fprintf + ppf + "%s" + (match reason with + | Conflict_resolved -> "conflict resolved" + | Invalid_move -> "invalid move" + | Timeout -> "timeout") + +let reason_encoding = + let open Data_encoding in + union + ~tag_size:`Uint8 + [ + case + ~title:"Conflict_resolved" + (Tag 0) + unit + (function Conflict_resolved -> Some () | _ -> None) + (fun () -> Conflict_resolved); + case + ~title:"Invalid_move" + (Tag 1) + unit + (function Invalid_move -> Some () | _ -> None) + (fun () -> Invalid_move); + case + ~title:"Timeout" + (Tag 2) + unit + (function Timeout -> Some () | _ -> None) + (fun () -> Timeout); + ] + +type status = Ongoing | Ended of (reason * Staker.t) + +let pp_status ppf status = + match status with + | Ongoing -> Format.fprintf ppf "Game ongoing" + | Ended (reason, staker) -> + Format.fprintf + ppf + "Game ended due to %a, %a loses their stake" + pp_reason + reason + Staker.pp + staker + +let status_encoding = + let open Data_encoding in + union + ~tag_size:`Uint8 + [ + case + ~title:"Ongoing" + (Tag 0) + unit + (function Ongoing -> Some () | _ -> None) + (fun () -> Ongoing); + case + ~title:"Ended" + (Tag 1) + (tup2 reason_encoding Staker.encoding) + (function Ended (r, s) -> Some (r, s) | _ -> None) + (fun (r, s) -> Ended (r, s)); + ] + +type outcome = {loser : player; reason : reason} + +let pp_outcome ppf outcome = + Format.fprintf + ppf + "Game outcome: %a - %a has lost.\n" + pp_reason + outcome.reason + pp_player + outcome.loser + +let outcome_encoding = + let open Data_encoding in + conv + (fun {loser; reason} -> (loser, reason)) + (fun (loser, reason) -> {loser; reason}) + (obj2 (req "loser" player_encoding) (req "reason" reason_encoding)) + +(** Checks that the tick count chosen by the current move is one of + the ones in the current dissection. Returns a tuple containing + the current dissection interval (including the two states) between + this tick and the next. *) +let find_choice game tick = + let open Result_syntax in + let rec traverse states = + match states with + | (state, state_tick) :: (next_state, next_tick) :: others -> + if Sc_rollup_tick_repr.(tick = state_tick) then + return (state, tick, next_state, next_tick) + else traverse ((next_state, next_tick) :: others) + | _ -> error () + in + traverse game.dissection + +let check pred = + let open Result_syntax in + if pred then return () else error () + +(** We check firstly that [dissection] is the correct length. It must be + 32 values long, unless the distance between [start_tick] and + [stop_tick] is too small to make this possible, in which case it + should be as long as possible. (If the distance is one we fail + immediately as there is no possible legal dissection). + + Then we check that [dissection] starts at the correct tick and state, + and that it ends at the correct tick and with a different state to + the current dissection. + + Finally, we check that [dissection] is well formed: it has correctly + ordered the ticks, and it contains no [None] states except for + possibly the last one. *) +let check_dissection start start_tick stop stop_tick dissection = + let open Result_syntax in + let len = Z.of_int @@ List.length dissection in + let dist = Sc_rollup_tick_repr.distance start_tick stop_tick in + let* _ = + if Z.(geq dist (of_int 32)) then check Z.(equal len (of_int 32)) + else if Z.(gt dist one) then check Z.(equal len (succ dist)) + else error () + in + let* _ = + match (List.hd dissection, List.last_opt dissection) with + | (Some (a, a_tick), Some (b, b_tick)) -> + check + (Option.equal State_hash.equal a start + && (not (Option.equal State_hash.equal b stop)) + && Sc_rollup_tick_repr.(a_tick = start_tick && b_tick = stop_tick)) + | _ -> error () + in + let rec traverse states = + match states with + | (Some _, tick) :: (next_state, next_tick) :: others -> + if Sc_rollup_tick_repr.(tick < next_tick) then + traverse ((next_state, next_tick) :: others) + else error () + | (None, _) :: _ :: _ -> error () + | _ -> return () + in + traverse dissection + +(** We check firstly that the interval in question is a single tick. + + Then we check the proof begins with the correct state and ends + with a different state to the one in the current dissection. + + Finally, we check that the proof itself is valid. *) +let check_proof start start_tick stop stop_tick proof = + let dist = Sc_rollup_tick_repr.distance start_tick stop_tick in + check + (Z.(equal dist one) + && Option.equal State_hash.equal start (Some (Proof.start proof)) + && (not (Option.equal State_hash.equal stop (Proof.stop proof))) + && Proof.valid proof) + +let play game refutation = + let result = + let open Result_syntax in + let* (start, start_tick, stop, stop_tick) = + find_choice game refutation.choice + in + match refutation.step with + | Dissection states -> + let* _ = check_dissection start start_tick stop stop_tick states in + return + (Either.Right + { + turn = opponent game.turn; + inbox_snapshot = game.inbox_snapshot; + dissection = states; + }) + | Proof proof -> + let* _ = check_proof start start_tick stop stop_tick proof in + return + (Either.Left {loser = opponent game.turn; reason = Conflict_resolved}) + in + match Option.of_result result with + | Some x -> x + | None -> Either.Left {loser = game.turn; reason = Invalid_move} diff --git a/src/proto_alpha/lib_protocol/sc_rollup_game_repr.mli b/src/proto_alpha/lib_protocol/sc_rollup_game_repr.mli new file mode 100644 index 000000000000..701e3043fa65 --- /dev/null +++ b/src/proto_alpha/lib_protocol/sc_rollup_game_repr.mli @@ -0,0 +1,312 @@ +(*****************************************************************************) +(* *) +(* Open Source License *) +(* Copyright (c) 2021 Nomadic Labs *) +(* Copyright (c) 2022 Trili Tech, *) +(* *) +(* 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. *) +(* *) +(*****************************************************************************) + +(** The smart contract rollup refutation game types are defined here, as + well as the basic pure logic for: + + - how to create a new game from a pair of commits in the commit tree + + - how to update a game or complete a game when a move is played. + + This game logic is used by the protocol when two commitments are in + conflict to determine which one of the commitments is wrong. + + Game state and moves + ==================== + + At any given moment, the game stores a list [dissection] of state + hashes and tick counts. These are the claims made by the player who + has just moved about the PVM history. + + The next player to move will specify a tick count which appears in + the [dissection]; this is the last of the state hashes which she + agrees with. She will then either: + + - provide a new [dissection] by giving a list of state hashes and + tick counts that starts at the chosen tick count and ends at the + next tick count in the previous [dissection]. It must agree at the + start but disagree with the final state. + + - if the tick difference between this state and the next is one, + there is no 'room' for a new [dissection]. In this case she must + provide a Merkle proof that shows the step in the current + [dissection] is invalid. + + Initializing a game + =================== + + The game begins when the refuter plays a first move. + + At this point the [initial] function will be called to convert the + defender's commitment into an initial [dissection]. The first move + is immediately applied to this to give the first state of the game. + + Note: it is quite possible for the game to end immediately after + this first move, either if the commitment has a tick count of one or + more probably if the refutation proves that the commitment was + 'premature' (the state is not blocked---there are further + computation steps to do or more inbox messages to read). + + Expected properties + =================== + + P1 - If [dissection] is honest, the next move must be dishonest: + + There is only one honest state hash for a given tick count. The + next player must provide a different hash to the honest hash in + the [dissection]. + + P2 - If [dissection] is dishonest, there is a strategy for a player + equipped with a perfect PVM to play an honest next move: + + The player with a perfect PVM can calculate honest hashes until + one disagrees with the [dissection], and challenge the dissection + at that point, publishing either an honest [dissection] or an + honest [Proof]. + + Each [dissection] has a maximum tick count step shorter than the + last, so by induction using P1 and P2 we have + + P1' - If [dissection] is honest, the last player has a winning + strategy. + + P2' - If [dissection] is dishonest, the next player has a winning + strategy. + + This allows us to see that + + Honest refuter wins: + An honest refuter will be refuting a dishonest commitment, because + there is only one honest state possible per level. Therefore the + initial [dissection] will be dishonest. By P2' the refuter has a + winning strategy. + + Honest defender wins: + An honest defender will have made an honest commitment which will + be translated into an honest initial [dissection]. By P1' the + defender has a winning strategy. + + In the case that both players are dishonest the outcome is slightly + harder to determine---it basically comes down to which player gets + to play the final move. That means that in this case the refutation + game is equivalent to a finite game of nim (by the Sprague-Grundy + Theorem, see en.wikipedia.org/wiki/Sprague-Grundy_theorem). However, + this fact is completely irrelevant, because if the rollup is working + correctly the winner will subsequently be defeated by some honest + opponent. +*) + +open Sc_rollup_repr + +(** Currently, [Proof] is a dummy type with the structure we need, set + up to allow testing. *) +module Proof : sig + (** There are three cases for a refutation game proof: + + [Computation_step]: a simple step in the PVM that doesn't involve + any interaction with the inbox. + + [Input_step]: a step in which the PVM 'reads' from the inbox. This + will include a proof that the machine is in a blocked state and a + proof that the next message to be read is correct. The inbox proof + part of this will refer to the [inbox_snapshot] stored in the game + type. + + [Blocked_step]: similar to an input step, this is a step where the + machine is in a blocked state. However, it includes a proof that + there are no further messages in the inbox at the current level. + This means the machine is genuinely blocked and the [stop] state + of this [Proof] will be [None]. *) + type t = + | Computation_step of { + valid : bool; + start : State_hash.t; + stop : State_hash.t; + } + | Input_step of {valid : bool; start : State_hash.t; stop : State_hash.t} + | Blocked_step of {valid : bool; start : State_hash.t} + + val encoding : t Data_encoding.t + + val pp : Format.formatter -> t -> unit + + (** The state hash of the machine before the step. *) + val start : t -> State_hash.t + + (** The state hash of the machine after the step. *) + val stop : t -> State_hash.t option + + (** Check the validity of a proof *) + val valid : t -> bool +end + +(** The two stakers index the game in the storage, as an ordered pair + of public key hashes. We use [Alice] and [Bob] to represent the + first and second player respectively. *) +type player = Alice | Bob + +(** + A game state is characterized by: + + - [turn], the player that must provide the next move. + + - [inbox_snapshot], a snapshot of the inbox state at the moment the + game is created. This is only used when checking [Input_step] and + [Blocked_step] proofs; it makes the proofs easier to create--- + otherwise they would have a 'moving target' because the actual + inbox may be updated continuously. + + - [dissection], a list of states with tick counts. The current + player will specify, in the next move, a tick count that + indicates the last of these states that she agrees with. + + Invariants: + ----------- + - [dissection] must contain at least 3 values + - only the last value in [dissection] may be [None] + - [inbox_snapshot] never changes once the game is created +*) +type t = { + turn : player; + inbox_snapshot : Sc_rollup_inbox_repr.t; + dissection : (State_hash.t option * Sc_rollup_tick_repr.t) list; +} + +(** Return the other player *) +val opponent : player -> player + +val encoding : t Data_encoding.t + +val pp : Format.formatter -> t -> unit + +module Index : sig + type t = Staker.t * Staker.t + + val to_path : t -> string list -> string list + + val of_path : string list -> t option + + val path_length : int + + val rpc_arg : t RPC_arg.t + + val encoding : t Data_encoding.t + + val compare : t -> t -> int + + (** The 'normal form' for indices is when the two stakers are + ordered (we just use [Staker.compare]). *) + val normalize : t -> t + + (** Given an index in normal form, resolve a given [player] ([Alice] + or [Bob]) to the actual staker they represent. *) + val staker : Staker.t * Staker.t -> player -> Staker.t +end + +(** To begin a game, first the conflict point in the commit tree is + found, and then this function is applied. + + [initial inbox parent child refuter defender] will construct an + initial game where [refuter] is next to play. The game has + [dissection] with three states: + + - firstly, the state (with tick zero) of [parent], the commitment + that both stakers agree on. + + - secondly, the state and tick count of [child], the commitment + that [defender] has staked on. + + - thirdly, a [None] state which is a single tick after the [child] + commitment. This represents the claim, implicit in the commitment, + that the state given is blocked. + + This gives [refuter] a binary choice: she can refute the commit + itself by providing a new dissection between the two committed + states, or she can refute the claim that the [child] commit is a + blocked state by immediately providing a proof of a single tick + increment from that state to its successor. *) +val initial : + Sc_rollup_inbox_repr.t -> + parent:Commitment.t -> + child:Commitment.t -> + refuter:Staker.t -> + defender:Staker.t -> + t + +(** A [step] in the game is either a new dissection (if there are + intermediate ticks remaining to put in it) or a proof. *) +type step = + | Dissection of (State_hash.t option * Sc_rollup_tick_repr.t) list + | Proof of Proof.t + +(** A [refutation] is a move in the game. [choice] is the final tick + in the current dissection at which the two players agree. *) +type refutation = {choice : Sc_rollup_tick_repr.t; step : step} + +val pp_refutation : Format.formatter -> refutation -> unit + +val refutation_encoding : refutation Data_encoding.t + +(** A game ends for one of three reasons: the conflict has been + resolved via a proof, a player has been timed out, or a player has + forfeited because of attempting to make an invalid move. *) +type reason = Conflict_resolved | Invalid_move | Timeout + +val pp_reason : Format.formatter -> reason -> unit + +val reason_encoding : reason Data_encoding.t + +(** A type that represents the current game status in a way that is + useful to the outside world (using actual [Staker.t] values + instead of the internal [player] type). + + The [Staker.t] in the [Ended] case is the loser of the game: the + staker who will have their stake slashed. + + Used in operation result types. *) +type status = Ongoing | Ended of (reason * Staker.t) + +val pp_status : Format.formatter -> status -> unit + +val status_encoding : status Data_encoding.t + +(** A game ends with a single [loser] and the [reason] for the game + ending. This type is 'internal' to the game logic, it uses + [Alice] or [Bob] to refer to the players without knowing which + stakers they are. *) +type outcome = {loser : player; reason : reason} + +val pp_outcome : Format.formatter -> outcome -> unit + +val outcome_encoding : outcome Data_encoding.t + +(** Applies the move [refutation] to the game. Checks the move is + valid and returns an [Invalid_move] outcome if not. + + In the case of the game continuing, this swaps the current + player and updates the [dissection]. In the case of a [Proof] + being provided this returns a [Conflict_resolved] outcome. *) +val play : t -> refutation -> (outcome, t) Either.t diff --git a/src/proto_alpha/lib_protocol/sc_rollup_tick_repr.ml b/src/proto_alpha/lib_protocol/sc_rollup_tick_repr.ml index e16ac07c9134..22156b964fed 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_tick_repr.ml +++ b/src/proto_alpha/lib_protocol/sc_rollup_tick_repr.ml @@ -40,6 +40,9 @@ let of_int x = if Compare.Int.(x < 0) then None else Some (Z.of_int x) let to_int x = if Z.fits_int x then Some (Z.to_int x) else None +let of_number_of_ticks x = + Z.of_int (Int32.to_int (Sc_rollup_repr.Number_of_ticks.to_int32 x)) + let ( <= ) = leq let ( < ) = lt diff --git a/src/proto_alpha/lib_protocol/sc_rollup_tick_repr.mli b/src/proto_alpha/lib_protocol/sc_rollup_tick_repr.mli index 9a91554a65f6..feb5c90eb63f 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_tick_repr.mli +++ b/src/proto_alpha/lib_protocol/sc_rollup_tick_repr.mli @@ -46,6 +46,13 @@ val of_int : int -> t option (** [to_int tick] converts the [tick] into an integer. *) val to_int : t -> int option +(** [of_number_of_ticks] converts from the bounded int type defined in + the [Sc_rollup_repr] module. [Number_of_ticks] is used inside of + commitments to limit the maximum possible storage requirement. It is + bounded between one and [max_int] meaning that this can never return + a negative number so an [option] isn't required. *) +val of_number_of_ticks : Sc_rollup_repr.Number_of_ticks.t -> t + val encoding : t Data_encoding.t val pp : Format.formatter -> t -> unit -- GitLab From 67f49a0f31fac35f08688179042f9680ff7fba6d Mon Sep 17 00:00:00 2001 From: Thomas Athorne Date: Thu, 28 Apr 2022 11:52:36 +0100 Subject: [PATCH 2/4] Proto: SCORU: add storage for refutation game --- .../lib_protocol/alpha_context.mli | 28 ++++ .../lib_protocol/sc_rollup_storage.ml | 132 ++++++++++++++++-- .../lib_protocol/sc_rollup_storage.mli | 116 ++++++++++++++- src/proto_alpha/lib_protocol/storage.ml | 26 ++++ src/proto_alpha/lib_protocol/storage.mli | 21 +++ 5 files changed, 302 insertions(+), 21 deletions(-) diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 4c8cda50ea2c..389ca397bd32 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2693,6 +2693,34 @@ module Sc_rollup : sig val last_cemented_commitment_hash_with_level : context -> t -> (Commitment_hash.t * Raw_level.t * context) tzresult Lwt.t + val get_or_init_game : + context -> + t -> + refuter:Staker.t -> + defender:Staker.t -> + (Game.t * context) tzresult Lwt.t + + val update_game : + context -> + t -> + refuter:Staker.t -> + defender:Staker.t -> + Game.refutation -> + (Game.outcome option * context) tzresult Lwt.t + + val apply_outcome : + context -> + t -> + Staker.t * Staker.t -> + Game.outcome -> + (Game.status * context) tzresult Lwt.t + + val timeout : + context -> + t -> + Staker.t * Staker.t -> + (Game.outcome * context) tzresult Lwt.t + module Internal_for_tests : sig val originated_sc_rollup : Origination_nonce.Internal_for_tests.t -> t end diff --git a/src/proto_alpha/lib_protocol/sc_rollup_storage.ml b/src/proto_alpha/lib_protocol/sc_rollup_storage.ml index 6c4d4dcec683..78f4a182a72d 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_storage.ml +++ b/src/proto_alpha/lib_protocol/sc_rollup_storage.ml @@ -42,6 +42,9 @@ type error += Sc_rollup_repr.Commitment_hash.t | (* `Temporary *) Sc_rollup_bad_inbox_level | (* `Temporary *) Sc_rollup_max_number_of_available_messages_reached + | (* `Temporary *) Sc_rollup_wrong_turn + | (* `Temporary *) Sc_rollup_no_game + | (* `Temporary *) Sc_rollup_timeout_level_not_reached let () = register_error_kind @@ -54,6 +57,30 @@ let () = | Sc_rollup_max_number_of_available_messages_reached -> Some () | _ -> None) (fun () -> Sc_rollup_max_number_of_available_messages_reached) ; + register_error_kind + `Temporary + ~id:"Sc_rollup_timeout_level_not_reached" + ~title:"Attempt to timeout game too early" + ~description:"Attempt to timeout game too early" + Data_encoding.unit + (function Sc_rollup_timeout_level_not_reached -> Some () | _ -> None) + (fun () -> Sc_rollup_timeout_level_not_reached) ; + register_error_kind + `Temporary + ~id:"Sc_rollup_no_game" + ~title:"Refutation game does not exist" + ~description:"Refutation game does not exist" + Data_encoding.unit + (function Sc_rollup_no_game -> Some () | _ -> None) + (fun () -> Sc_rollup_no_game) ; + register_error_kind + `Temporary + ~id:"Sc_rollup_wrong_turn" + ~title:"Attempt to play move but not staker's turn" + ~description:"Attempt to play move but not staker's turn" + Data_encoding.unit + (function Sc_rollup_wrong_turn -> Some () | _ -> None) + (fun () -> Sc_rollup_wrong_turn) ; let description = "Already staked." in register_error_kind `Temporary @@ -223,15 +250,12 @@ let originate ctxt ~kind ~boot_sector = Raw_context.increment_origination_nonce ctxt >>?= fun (ctxt, nonce) -> let level = Raw_context.current_level ctxt in Sc_rollup_repr.Address.from_nonce nonce >>?= fun address -> - Storage.Sc_rollup.PVM_kind.add ctxt address kind >>= fun ctxt -> - Storage.Sc_rollup.Initial_level.add - ctxt - address - (Level_storage.current ctxt).level + Store.PVM_kind.add ctxt address kind >>= fun ctxt -> + Store.Initial_level.add ctxt address (Level_storage.current ctxt).level >>= fun ctxt -> - Storage.Sc_rollup.Boot_sector.add ctxt address boot_sector >>= fun ctxt -> + Store.Boot_sector.add ctxt address boot_sector >>= fun ctxt -> let inbox = Sc_rollup_inbox_repr.empty address level.level in - Storage.Sc_rollup.Inbox.init ctxt address inbox >>=? fun (ctxt, size_diff) -> + Store.Inbox.init ctxt address inbox >>=? fun (ctxt, size_diff) -> Store.Last_cemented_commitment.init ctxt address Commitment_hash.zero >>=? fun (ctxt, lcc_size_diff) -> Store.Staker_count.init ctxt address 0l >>=? fun (ctxt, stakers_size_diff) -> @@ -248,7 +272,7 @@ let originate ctxt ~kind ~boot_sector = in return (address, size, ctxt) -let kind ctxt address = Storage.Sc_rollup.PVM_kind.find ctxt address +let kind ctxt address = Store.PVM_kind.find ctxt address let last_cemented_commitment ctxt rollup = let open Lwt_tzresult_syntax in @@ -260,17 +284,17 @@ let last_cemented_commitment ctxt rollup = (** Try to consume n messages. *) let consume_n_messages ctxt rollup n = let open Lwt_tzresult_syntax in - let* (ctxt, inbox) = Storage.Sc_rollup.Inbox.get ctxt rollup in + let* (ctxt, inbox) = Store.Inbox.get ctxt rollup in Sc_rollup_inbox_repr.consume_n_messages n inbox >>?= function | None -> return ctxt | Some inbox -> - let* (ctxt, size) = Storage.Sc_rollup.Inbox.update ctxt rollup inbox in + let* (ctxt, size) = Store.Inbox.update ctxt rollup inbox in assert (Compare.Int.(size <= 0)) ; return ctxt let inbox ctxt rollup = let open Lwt_tzresult_syntax in - let* (ctxt, res) = Storage.Sc_rollup.Inbox.find ctxt rollup in + let* (ctxt, res) = Store.Inbox.find ctxt rollup in match res with | None -> fail (Sc_rollup_does_not_exist rollup) | Some inbox -> return (inbox, ctxt) @@ -336,7 +360,7 @@ let add_messages ctxt rollup messages = let*? ctxt = Sc_rollup_in_memory_inbox.set_current_messages ctxt rollup current_messages in - Storage.Sc_rollup.Inbox.update ctxt rollup inbox >>=? fun (ctxt, size) -> + let* (ctxt, size) = Store.Inbox.update ctxt rollup inbox in return (inbox, Z.of_int size, ctxt) (* This function is called in other functions in the module only after they have @@ -759,11 +783,11 @@ let remove_staker ctxt rollup staker = in go staked_on ctxt -let list ctxt = Storage.Sc_rollup.PVM_kind.keys ctxt >|= Result.return +let list ctxt = Store.PVM_kind.keys ctxt >|= Result.return let initial_level ctxt rollup = let open Lwt_tzresult_syntax in - let* level = Storage.Sc_rollup.Initial_level.find ctxt rollup in + let* level = Store.Initial_level.find ctxt rollup in match level with | None -> fail (Sc_rollup_does_not_exist rollup) | Some level -> return level @@ -786,3 +810,83 @@ let last_cemented_commitment_hash_with_level ctxt rollup = get_commitment_internal ctxt rollup commitment_hash in (commitment_hash, inbox_level, ctxt) + +(** TODO #2902: replace with protocol constant and consider good value. *) +let timeout_period = 500 + +let timeout_level ctxt = + let level = Raw_context.current_level ctxt in + Raw_level_repr.add level.level timeout_period + +let get_or_init_game ctxt rollup ~refuter ~defender = + let open Lwt_tzresult_syntax in + let stakers = Sc_rollup_game_repr.Index.normalize (refuter, defender) in + let* (ctxt, game) = Store.Game.find (ctxt, rollup) stakers in + match game with + | Some g -> return (g, ctxt) + | None -> + let* ((_, child), ctxt) = + get_conflict_point ctxt rollup refuter defender + in + let* (child, ctxt) = get_commitment_internal ctxt rollup child in + let* (parent, ctxt) = + get_commitment_internal ctxt rollup child.predecessor + in + let* (ctxt, inbox) = Store.Inbox.get ctxt rollup in + let game = + Sc_rollup_game_repr.initial inbox ~parent ~child ~refuter ~defender + in + let* (ctxt, _) = Store.Game.init (ctxt, rollup) stakers game in + let* (ctxt, _) = + Store.Game_timeout.init (ctxt, rollup) stakers (timeout_level ctxt) + in + return (game, ctxt) + +let update_game ctxt rollup ~refuter ~defender refutation = + let open Lwt_tzresult_syntax in + let (alice, bob) = Sc_rollup_game_repr.Index.normalize (refuter, defender) in + let* (game, ctxt) = get_or_init_game ctxt rollup ~refuter ~defender in + let* _ = + match game.turn with + | Alice -> + if Sc_rollup_repr.Staker.equal alice refuter then return () + else fail Sc_rollup_wrong_turn + | Bob -> + if Sc_rollup_repr.Staker.equal bob refuter then return () + else fail Sc_rollup_wrong_turn + in + match Sc_rollup_game_repr.play game refutation with + | Either.Left outcome -> return (Some outcome, ctxt) + | Either.Right new_game -> + let* (ctxt, _) = Store.Game.update (ctxt, rollup) (alice, bob) new_game in + let* (ctxt, _) = + Store.Game_timeout.update + (ctxt, rollup) + (alice, bob) + (timeout_level ctxt) + in + return (None, ctxt) + +let apply_outcome ctxt rollup stakers (outcome : Sc_rollup_game_repr.outcome) = + let open Lwt_tzresult_syntax in + let (alice, bob) = Sc_rollup_game_repr.Index.normalize stakers in + let losing_staker = Sc_rollup_game_repr.Index.staker stakers outcome.loser in + let* ctxt = remove_staker ctxt rollup losing_staker in + let* (ctxt, _, _) = Store.Game.remove (ctxt, rollup) (alice, bob) in + let* (ctxt, _, _) = Store.Game_timeout.remove (ctxt, rollup) (alice, bob) in + return (Sc_rollup_game_repr.Ended (outcome.reason, losing_staker), ctxt) + +let timeout ctxt rollup stakers = + let open Lwt_tzresult_syntax in + let level = (Raw_context.current_level ctxt).level in + let (alice, bob) = Sc_rollup_game_repr.Index.normalize stakers in + let* (ctxt, game) = Store.Game.find (ctxt, rollup) (alice, bob) in + match game with + | None -> fail Sc_rollup_no_game + | Some game -> + let* (ctxt, timeout_level) = + Store.Game_timeout.get (ctxt, rollup) (alice, bob) + in + if Raw_level_repr.(level > timeout_level) then + return (Sc_rollup_game_repr.{loser = game.turn; reason = Timeout}, ctxt) + else fail Sc_rollup_timeout_level_not_reached diff --git a/src/proto_alpha/lib_protocol/sc_rollup_storage.mli b/src/proto_alpha/lib_protocol/sc_rollup_storage.mli index a2a8697e9754..c2034064f6fc 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_storage.mli +++ b/src/proto_alpha/lib_protocol/sc_rollup_storage.mli @@ -142,11 +142,11 @@ type error += Sc_rollup_bad_inbox_level (** Module [Internal] implements functions that are used only internally by - the [Sc_rollup_storage] module, but need to be exposed in tests or - benchmarks. + the [Sc_rollup_storage] module, but need to be exposed in tests or + benchmarks. *) module Internal : sig - (** [update_num_and_size_of_messages ~num_messages ~total_messages_size + (** [update_num_and_size_of_messages ~num_messages ~total_messages_size message] returns the length and total messages size [messages]. *) val update_num_and_size_of_messages : @@ -365,7 +365,9 @@ type conflict_point = May fail with: {ul {li [Sc_rollup_does_not_exist] if [rollup] does not exist} - {li [Sc_rollup_no_conflict] if [staker1] is staked on an ancestor of the commitment staked on by [staker2], or vice versa} + {li [Sc_rollup_no_conflict] if [staker1] is staked on an ancestor of the + commitment staked on by [staker2], or vice versa} + {li [Sc_rollup_not_staked] if one of the stakers is not staked} } *) val get_conflict_point : Raw_context.t -> @@ -418,9 +420,9 @@ val initial_level : val get_boot_sector : Raw_context.t -> Sc_rollup_repr.t -> string tzresult Lwt.t (* [last_cemented_commitment_hash_with_level ctxt sc_rollup] returns the hash - and level of the last cemented commitment (lcc) for [sc_rollup]. If the - rollup exists but no lcc exists, the initial commitment - `Sc_rollup.Commitment.zero` together with the rollup origination level is + and level of the last cemented commitment (lcc) for [sc_rollup]. If the + rollup exists but no lcc exists, the initial commitment + `Sc_rollup.Commitment.zero` together with the rollup origination level is returned. May fail with: @@ -431,3 +433,103 @@ val last_cemented_commitment_hash_with_level : Sc_rollup_repr.t -> (Sc_rollup_repr.Commitment_hash.t * Raw_level_repr.t * Raw_context.t) tzresult Lwt.t + +(** [get_or_init_game ctxt rollup refuter defender] will simply return + the current game between the two stakers [refuter] and [defender] if + it exists. + + If it does not already exist, it will be created with [refuter] as the + first player to move. The initial state of the game will be obtained + from the commitment pair belonging to [defender] at the conflict + point. See [Sc_rollup_game_repr.initial] for documentation on how a + pair of commitments is turned into an initial game state. + + May fail with: + {ul + {li [Sc_rollup_does_not_exist] if [rollup] does not exist} + {li [Sc_rollup_no_conflict] if [refuter] is staked on an ancestor of + the commitment staked on by [defender], or vice versa} + {li [Sc_rollup_not_staked] if one of the [refuter] or [defender] is + not actually staked} + } *) +val get_or_init_game : + Raw_context.t -> + Sc_rollup_repr.t -> + refuter:Sc_rollup_repr.Staker.t -> + defender:Sc_rollup_repr.Staker.t -> + (Sc_rollup_game_repr.t * Raw_context.t) tzresult Lwt.t + +(** [update_game ctxt rollup refuter defender refutation] handles the + storage-side logic for when one of the players makes a move in the + game. It initializes the game if necessary (the first move looks + much like any other). It checks that [refuter] is the player whose + turn it is; if so, it applies [refutation] using the [play]. + + If the result is a new game, this is stored and the timeout level is + updated. + + If the result is an [outcome], this will be returned. + + May fail with: + {ul + {li [Sc_rollup_does_not_exist] if [rollup] does not exist} + {li [Sc_rollup_no_conflict] if [refuter] is staked on an ancestor of + the commitment staked on by [defender], or vice versa} + {li [Sc_rollup_not_staked] if one of the [refuter] or [defender] is + not actually staked} + {li [Sc_rollup_wrong_turn] if a player is trying to move out of + turn} + } *) +val update_game : + Raw_context.t -> + Sc_rollup_repr.t -> + refuter:Sc_rollup_repr.Staker.t -> + defender:Sc_rollup_repr.Staker.t -> + Sc_rollup_game_repr.refutation -> + (Sc_rollup_game_repr.outcome option * Raw_context.t) tzresult Lwt.t + +(** [apply_outcome ctxt rollup outcome] will take an [outcome] produced + by [timeout] or [update_game] and perform the necessary end-of-game + cleanup: remove the game itself from the store and punish the losing + player by removing their stake. + + It also translates the 'internal' type to represent the end of the + game, [outcome], into the [status] type that makes sense to the + outside world. + + This is mostly just calling [remove_staker], so it can fail with the + same errors as that. However, if it is called on an [outcome] + generated by [update_game] or [timeout] it should not fail unless + something has gone wrong. + + Note: this function takes the two stakers as a pair rather than + separate arguments. This reflects the fact that for this function + the two players are symmetric. This function will normalize the + order of the players if necessary to create a valid game index. *) +val apply_outcome : + Raw_context.t -> + Sc_rollup_repr.t -> + Sc_rollup_repr.Staker.t * Sc_rollup_repr.Staker.t -> + Sc_rollup_game_repr.outcome -> + (Sc_rollup_game_repr.status * Raw_context.t) tzresult Lwt.t + +(** [timeout ctxt rollup stakers] will check that the timeout has + elapsed and if so return a game outcome that punishes whichever + of [stakers] is supposed to have played a move. + + May fail with: + {ul + {li [Sc_rollup_no_game] if the game does not in fact exist} + {li [Sc_rollup_timeout_level_not_reached] if the player still has + time in which to play} + } + + Note: this function takes the two stakers as a pair rather than + separate arguments. This reflects the fact that for this function + the two players are symmetric. This function will normalize the + order of the players if necessary to create a valid game index. *) +val timeout : + Raw_context.t -> + Sc_rollup_repr.t -> + Sc_rollup_repr.Staker.t * Sc_rollup_repr.Staker.t -> + (Sc_rollup_game_repr.outcome * Raw_context.t) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index 68e37758c40e..cc3dd73537b4 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -1600,6 +1600,32 @@ module Sc_rollup = struct (struct type t = Raw_level_repr.t + let encoding = Raw_level_repr.encoding + end) + + module Game = + Make_indexed_carbonated_data_storage + (Make_subcontext (Registered) (Indexed_context.Raw_context) + (struct + let name = ["game"] + end)) + (Make_index (Sc_rollup_game_repr.Index)) + (struct + type t = Sc_rollup_game_repr.t + + let encoding = Sc_rollup_game_repr.encoding + end) + + module Game_timeout = + Make_indexed_carbonated_data_storage + (Make_subcontext (Registered) (Indexed_context.Raw_context) + (struct + let name = ["game_timeout"] + end)) + (Make_index (Sc_rollup_game_repr.Index)) + (struct + type t = Raw_level_repr.t + let encoding = Raw_level_repr.encoding end) end diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index 940de30ebd4e..d853151ecae1 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -764,4 +764,25 @@ module Sc_rollup : sig with type key = Sc_rollup_repr.Commitment_hash.t and type value = Raw_level_repr.t and type t = Raw_context.t * Sc_rollup_repr.t + + (** Refutation games are indexed by the rollup and the pair of + competing stakers. The staker pair should always be ordered to + ensure that games are not duplicated. + *) + module Game : + Non_iterable_indexed_carbonated_data_storage + with type key = Sc_rollup_game_repr.Index.t + and type value = Sc_rollup_game_repr.t + and type t = Raw_context.t * Sc_rollup_repr.t + + (** [Game_timeout] stores the block level at which the staker whose + turn it is to move will (become vulnerable to) timeout. The staker + pair should always be ordered to ensure that this value is not + duplicated. + *) + module Game_timeout : + Non_iterable_indexed_carbonated_data_storage + with type key = Sc_rollup_game_repr.Index.t + and type value = Raw_level_repr.t + and type t = Raw_context.t * Sc_rollup_repr.t end -- GitLab From 1c9fc148db39657a64b0bb164005a6405e4a6efc Mon Sep 17 00:00:00 2001 From: Thomas Athorne Date: Thu, 28 Apr 2022 12:00:05 +0100 Subject: [PATCH 3/4] Proto: SCORU: add L1 operations for refutation game --- src/proto_alpha/lib_client/injection.ml | 6 + .../lib_client/operation_result.ml | 70 + .../lib_protocol/alpha_context.mli | 25 + src/proto_alpha/lib_protocol/apply.ml | 26 +- src/proto_alpha/lib_protocol/apply_results.ml | 133 + .../lib_protocol/apply_results.mli | 10 + .../lib_protocol/operation_repr.ml | 83 + .../lib_protocol/operation_repr.mli | 25 + .../lib_protocol/test/helpers/block.ml | 7 +- .../_regressions/rpc/alpha.client.mempool.out | 2780 +++++++++++------ tezt/_regressions/rpc/alpha.proxy.mempool.out | 2780 +++++++++++------ 11 files changed, 4142 insertions(+), 1803 deletions(-) diff --git a/src/proto_alpha/lib_client/injection.ml b/src/proto_alpha/lib_client/injection.ml index 6606d7cf23d9..305c77ab229e 100644 --- a/src/proto_alpha/lib_client/injection.ml +++ b/src/proto_alpha/lib_client/injection.ml @@ -335,6 +335,8 @@ let estimated_gas_single (type kind) Ok consumed_gas | Applied (Sc_rollup_cement_result {consumed_gas; _}) -> Ok consumed_gas | Applied (Sc_rollup_publish_result {consumed_gas; _}) -> Ok consumed_gas + | Applied (Sc_rollup_refute_result {consumed_gas; _}) -> Ok consumed_gas + | Applied (Sc_rollup_timeout_result {consumed_gas; _}) -> Ok consumed_gas | Skipped _ -> Ok Gas.Arith.zero (* there must be another error for this to happen *) | Backtracked (_, None) -> @@ -397,6 +399,8 @@ let estimated_storage_single (type kind) ~tx_rollup_origination_size *) | Applied (Sc_rollup_cement_result _) -> Ok Z.zero | Applied (Sc_rollup_publish_result _) -> Ok Z.zero + | Applied (Sc_rollup_refute_result _) -> Ok Z.zero + | Applied (Sc_rollup_timeout_result _) -> Ok Z.zero | Skipped _ -> Ok Z.zero (* there must be another error for this to happen *) | Backtracked (_, None) -> @@ -460,6 +464,8 @@ let originated_contracts_single (type kind) | Applied (Sc_rollup_add_messages_result _) -> Ok [] | Applied (Sc_rollup_cement_result _) -> Ok [] | Applied (Sc_rollup_publish_result _) -> Ok [] + | Applied (Sc_rollup_refute_result _) -> Ok [] + | Applied (Sc_rollup_timeout_result _) -> Ok [] | Skipped _ -> Ok [] (* there must be another error for this to happen *) | Backtracked (_, None) -> Ok [] (* there must be another error for this to happen *) diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index 235beabdf9a9..279d8c173f56 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -313,6 +313,32 @@ let pp_manager_operation_content (type kind) source internal pp_result ppf Sc_rollup.Address.pp rollup pp_result + result + | Sc_rollup_refute {rollup; opponent; refutation} -> + Format.fprintf + ppf + "@[Refute staker %a in the smart contract rollup at address %a \ + using refutation %a%a@]" + Sc_rollup.Staker.pp + opponent + Sc_rollup.Address.pp + rollup + Sc_rollup.Game.pp_refutation + refutation + pp_result + result + | Sc_rollup_timeout {rollup; stakers} -> + Format.fprintf + ppf + "@[Punish one of the two stakers %a and %a by timeout in the smart \ + contract rollup at address %a%a@]" + Sc_rollup.Staker.pp + (fst stakers) + Sc_rollup.Staker.pp + (snd stakers) + Sc_rollup.Address.pp + rollup + pp_result result) ; Format.fprintf ppf "@]" @@ -668,6 +694,24 @@ let pp_manager_operation_contents_and_result ppf Sc_rollup.Commitment_hash.pp staked_hash in + let pp_sc_rollup_refute_result + (Sc_rollup_refute_result {consumed_gas; status}) = + Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas ; + Format.fprintf + ppf + "@,Refutation game status: %a" + Sc_rollup.Game.pp_status + status + in + let pp_sc_rollup_timeout_result + (Sc_rollup_timeout_result {consumed_gas; status}) = + Format.fprintf ppf "@,Consumed gas: %a" Gas.Arith.pp consumed_gas ; + Format.fprintf + ppf + "@,Refutation game status: %a" + Sc_rollup.Game.pp_status + status + in let pp_result (type kind) ppf (result : kind manager_operation_result) = Format.fprintf ppf "@," ; match result with @@ -876,6 +920,32 @@ let pp_manager_operation_contents_and_result ppf rollup was BACKTRACKED, its expected effects (as follow) were NOT \ applied.@]" ; pp_sc_rollup_publish_result op + | Applied (Sc_rollup_refute_result _ as op) -> + Format.fprintf + ppf + "This operation playing a refutation game step on a smart contract \ + rollup was successfully applied" ; + pp_sc_rollup_refute_result op + | Backtracked ((Sc_rollup_refute_result _ as op), _errs) -> + Format.fprintf + ppf + "@[This operation playing a refutation game step on a smart \ + contract rollup was BACKTRACKED, its expected effects (as follow) \ + were NOT applied.@]" ; + pp_sc_rollup_refute_result op + | Applied (Sc_rollup_timeout_result _ as op) -> + Format.fprintf + ppf + "This operation to end a refutation game on a smart contract rollup \ + by timeout was successfully applied" ; + pp_sc_rollup_timeout_result op + | Backtracked ((Sc_rollup_timeout_result _ as op), _errs) -> + Format.fprintf + ppf + "@[This operation to end a refutation game on a smart contract \ + rollup by timeout was BACKTRACKED, its expected effects (as follow) \ + were NOT applied.@]" ; + pp_sc_rollup_timeout_result op in Format.fprintf diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 389ca397bd32..175ebf15a967 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2908,6 +2908,10 @@ module Kind : sig type sc_rollup_publish = Sc_rollup_publish_kind + type sc_rollup_refute = Sc_rollup_refute_kind + + type sc_rollup_timeout = Sc_rollup_timeout_kind + type 'a manager = | Reveal_manager_kind : reveal manager | Transaction_manager_kind : transaction manager @@ -2931,6 +2935,8 @@ module Kind : sig | Sc_rollup_add_messages_manager_kind : sc_rollup_add_messages manager | Sc_rollup_cement_manager_kind : sc_rollup_cement manager | Sc_rollup_publish_manager_kind : sc_rollup_publish manager + | Sc_rollup_refute_manager_kind : sc_rollup_refute manager + | Sc_rollup_timeout_manager_kind : sc_rollup_timeout manager end type 'a consensus_operation_type = @@ -3123,6 +3129,17 @@ and _ manager_operation = commitment : Sc_rollup.Commitment.t; } -> Kind.sc_rollup_publish manager_operation + | Sc_rollup_refute : { + rollup : Sc_rollup.t; + opponent : Sc_rollup.Staker.t; + refutation : Sc_rollup.Game.refutation; + } + -> Kind.sc_rollup_refute manager_operation + | Sc_rollup_timeout : { + rollup : Sc_rollup.t; + stakers : Sc_rollup.Staker.t * Sc_rollup.Staker.t; + } + -> Kind.sc_rollup_timeout manager_operation and counter = Z.t @@ -3284,6 +3301,10 @@ module Operation : sig val sc_rollup_publish_case : Kind.sc_rollup_publish Kind.manager case + val sc_rollup_refute_case : Kind.sc_rollup_refute Kind.manager case + + val sc_rollup_timeout_case : Kind.sc_rollup_timeout Kind.manager case + module Manager_operations : sig type 'b case = | MCase : { @@ -3341,6 +3362,10 @@ module Operation : sig val sc_rollup_cement_case : Kind.sc_rollup_cement case val sc_rollup_publish_case : Kind.sc_rollup_publish case + + val sc_rollup_refute_case : Kind.sc_rollup_refute case + + val sc_rollup_timeout_case : Kind.sc_rollup_timeout case end end diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index 115afe06eda1..ce9d8c8b33d3 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -1760,6 +1760,28 @@ let apply_external_manager_operation_content : let consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt in let result = Sc_rollup_publish_result {staked_hash; consumed_gas} in return (ctxt, result, []) + | Sc_rollup_refute {rollup; opponent; refutation} -> + Sc_rollup.update_game + ctxt + rollup + ~refuter:source + ~defender:opponent + refutation + >>=? fun (outcome, ctxt) -> + (match outcome with + | None -> return (Sc_rollup.Game.Ongoing, ctxt) + | Some o -> Sc_rollup.apply_outcome ctxt rollup (source, opponent) o) + >>=? fun (status, ctxt) -> + let consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt in + let result = Sc_rollup_refute_result {status; consumed_gas} in + return (ctxt, result, []) + | Sc_rollup_timeout {rollup; stakers} -> + Sc_rollup.timeout ctxt rollup stakers >>=? fun (outcome, ctxt) -> + Sc_rollup.apply_outcome ctxt rollup stakers outcome + >>=? fun (status, ctxt) -> + let consumed_gas = Gas.consumed ~since:before_operation ~until:ctxt in + let result = Sc_rollup_timeout_result {status; consumed_gas} in + return (ctxt, result, []) type success_or_failure = Success of context | Failure @@ -1966,7 +1988,7 @@ let precheck_manager_contents (type kind) ctxt (op : kind Kind.manager contents) ~count_limit:tx_rollup_max_messages_per_inbox >>?= fun () -> return ctxt | Sc_rollup_originate _ | Sc_rollup_add_messages _ | Sc_rollup_cement _ - | Sc_rollup_publish _ -> + | Sc_rollup_publish _ | Sc_rollup_refute _ | Sc_rollup_timeout _ -> assert_sc_rollup_feature_enabled ctxt >|=? fun () -> ctxt) >>=? fun ctxt -> Contract.increment_counter ctxt source >>=? fun ctxt -> @@ -2116,6 +2138,8 @@ let burn_storage_fees : | Sc_rollup_add_messages_result _ -> return (ctxt, storage_limit, smopr) | Sc_rollup_cement_result _ -> return (ctxt, storage_limit, smopr) | Sc_rollup_publish_result _ -> return (ctxt, storage_limit, smopr) + | Sc_rollup_refute_result _ -> return (ctxt, storage_limit, smopr) + | Sc_rollup_timeout_result _ -> return (ctxt, storage_limit, smopr) let apply_manager_contents (type kind) ctxt mode chain_id ~gas_consumed_in_precheck (op : kind Kind.manager contents) : diff --git a/src/proto_alpha/lib_protocol/apply_results.ml b/src/proto_alpha/lib_protocol/apply_results.ml index 68a004b60014..ca56c7583010 100644 --- a/src/proto_alpha/lib_protocol/apply_results.ml +++ b/src/proto_alpha/lib_protocol/apply_results.ml @@ -239,6 +239,16 @@ type _ successful_manager_operation_result = staked_hash : Sc_rollup.Commitment_hash.t; } -> Kind.sc_rollup_publish successful_manager_operation_result + | Sc_rollup_refute_result : { + consumed_gas : Gas.Arith.fp; + status : Sc_rollup.Game.status; + } + -> Kind.sc_rollup_refute successful_manager_operation_result + | Sc_rollup_timeout_result : { + consumed_gas : Gas.Arith.fp; + status : Sc_rollup.Game.status; + } + -> Kind.sc_rollup_timeout successful_manager_operation_result let migration_origination_result_to_successful_manager_operation_result ({ @@ -961,6 +971,46 @@ module Manager_result = struct ~inj:(fun (consumed_gas, consumed_milligas, staked_hash) -> assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; Sc_rollup_publish_result {consumed_gas = consumed_milligas; staked_hash}) + + let sc_rollup_refute_case = + make + ~op_case:Operation.Encoding.Manager_operations.sc_rollup_refute_case + ~encoding: + Data_encoding.( + obj3 + (req "consumed_gas" Gas.Arith.n_integral_encoding) + (dft "consumed_milligas" Gas.Arith.n_fp_encoding Gas.Arith.zero) + (req "status" Sc_rollup.Game.status_encoding)) + ~select:(function + | Successful_manager_result (Sc_rollup_refute_result _ as op) -> Some op + | _ -> None) + ~proj:(function + | Sc_rollup_refute_result {consumed_gas; status} -> + (Gas.Arith.ceil consumed_gas, consumed_gas, status)) + ~kind:Kind.Sc_rollup_refute_manager_kind + ~inj:(fun (consumed_gas, consumed_milligas, status) -> + assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; + Sc_rollup_refute_result {consumed_gas = consumed_milligas; status}) + + let sc_rollup_timeout_case = + make + ~op_case:Operation.Encoding.Manager_operations.sc_rollup_timeout_case + ~encoding: + (obj3 + (req "consumed_gas" Gas.Arith.n_integral_encoding) + (dft "consumed_milligas" Gas.Arith.n_fp_encoding Gas.Arith.zero) + (req "status" Sc_rollup.Game.status_encoding)) + ~select:(function + | Successful_manager_result (Sc_rollup_timeout_result _ as op) -> + Some op + | _ -> None) + ~proj:(function + | Sc_rollup_timeout_result {consumed_gas; status} -> + (Gas.Arith.ceil consumed_gas, consumed_gas, status)) + ~kind:Kind.Sc_rollup_timeout_manager_kind + ~inj:(fun (consumed_gas, consumed_milligas, status) -> + assert (Gas.Arith.(equal (ceil consumed_milligas) consumed_gas)) ; + Sc_rollup_timeout_result {consumed_gas = consumed_milligas; status}) end type 'kind iselect = @@ -1283,6 +1333,13 @@ let equal_manager_kind : -> Some Eq | (Kind.Sc_rollup_publish_manager_kind, _) -> None + | (Kind.Sc_rollup_refute_manager_kind, Kind.Sc_rollup_refute_manager_kind) -> + Some Eq + | (Kind.Sc_rollup_refute_manager_kind, _) -> None + | (Kind.Sc_rollup_timeout_manager_kind, Kind.Sc_rollup_timeout_manager_kind) + -> + Some Eq + | (Kind.Sc_rollup_timeout_manager_kind, _) -> None module Encoding = struct type 'kind case = @@ -1779,6 +1836,28 @@ module Encoding = struct -> Some (op, res) | _ -> None) + + let[@coq_axiom_with_reason "gadt"] sc_rollup_refute_case = + make_manager_case + Operation.Encoding.sc_rollup_refute_case + Manager_result.sc_rollup_refute_case + (function + | Contents_and_result + ((Manager_operation {operation = Sc_rollup_refute _; _} as op), res) + -> + Some (op, res) + | _ -> None) + + let[@coq_axiom_with_reason "gadt"] sc_rollup_timeout_case = + make_manager_case + Operation.Encoding.sc_rollup_timeout_case + Manager_result.sc_rollup_timeout_case + (function + | Contents_and_result + ((Manager_operation {operation = Sc_rollup_timeout _; _} as op), res) + -> + Some (op, res) + | _ -> None) end let contents_result_encoding = @@ -1828,6 +1907,8 @@ let contents_result_encoding = make sc_rollup_add_messages_case; make sc_rollup_cement_case; make sc_rollup_publish_case; + make sc_rollup_refute_case; + make sc_rollup_timeout_case; ] let contents_and_result_encoding = @@ -1882,6 +1963,8 @@ let contents_and_result_encoding = make sc_rollup_add_messages_case; make sc_rollup_cement_case; make sc_rollup_publish_case; + make sc_rollup_refute_case; + make sc_rollup_timeout_case; ] type 'kind contents_result_list = @@ -2510,6 +2593,56 @@ let kind_equal : } ) -> Some Eq | (Manager_operation {operation = Sc_rollup_publish _; _}, _) -> None + | ( Manager_operation {operation = Sc_rollup_refute _; _}, + Manager_operation_result + {operation_result = Applied (Sc_rollup_refute_result _); _} ) -> + Some Eq + | ( Manager_operation {operation = Sc_rollup_refute _; _}, + Manager_operation_result + {operation_result = Backtracked (Sc_rollup_refute_result _, _); _} ) -> + Some Eq + | ( Manager_operation {operation = Sc_rollup_refute _; _}, + Manager_operation_result + { + operation_result = + Failed (Alpha_context.Kind.Sc_rollup_refute_manager_kind, _); + _; + } ) -> + Some Eq + | ( Manager_operation {operation = Sc_rollup_refute _; _}, + Manager_operation_result + { + operation_result = + Skipped Alpha_context.Kind.Sc_rollup_refute_manager_kind; + _; + } ) -> + Some Eq + | (Manager_operation {operation = Sc_rollup_refute _; _}, _) -> None + | ( Manager_operation {operation = Sc_rollup_timeout _; _}, + Manager_operation_result + {operation_result = Applied (Sc_rollup_timeout_result _); _} ) -> + Some Eq + | ( Manager_operation {operation = Sc_rollup_timeout _; _}, + Manager_operation_result + {operation_result = Backtracked (Sc_rollup_timeout_result _, _); _} ) -> + Some Eq + | ( Manager_operation {operation = Sc_rollup_timeout _; _}, + Manager_operation_result + { + operation_result = + Failed (Alpha_context.Kind.Sc_rollup_timeout_manager_kind, _); + _; + } ) -> + Some Eq + | ( Manager_operation {operation = Sc_rollup_timeout _; _}, + Manager_operation_result + { + operation_result = + Skipped Alpha_context.Kind.Sc_rollup_timeout_manager_kind; + _; + } ) -> + Some Eq + | (Manager_operation {operation = Sc_rollup_timeout _; _}, _) -> None let rec kind_equal_list : type kind kind2. diff --git a/src/proto_alpha/lib_protocol/apply_results.mli b/src/proto_alpha/lib_protocol/apply_results.mli index a767d584d985..70a2ddadfb44 100644 --- a/src/proto_alpha/lib_protocol/apply_results.mli +++ b/src/proto_alpha/lib_protocol/apply_results.mli @@ -275,6 +275,16 @@ and _ successful_manager_operation_result = staked_hash : Sc_rollup.Commitment_hash.t; } -> Kind.sc_rollup_publish successful_manager_operation_result + | Sc_rollup_refute_result : { + consumed_gas : Gas.Arith.fp; + status : Sc_rollup.Game.status; + } + -> Kind.sc_rollup_refute successful_manager_operation_result + | Sc_rollup_timeout_result : { + consumed_gas : Gas.Arith.fp; + status : Sc_rollup.Game.status; + } + -> Kind.sc_rollup_timeout successful_manager_operation_result and packed_successful_manager_operation_result = | Successful_manager_result : diff --git a/src/proto_alpha/lib_protocol/operation_repr.ml b/src/proto_alpha/lib_protocol/operation_repr.ml index 58daffd5848c..59b639903385 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.ml +++ b/src/proto_alpha/lib_protocol/operation_repr.ml @@ -97,6 +97,10 @@ module Kind = struct type sc_rollup_publish = Sc_rollup_publish_kind + type sc_rollup_refute = Sc_rollup_refute_kind + + type sc_rollup_timeout = Sc_rollup_timeout_kind + type 'a manager = | Reveal_manager_kind : reveal manager | Transaction_manager_kind : transaction manager @@ -120,6 +124,8 @@ module Kind = struct | Sc_rollup_add_messages_manager_kind : sc_rollup_add_messages manager | Sc_rollup_cement_manager_kind : sc_rollup_cement manager | Sc_rollup_publish_manager_kind : sc_rollup_publish manager + | Sc_rollup_refute_manager_kind : sc_rollup_refute manager + | Sc_rollup_timeout_manager_kind : sc_rollup_timeout manager end type 'a consensus_operation_type = @@ -372,6 +378,17 @@ and _ manager_operation = commitment : Sc_rollup_repr.Commitment.t; } -> Kind.sc_rollup_publish manager_operation + | Sc_rollup_refute : { + rollup : Sc_rollup_repr.t; + opponent : Sc_rollup_repr.Staker.t; + refutation : Sc_rollup_game_repr.refutation; + } + -> Kind.sc_rollup_refute manager_operation + | Sc_rollup_timeout : { + rollup : Sc_rollup_repr.t; + stakers : Sc_rollup_repr.Staker.t * Sc_rollup_repr.Staker.t; + } + -> Kind.sc_rollup_timeout manager_operation and counter = Z.t @@ -398,6 +415,8 @@ let manager_kind : type kind. kind manager_operation -> kind Kind.manager = | Sc_rollup_add_messages _ -> Kind.Sc_rollup_add_messages_manager_kind | Sc_rollup_cement _ -> Kind.Sc_rollup_cement_manager_kind | Sc_rollup_publish _ -> Kind.Sc_rollup_publish_manager_kind + | Sc_rollup_refute _ -> Kind.Sc_rollup_refute_manager_kind + | Sc_rollup_timeout _ -> Kind.Sc_rollup_timeout_manager_kind type packed_manager_operation = | Manager : 'kind manager_operation -> packed_manager_operation @@ -481,6 +500,10 @@ let sc_rollup_operation_cement_tag = sc_rollup_operation_tag_offset + 2 let sc_rollup_operation_publish_tag = sc_rollup_operation_tag_offset + 3 +let sc_rollup_operation_refute_tag = sc_rollup_operation_tag_offset + 4 + +let sc_rollup_operation_timeout_tag = sc_rollup_operation_tag_offset + 5 + module Encoding = struct open Data_encoding @@ -960,6 +983,50 @@ module Encoding = struct inj = (fun (rollup, commitment) -> Sc_rollup_publish {rollup; commitment}); } + + let[@coq_axiom_with_reason "gadt"] sc_rollup_refute_case = + MCase + { + tag = sc_rollup_operation_refute_tag; + name = "sc_rollup_refute"; + encoding = + obj3 + (req "rollup" Sc_rollup_repr.encoding) + (req "opponent" Sc_rollup_repr.Staker.encoding) + (req "refutation" Sc_rollup_game_repr.refutation_encoding); + select = + (function + | Manager (Sc_rollup_refute _ as op) -> Some op | _ -> None); + proj = + (function + | Sc_rollup_refute {rollup; opponent; refutation} -> + (rollup, opponent, refutation)); + inj = + (fun (rollup, opponent, refutation) -> + Sc_rollup_refute {rollup; opponent; refutation}); + } + + let[@coq_axiom_with_reason "gadt"] sc_rollup_timeout_case = + MCase + { + tag = sc_rollup_operation_timeout_tag; + name = "sc_rollup_timeout"; + encoding = + obj2 + (req "rollup" Sc_rollup_repr.encoding) + (req + "stakers" + (tup2 + Sc_rollup_repr.Staker.encoding + Sc_rollup_repr.Staker.encoding)); + select = + (function + | Manager (Sc_rollup_timeout _ as op) -> Some op | _ -> None); + proj = + (function + | Sc_rollup_timeout {rollup; stakers} -> (rollup, stakers)); + inj = (fun (rollup, stakers) -> Sc_rollup_timeout {rollup; stakers}); + } end type 'b case = @@ -1320,6 +1387,16 @@ module Encoding = struct sc_rollup_operation_publish_tag Manager_operations.sc_rollup_publish_case + let sc_rollup_refute_case = + make_manager_case + sc_rollup_operation_refute_tag + Manager_operations.sc_rollup_refute_case + + let sc_rollup_timeout_case = + make_manager_case + sc_rollup_operation_timeout_tag + Manager_operations.sc_rollup_timeout_case + let contents_encoding = let make (Case {tag; name; encoding; select; proj; inj}) = case @@ -1361,6 +1438,8 @@ module Encoding = struct make sc_rollup_add_messages_case; make sc_rollup_cement_case; make sc_rollup_publish_case; + make sc_rollup_refute_case; + make sc_rollup_timeout_case; ] let contents_list_encoding = @@ -1574,6 +1653,10 @@ let equal_manager_operation_kind : | (Sc_rollup_cement _, _) -> None | (Sc_rollup_publish _, Sc_rollup_publish _) -> Some Eq | (Sc_rollup_publish _, _) -> None + | (Sc_rollup_refute _, Sc_rollup_refute _) -> Some Eq + | (Sc_rollup_refute _, _) -> None + | (Sc_rollup_timeout _, Sc_rollup_timeout _) -> Some Eq + | (Sc_rollup_timeout _, _) -> None let equal_contents_kind : type a b. a contents -> b contents -> (a, b) eq option = diff --git a/src/proto_alpha/lib_protocol/operation_repr.mli b/src/proto_alpha/lib_protocol/operation_repr.mli index d669650e67ca..47371a1c23be 100644 --- a/src/proto_alpha/lib_protocol/operation_repr.mli +++ b/src/proto_alpha/lib_protocol/operation_repr.mli @@ -126,6 +126,10 @@ module Kind : sig type sc_rollup_publish = Sc_rollup_publish_kind + type sc_rollup_refute = Sc_rollup_refute_kind + + type sc_rollup_timeout = Sc_rollup_timeout_kind + type 'a manager = | Reveal_manager_kind : reveal manager | Transaction_manager_kind : transaction manager @@ -149,6 +153,8 @@ module Kind : sig | Sc_rollup_add_messages_manager_kind : sc_rollup_add_messages manager | Sc_rollup_cement_manager_kind : sc_rollup_cement manager | Sc_rollup_publish_manager_kind : sc_rollup_publish manager + | Sc_rollup_refute_manager_kind : sc_rollup_refute manager + | Sc_rollup_timeout_manager_kind : sc_rollup_timeout manager end type 'a consensus_operation_type = @@ -441,6 +447,17 @@ and _ manager_operation = commitment : Sc_rollup_repr.Commitment.t; } -> Kind.sc_rollup_publish manager_operation + | Sc_rollup_refute : { + rollup : Sc_rollup_repr.t; + opponent : Sc_rollup_repr.Staker.t; + refutation : Sc_rollup_game_repr.refutation; + } + -> Kind.sc_rollup_refute manager_operation + | Sc_rollup_timeout : { + rollup : Sc_rollup_repr.t; + stakers : Sc_rollup_repr.Staker.t * Sc_rollup_repr.Staker.t; + } + -> Kind.sc_rollup_timeout manager_operation (** Counters are used as anti-replay protection mechanism in manager operations: each manager account stores a counter and @@ -582,6 +599,10 @@ module Encoding : sig val sc_rollup_publish_case : Kind.sc_rollup_publish Kind.manager case + val sc_rollup_refute_case : Kind.sc_rollup_refute Kind.manager case + + val sc_rollup_timeout_case : Kind.sc_rollup_timeout Kind.manager case + module Manager_operations : sig type 'b case = | MCase : { @@ -638,5 +659,9 @@ module Encoding : sig val sc_rollup_cement_case : Kind.sc_rollup_cement case val sc_rollup_publish_case : Kind.sc_rollup_publish case + + val sc_rollup_refute_case : Kind.sc_rollup_refute case + + val sc_rollup_timeout_case : Kind.sc_rollup_timeout case end end diff --git a/src/proto_alpha/lib_protocol/test/helpers/block.ml b/src/proto_alpha/lib_protocol/test/helpers/block.ml index a0dacf2fbce4..b9b21b799c24 100644 --- a/src/proto_alpha/lib_protocol/test/helpers/block.ml +++ b/src/proto_alpha/lib_protocol/test/helpers/block.ml @@ -725,7 +725,8 @@ let bake_n_with_all_balance_updates ?(baking_mode = Application) ?policy | Tx_rollup_rejection_result _ | Transfer_ticket_result _ | Tx_rollup_dispatch_tickets_result _ | Sc_rollup_originate_result _ | Sc_rollup_add_messages_result _ - | Sc_rollup_cement_result _ | Sc_rollup_publish_result _ -> + | Sc_rollup_cement_result _ | Sc_rollup_publish_result _ + | Sc_rollup_refute_result _ | Sc_rollup_timeout_result _ -> balance_updates_rev | Transaction_result (Transaction_to_contract_result {balance_updates; _}) @@ -768,7 +769,9 @@ let bake_n_with_origination_results ?(baking_mode = Application) ?policy n b = | Successful_manager_result (Sc_rollup_originate_result _) | Successful_manager_result (Sc_rollup_add_messages_result _) | Successful_manager_result (Sc_rollup_cement_result _) - | Successful_manager_result (Sc_rollup_publish_result _) -> + | Successful_manager_result (Sc_rollup_publish_result _) + | Successful_manager_result (Sc_rollup_refute_result _) + | Successful_manager_result (Sc_rollup_timeout_result _) -> origination_results_rev | Successful_manager_result (Origination_result x) -> Origination_result x :: origination_results_rev) diff --git a/tezt/_regressions/rpc/alpha.client.mempool.out b/tezt/_regressions/rpc/alpha.client.mempool.out index cf7ba36b9ebf..cb5d48351978 100644 --- a/tezt/_regressions/rpc/alpha.client.mempool.out +++ b/tezt/_regressions/rpc/alpha.client.mempool.out @@ -3194,6 +3194,196 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind" ], "additionalProperties": false + }, + { + "title": "Sc_rollup_refute", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "sc_rollup_refute" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "rollup": { + "$ref": "#/definitions/alpha.rollup_address" + }, + "opponent": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "refutation": { + "type": "object", + "properties": { + "choice": { + "$ref": "#/definitions/positive_bignum" + }, + "step": { + "oneOf": [ + { + "title": "Dissection", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "oneOf": [ + { + "title": "Some", + "$ref": "#/definitions/state_hash" + }, + { + "title": "None", + "type": "null" + } + ] + }, + { + "$ref": "#/definitions/positive_bignum" + } + ], + "additionalItems": false + } + }, + { + "title": "Proof", + "oneOf": [ + { + "title": "Proof of a normal computation step", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + }, + { + "title": "Proof of an input step", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + }, + { + "title": "Proof that the PVM is blocked", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + } + ] + } + ] + } + }, + "required": [ + "step", + "choice" + ], + "additionalProperties": false + } + }, + "required": [ + "refutation", + "opponent", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Sc_rollup_timeout", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "sc_rollup_timeout" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "rollup": { + "$ref": "#/definitions/alpha.rollup_address" + }, + "stakers": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + { + "$ref": "#/definitions/Signature.Public_key_hash" + } + ], + "additionalItems": false + } + }, + "required": [ + "stakers", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false } ] }, @@ -4357,7 +4547,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "parameters", "layout": { - "name": "X_137", + "name": "X_143", "kind": "Ref" }, "data_kind": { @@ -5022,7 +5212,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "commitment", "layout": { - "name": "X_135", + "name": "X_141", "kind": "Ref" }, "data_kind": { @@ -5385,7 +5575,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "message", "layout": { - "name": "X_8", + "name": "X_14", "kind": "Ref" }, "data_kind": { @@ -5454,7 +5644,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "previous_message_result", "layout": { - "name": "X_9", + "name": "X_15", "kind": "Ref" }, "data_kind": { @@ -5484,7 +5674,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "proof", "layout": { - "name": "X_134", + "name": "X_140", "kind": "Ref" }, "data_kind": { @@ -5640,7 +5830,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "name": "tickets_info", "layout": { "layout": { - "name": "X_4", + "name": "X_10", "kind": "Ref" }, "kind": "Seq" @@ -5881,7 +6071,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "kind", "layout": { - "name": "X_3", + "name": "X_9", "kind": "Ref" }, "data_kind": { @@ -6004,7 +6194,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "name": "message", "layout": { "layout": { - "name": "X_2", + "name": "X_8", "kind": "Ref" }, "kind": "Seq" @@ -6208,7 +6398,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "commitment", "layout": { - "name": "X_1", + "name": "X_7", "kind": "Ref" }, "data_kind": { @@ -6219,65 +6409,9 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' } ], "name": "Sc_rollup_publish" - } - ] - } - }, - { - "description": { - "title": "alpha.inlined.endorsement" - }, - "encoding": { - "fields": [ - { - "name": "branch", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "operations", - "layout": { - "name": "alpha.inlined.endorsement_mempool.contents", - "kind": "Ref" - }, - "data_kind": { - "size": 43, - "kind": "Float" - }, - "kind": "named" }, { - "name": "signature", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" - } - ] - } - }, - { - "description": { - "title": "alpha.inlined.endorsement_mempool.contents" - }, - "encoding": { - "tag_size": "Uint8", - "kind": { - "size": 43, - "kind": "Float" - }, - "cases": [ - { - "tag": 21, + "tag": 204, "fields": [ { "name": "Tag", @@ -6292,83 +6426,354 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "slot", + "name": "source", "layout": { - "size": "Uint16", - "kind": "Int" + "name": "public_key_hash", + "kind": "Ref" }, "data_kind": { - "size": 2, + "size": 21, "kind": "Float" }, "kind": "named" }, { - "name": "level", + "name": "fee", "layout": { - "size": "Int32", - "kind": "Int" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "size": 4, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" }, { - "name": "round", + "name": "counter", "layout": { - "size": "Int32", - "kind": "Int" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "size": 4, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" }, { - "name": "block_payload_hash", + "name": "gas_limit", "layout": { - "kind": "Bytes" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" - } - ], - "name": "Endorsement" - } - ] - } - }, - { - "description": { - "title": "alpha.block_header.alpha.full_header" - }, - "encoding": { - "fields": [ - { - "name": "level", - "layout": { - "size": "Int32", - "kind": "Int" - }, - "data_kind": { - "size": 4, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "proto", - "layout": { - "size": "Uint8", - "kind": "Int" - }, - "data_kind": { + }, + { + "name": "storage_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "kind": "dyn", + "name": "alpha.rollup_address", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "rollup", + "layout": { + "kind": "String" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + }, + { + "name": "opponent", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "refutation", + "layout": { + "name": "X_2", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Sc_rollup_refute" + }, + { + "tag": 205, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "source", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "fee", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "counter", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "gas_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "storage_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "kind": "dyn", + "name": "alpha.rollup_address", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "rollup", + "layout": { + "kind": "String" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + }, + { + "name": "stakers", + "layout": { + "name": "X_1", + "kind": "Ref" + }, + "data_kind": { + "size": 42, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Sc_rollup_timeout" + } + ] + } + }, + { + "description": { + "title": "alpha.inlined.endorsement" + }, + "encoding": { + "fields": [ + { + "name": "branch", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "operations", + "layout": { + "name": "alpha.inlined.endorsement_mempool.contents", + "kind": "Ref" + }, + "data_kind": { + "size": 43, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "signature", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + } + ] + } + }, + { + "description": { + "title": "alpha.inlined.endorsement_mempool.contents" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "size": 43, + "kind": "Float" + }, + "cases": [ + { + "tag": 21, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "slot", + "layout": { + "size": "Uint16", + "kind": "Int" + }, + "data_kind": { + "size": 2, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "level", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "round", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "block_payload_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Endorsement" + } + ] + } + }, + { + "description": { + "title": "alpha.block_header.alpha.full_header" + }, + "encoding": { + "fields": [ + { + "name": "level", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "proto", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { "size": 1, "kind": "Float" }, @@ -6893,7 +7298,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_137" + "title": "X_143" }, "encoding": { "fields": [ @@ -7276,7 +7681,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_135" + "title": "X_141" }, "encoding": { "fields": [ @@ -7303,50 +7708,238 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "layout": { "kind": "Bytes" }, - "kind": "Seq" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" - }, - { - "name": "predecessor", - "layout": { - "name": "X_136", - "kind": "Ref" - }, - "data_kind": { - "kind": "Dynamic" - }, - "kind": "named" + "kind": "Seq" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + }, + { + "name": "predecessor", + "layout": { + "name": "X_142", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "inbox_merkle_root", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + } + ] + } + }, + { + "description": { + "title": "X_142" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "kind": "Zero_width" + }, + "kind": "anon", + "data_kind": { + "size": 0, + "kind": "Float" + } + } + ], + "name": "None" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "Commitment_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Some" + } + ] + } + }, + { + "description": { + "title": "X_140" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "size": "Int16", + "kind": "Int" + }, + "kind": "anon", + "data_kind": { + "size": 2, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "name": "X_16", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } + } + ], + "name": "case 0" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "size": "Int16", + "kind": "Int" + }, + "kind": "anon", + "data_kind": { + "size": 2, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "name": "X_16", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } + } + ], + "name": "case 1" }, { - "name": "inbox_merkle_root", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" - } - ] - } - }, - { - "description": { - "title": "X_136" - }, - "encoding": { - "tag_size": "Uint8", - "kind": { - "kind": "Dynamic" - }, - "cases": [ - { - "tag": 0, + "tag": 2, "fields": [ { "name": "Tag", @@ -7362,19 +7955,50 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "kind": "Zero_width" + "size": "Int16", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 2, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } + }, + { + "layout": { + "name": "X_16", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } } ], - "name": "None" + "name": "case 2" }, { - "tag": 1, + "tag": 3, "fields": [ { "name": "Tag", @@ -7389,25 +8013,55 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "Commitment_hash", + "layout": { + "size": "Int16", + "kind": "Int" + }, + "kind": "anon", + "data_kind": { + "size": 2, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { "layout": { "kind": "Bytes" }, + "kind": "anon", "data_kind": { "size": 32, "kind": "Float" + } + }, + { + "layout": { + "name": "X_16", + "kind": "Ref" }, - "kind": "named" + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } } ], - "name": "Some" + "name": "case 3" } ] } }, { "description": { - "title": "X_134" + "title": "X_139" }, "encoding": { "tag_size": "Uint8", @@ -7432,50 +8086,71 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int16", + "size": "Uint8", "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 2, + "size": 1, "kind": "Float" } }, { "layout": { - "kind": "Bytes" + "name": "X_138", + "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 0, "kind": "Float" } + } + ], + "name": "case 0" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" }, { "layout": { - "kind": "Bytes" + "size": "Uint16", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 2, "kind": "Float" } }, { "layout": { - "name": "X_10", + "name": "X_138", "kind": "Ref" }, "kind": "anon", "data_kind": { - "kind": "Dynamic" + "size": 0, + "kind": "Float" } } ], - "name": "case 0" + "name": "case 1" }, { - "tag": 1, + "tag": 2, "fields": [ { "name": "Tag", @@ -7491,50 +8166,71 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int16", + "size": "Int32", "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 2, + "size": 4, "kind": "Float" } }, { "layout": { - "kind": "Bytes" + "name": "X_138", + "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 0, "kind": "Float" } + } + ], + "name": "case 2" + }, + { + "tag": 3, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" }, { "layout": { - "kind": "Bytes" + "size": "Int64", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 8, "kind": "Float" } }, { "layout": { - "name": "X_10", + "name": "X_138", "kind": "Ref" }, "kind": "anon", "data_kind": { - "kind": "Dynamic" + "size": 0, + "kind": "Float" } } ], - "name": "case 1" + "name": "case 3" }, { - "tag": 2, + "tag": 4, "fields": [ { "name": "Tag", @@ -7550,50 +8246,71 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int16", + "size": "Uint8", "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 2, + "size": 1, "kind": "Float" } }, { "layout": { - "kind": "Bytes" + "name": "X_134", + "kind": "Ref" }, "kind": "anon", "data_kind": { "size": 32, "kind": "Float" } + } + ], + "name": "case 4" + }, + { + "tag": 5, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" }, { "layout": { - "kind": "Bytes" + "size": "Uint16", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 2, "kind": "Float" } }, { "layout": { - "name": "X_10", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "kind": "Dynamic" + "size": 32, + "kind": "Float" } } ], - "name": "case 2" + "name": "case 5" }, { - "tag": 3, + "tag": 6, "fields": [ { "name": "Tag", @@ -7609,63 +8326,71 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int16", + "size": "Int32", "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 2, + "size": 4, "kind": "Float" } }, { "layout": { - "kind": "Bytes" + "name": "X_134", + "kind": "Ref" }, "kind": "anon", "data_kind": { "size": 32, "kind": "Float" } + } + ], + "name": "case 6" + }, + { + "tag": 7, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" }, { "layout": { - "kind": "Bytes" + "size": "Int64", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 8, "kind": "Float" } }, { "layout": { - "name": "X_10", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "kind": "Dynamic" + "size": 32, + "kind": "Float" } } ], - "name": "case 3" - } - ] - } - }, - { - "description": { - "title": "X_133" - }, - "encoding": { - "tag_size": "Uint8", - "kind": { - "kind": "Dynamic" - }, - "cases": [ + "name": "case 7" + }, { - "tag": 0, + "tag": 8, "fields": [ { "name": "Tag", @@ -7692,20 +8417,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_132", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" } } ], - "name": "case 0" + "name": "case 8" }, { - "tag": 1, + "tag": 9, "fields": [ { "name": "Tag", @@ -7732,20 +8457,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_132", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" } } ], - "name": "case 1" + "name": "case 9" }, { - "tag": 2, + "tag": 10, "fields": [ { "name": "Tag", @@ -7772,20 +8497,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_132", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" } } ], - "name": "case 2" + "name": "case 10" }, { - "tag": 3, + "tag": 11, "fields": [ { "name": "Tag", @@ -7812,20 +8537,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_132", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" } } ], - "name": "case 3" + "name": "case 11" }, { - "tag": 4, + "tag": 12, "fields": [ { "name": "Tag", @@ -7852,20 +8577,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_128", + "name": "X_126", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 64, "kind": "Float" } } ], - "name": "case 4" + "name": "case 12" }, { - "tag": 5, + "tag": 13, "fields": [ { "name": "Tag", @@ -7892,20 +8617,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_128", + "name": "X_126", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 64, "kind": "Float" } } ], - "name": "case 5" + "name": "case 13" }, { - "tag": 6, + "tag": 14, "fields": [ { "name": "Tag", @@ -7932,20 +8657,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_128", + "name": "X_126", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 64, "kind": "Float" } } ], - "name": "case 6" + "name": "case 14" }, { - "tag": 7, + "tag": 15, "fields": [ { "name": "Tag", @@ -7972,20 +8697,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_128", + "name": "X_126", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 64, "kind": "Float" } } ], - "name": "case 7" + "name": "case 15" }, { - "tag": 8, + "tag": 128, "fields": [ { "name": "Tag", @@ -8000,32 +8725,55 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { + "layout": { + "kind": "Zero_width" + }, + "kind": "anon", + "data_kind": { + "size": 0, + "kind": "Float" + } + } + ], + "name": "case 128" + }, + { + "tag": 129, + "fields": [ + { + "name": "Tag", "layout": { "size": "Uint8", "kind": "Int" }, - "kind": "anon", "data_kind": { "size": 1, "kind": "Float" - } + }, + "kind": "named" }, { "layout": { - "name": "X_128", - "kind": "Ref" + "layout": { + "name": "X_21", + "kind": "Ref" + }, + "kind": "Seq", + "length_limit": { + "kind": "exactly", + "exactly": 1 + } }, "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Variable" } } ], - "name": "case 8" + "name": "case 129" }, { - "tag": 9, + "tag": 130, "fields": [ { "name": "Tag", @@ -8041,31 +8789,62 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Uint16", - "kind": "Int" + "layout": { + "name": "X_21", + "kind": "Ref" + }, + "kind": "Seq", + "length_limit": { + "kind": "exactly", + "exactly": 2 + } }, "kind": "anon", "data_kind": { - "size": 2, - "kind": "Float" + "kind": "Variable" } + } + ], + "name": "case 130" + }, + { + "tag": 131, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" }, { "layout": { - "name": "X_128", - "kind": "Ref" + "layout": { + "name": "X_21", + "kind": "Ref" + }, + "kind": "Seq" }, "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Variable" } } ], - "name": "case 9" + "name": "case 131" }, { - "tag": 10, + "tag": 192, "fields": [ { "name": "Tag", @@ -8080,32 +8859,24 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "layout": { - "size": "Int32", - "kind": "Int" - }, - "kind": "anon", - "data_kind": { - "size": 4, - "kind": "Float" - } + "kind": "dyn", + "num_fields": 1, + "size": "Uint8" }, { "layout": { - "name": "X_128", - "kind": "Ref" + "kind": "Bytes" }, "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Variable" } } ], - "name": "case 10" + "name": "case 192" }, { - "tag": 11, + "tag": 193, "fields": [ { "name": "Tag", @@ -8119,33 +8890,57 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, "kind": "named" }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint16" + }, { "layout": { - "size": "Int64", - "kind": "Int" + "kind": "Bytes" }, "kind": "anon", "data_kind": { - "size": 8, - "kind": "Float" + "kind": "Variable" } + } + ], + "name": "case 193" + }, + { + "tag": 195, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" }, { "layout": { - "name": "X_128", - "kind": "Ref" + "kind": "Bytes" }, "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Variable" } } ], - "name": "case 11" + "name": "case 195" }, { - "tag": 12, + "tag": 224, "fields": [ { "name": "Tag", @@ -8172,20 +8967,29 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_120", + "name": "X_121", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 64, + "kind": "Dynamic" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "case 12" + "name": "case 224" }, { - "tag": 13, + "tag": 225, "fields": [ { "name": "Tag", @@ -8212,20 +9016,29 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_120", + "name": "X_121", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 64, + "kind": "Dynamic" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "case 13" + "name": "case 225" }, { - "tag": 14, + "tag": 226, "fields": [ { "name": "Tag", @@ -8252,20 +9065,29 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_120", + "name": "X_121", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 64, + "kind": "Dynamic" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "case 14" + "name": "case 226" }, { - "tag": 15, + "tag": 227, "fields": [ { "name": "Tag", @@ -8292,20 +9114,99 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_120", + "name": "X_121", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 64, + "kind": "Dynamic" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "case 15" + "name": "case 227" + } + ] + } + }, + { + "description": { + "title": "X_138" + }, + "encoding": { + "fields": [] + } + }, + { + "description": { + "title": "X_134" + }, + "encoding": { + "fields": [ + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + } + ] + } + }, + { + "description": { + "title": "X_126" + }, + "encoding": { + "fields": [ + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } }, { - "tag": 128, + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + } + ] + } + }, + { + "description": { + "title": "X_122" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "size": 33, + "kind": "Float" + }, + "cases": [ + { + "tag": 0, "fields": [ { "name": "Tag", @@ -8320,20 +9221,21 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { + "name": "Context_hash", "layout": { - "kind": "Zero_width" + "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" - } + }, + "kind": "named" } ], - "name": "case 128" + "name": "case 0" }, { - "tag": 129, + "tag": 1, "fields": [ { "name": "Tag", @@ -8348,62 +9250,145 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { + "name": "Context_hash", "layout": { - "layout": { - "name": "X_15", - "kind": "Ref" - }, - "kind": "Seq", - "length_limit": { - "kind": "exactly", - "exactly": 1 - } + "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "kind": "Variable" - } + "size": 32, + "kind": "Float" + }, + "kind": "named" } ], - "name": "case 129" + "name": "case 1" + } + ] + } + }, + { + "description": { + "title": "X_121" + }, + "encoding": { + "fields": [ + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint8" + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "kind": "Variable" + } + } + ] + } + }, + { + "description": { + "title": "X_16" + }, + "encoding": { + "fields": [ + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "layout": { + "layout": { + "name": "X_139", + "kind": "Ref" + }, + "kind": "Seq" + }, + "kind": "anon", + "data_kind": { + "kind": "Variable" + } + } + ] + } + }, + { + "description": { + "title": "X_21" + }, + "encoding": { + "fields": [ + { + "layout": { + "name": "X_121", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } + }, + { + "layout": { + "name": "X_122", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "size": 33, + "kind": "Float" + } + } + ] + } + }, + { + "description": { + "title": "X_15" + }, + "encoding": { + "fields": [ + { + "name": "context_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" }, { - "tag": 130, - "fields": [ - { - "name": "Tag", - "layout": { - "size": "Uint8", - "kind": "Int" - }, - "data_kind": { - "size": 1, - "kind": "Float" - }, - "kind": "named" - }, - { - "layout": { - "layout": { - "name": "X_15", - "kind": "Ref" - }, - "kind": "Seq", - "length_limit": { - "kind": "exactly", - "exactly": 2 - } - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } - } - ], - "name": "case 130" - }, + "name": "withdraw_list_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + } + ] + } + }, + { + "description": { + "title": "X_14" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ { - "tag": 131, + "tag": 0, "fields": [ { "name": "Tag", @@ -8423,55 +9408,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": "Uint30" }, { + "name": "batch", "layout": { - "layout": { - "name": "X_15", - "kind": "Ref" - }, - "kind": "Seq" + "kind": "String" }, - "kind": "anon", "data_kind": { "kind": "Variable" - } - } - ], - "name": "case 131" - }, - { - "tag": 192, - "fields": [ - { - "name": "Tag", - "layout": { - "size": "Uint8", - "kind": "Int" - }, - "data_kind": { - "size": 1, - "kind": "Float" }, "kind": "named" - }, - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint8" - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } } ], - "name": "case 192" + "name": "Batch" }, { - "tag": 193, + "tag": 1, "fields": [ { "name": "Tag", @@ -8486,56 +9436,88 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "kind": "dyn", - "num_fields": 1, - "size": "Uint16" - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } - } - ], - "name": "case 193" - }, - { - "tag": 195, - "fields": [ - { - "name": "Tag", + "name": "deposit", "layout": { - "size": "Uint8", - "kind": "Int" + "name": "X_12", + "kind": "Ref" }, "data_kind": { - "size": 1, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" - }, - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } } ], - "name": "case 195" + "name": "Deposit" + } + ] + } + }, + { + "description": { + "title": "X_12" + }, + "encoding": { + "fields": [ + { + "name": "sender", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" }, { - "tag": 224, + "name": "destination", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 20, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "ticket_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "amount", + "layout": { + "name": "X_13", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ] + } + }, + { + "description": { + "title": "X_13" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, "fields": [ { "name": "Tag", @@ -8559,32 +9541,12 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": 1, "kind": "Float" } - }, - { - "layout": { - "name": "X_115", - "kind": "Ref" - }, - "kind": "anon", - "data_kind": { - "kind": "Dynamic" - } - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "size": 32, - "kind": "Float" - } } ], - "name": "case 224" + "name": "case 0" }, { - "tag": 225, + "tag": 1, "fields": [ { "name": "Tag", @@ -8608,32 +9570,12 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": 2, "kind": "Float" } - }, - { - "layout": { - "name": "X_115", - "kind": "Ref" - }, - "kind": "anon", - "data_kind": { - "kind": "Dynamic" - } - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "size": 32, - "kind": "Float" - } } ], - "name": "case 225" + "name": "case 1" }, { - "tag": 226, + "tag": 2, "fields": [ { "name": "Tag", @@ -8657,32 +9599,12 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": 4, "kind": "Float" } - }, - { - "layout": { - "name": "X_115", - "kind": "Ref" - }, - "kind": "anon", - "data_kind": { - "kind": "Dynamic" - } - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "size": 32, - "kind": "Float" - } } ], - "name": "case 226" + "name": "case 2" }, { - "tag": 227, + "tag": 3, "fields": [ { "name": "Tag", @@ -8706,97 +9628,95 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": 8, "kind": "Float" } - }, - { - "layout": { - "name": "X_115", - "kind": "Ref" - }, - "kind": "anon", - "data_kind": { - "kind": "Dynamic" - } - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "size": 32, - "kind": "Float" - } } ], - "name": "case 227" + "name": "case 3" } ] } }, { "description": { - "title": "X_132" - }, - "encoding": { - "fields": [] - } - }, - { - "description": { - "title": "X_128" + "title": "X_10" }, "encoding": { "fields": [ { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "contents", "layout": { "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" - } - } - ] - } - }, - { - "description": { - "title": "X_120" - }, - "encoding": { - "fields": [ + "kind": "Variable" + }, + "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, { + "name": "ty", "layout": { "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "size": 32, + "kind": "Variable" + }, + "kind": "named" + }, + { + "name": "ticketer", + "layout": { + "name": "alpha.contract_id", + "kind": "Ref" + }, + "data_kind": { + "size": 22, "kind": "Float" - } + }, + "kind": "named" }, { + "name": "amount", "layout": { - "kind": "Bytes" + "name": "X_13", + "kind": "Ref" }, - "kind": "anon", "data_kind": { - "size": 32, + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "claimer", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, "kind": "Float" - } + }, + "kind": "named" } ] } }, { "description": { - "title": "X_116" + "title": "alpha.contract_id" }, "encoding": { "tag_size": "Uint8", "kind": { - "size": 33, + "size": 22, "kind": "Float" }, "cases": [ @@ -8816,18 +9736,19 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "Context_hash", + "name": "Signature.Public_key_hash", "layout": { - "kind": "Bytes" + "name": "public_key_hash", + "kind": "Ref" }, "data_kind": { - "size": 32, + "size": 21, "kind": "Float" }, "kind": "named" } ], - "name": "case 0" + "name": "Implicit" }, { "tag": 1, @@ -8845,48 +9766,78 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "Context_hash", + "name": "Contract_hash", "layout": { "kind": "Bytes" }, "data_kind": { - "size": 32, + "size": 20, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "padding", + "layout": { + "kind": "Padding" + }, + "data_kind": { + "size": 1, "kind": "Float" }, "kind": "named" } ], - "name": "case 1" + "name": "Originated" } ] } }, { "description": { - "title": "X_115" + "title": "X_9" }, "encoding": { - "fields": [ - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint8" - }, + "tag_size": "Uint16", + "kind": { + "size": 2, + "kind": "Float" + }, + "cases": [ { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint16", + "kind": "Int" + }, + "data_kind": { + "size": 2, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "kind": "Zero_width" + }, + "kind": "anon", + "data_kind": { + "size": 0, + "kind": "Float" + } + } + ], + "name": "Example_arith smart contract rollup kind" } ] } }, { "description": { - "title": "X_10" + "title": "X_8" }, "encoding": { "fields": [ @@ -8897,11 +9848,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "layout": { - "name": "X_133", - "kind": "Ref" - }, - "kind": "Seq" + "kind": "String" }, "kind": "anon", "data_kind": { @@ -8913,59 +9860,96 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_15" + "title": "X_7" }, "encoding": { "fields": [ { + "name": "compressed_state", "layout": { - "name": "X_115", - "kind": "Ref" + "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "kind": "Dynamic" - } + "size": 32, + "kind": "Float" + }, + "kind": "named" }, { + "name": "inbox_level", "layout": { - "name": "X_116", - "kind": "Ref" + "size": "Int32", + "kind": "Int" }, - "kind": "anon", "data_kind": { - "size": 33, + "size": 4, "kind": "Float" - } + }, + "kind": "named" + }, + { + "name": "predecessor", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "number_of_messages", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "number_of_ticks", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" } ] } }, { "description": { - "title": "X_9" + "title": "X_2" }, "encoding": { "fields": [ { - "name": "context_hash", + "name": "choice", "layout": { - "kind": "Bytes" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" }, { - "name": "withdraw_list_hash", + "name": "step", "layout": { - "kind": "Bytes" + "name": "X_6", + "kind": "Ref" }, "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" } @@ -8974,7 +9958,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_8" + "title": "X_6" }, "encoding": { "tag_size": "Uint8", @@ -9003,17 +9987,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": "Uint30" }, { - "name": "batch", "layout": { - "kind": "String" + "layout": { + "name": "X_4", + "kind": "Ref" + }, + "kind": "Seq" }, + "kind": "anon", "data_kind": { "kind": "Variable" - }, - "kind": "named" + } } ], - "name": "Batch" + "name": "Dissection" }, { "tag": 1, @@ -9031,79 +10018,53 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "deposit", "layout": { - "name": "X_6", + "name": "X_3", "kind": "Ref" }, + "kind": "anon", "data_kind": { "kind": "Dynamic" - }, - "kind": "named" + } } ], - "name": "Deposit" + "name": "Proof" } ] } }, { "description": { - "title": "X_6" + "title": "X_4" }, "encoding": { "fields": [ { - "name": "sender", "layout": { - "name": "public_key_hash", + "name": "X_5", "kind": "Ref" }, + "kind": "anon", "data_kind": { - "size": 21, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "destination", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 20, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "ticket_hash", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" + "kind": "Dynamic" + } }, { - "name": "amount", "layout": { - "name": "X_7", + "name": "N.t", "kind": "Ref" }, + "kind": "anon", "data_kind": { "kind": "Dynamic" - }, - "kind": "named" + } } ] } }, { "description": { - "title": "X_7" + "title": "X_5" }, "encoding": { "tag_size": "Uint8", @@ -9128,17 +10089,16 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Uint8", - "kind": "Int" + "kind": "Zero_width" }, "kind": "anon", "data_kind": { - "size": 1, + "size": 0, "kind": "Float" } } ], - "name": "case 0" + "name": "None" }, { "tag": 1, @@ -9156,21 +10116,34 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { + "name": "state_hash", "layout": { - "size": "Uint16", - "kind": "Int" + "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "size": 2, + "size": 32, "kind": "Float" - } + }, + "kind": "named" } ], - "name": "case 1" - }, + "name": "Some" + } + ] + } + }, + { + "description": { + "title": "X_3" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ { - "tag": 2, + "tag": 0, "fields": [ { "name": "Tag", @@ -9186,137 +10159,39 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int32", - "kind": "Int" + "kind": "Bool" }, "kind": "anon", "data_kind": { - "size": 4, + "size": 1, "kind": "Float" } - } - ], - "name": "case 2" - }, - { - "tag": 3, - "fields": [ + }, { - "name": "Tag", "layout": { - "size": "Uint8", - "kind": "Int" + "kind": "Bytes" }, + "kind": "anon", "data_kind": { - "size": 1, + "size": 32, "kind": "Float" - }, - "kind": "named" + } }, { "layout": { - "size": "Int64", - "kind": "Int" + "kind": "Bytes" }, "kind": "anon", "data_kind": { - "size": 8, + "size": 32, "kind": "Float" } } ], - "name": "case 3" - } - ] - } - }, - { - "description": { - "title": "X_4" - }, - "encoding": { - "fields": [ - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "name": "contents", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" - }, - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "name": "ty", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" - }, - { - "name": "ticketer", - "layout": { - "name": "alpha.contract_id", - "kind": "Ref" - }, - "data_kind": { - "size": 22, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "amount", - "layout": { - "name": "X_7", - "kind": "Ref" - }, - "data_kind": { - "kind": "Dynamic" - }, - "kind": "named" + "name": "Proof of a normal computation step" }, { - "name": "claimer", - "layout": { - "name": "public_key_hash", - "kind": "Ref" - }, - "data_kind": { - "size": 21, - "kind": "Float" - }, - "kind": "named" - } - ] - } - }, - { - "description": { - "title": "alpha.contract_id" - }, - "encoding": { - "tag_size": "Uint8", - "kind": { - "size": 22, - "kind": "Float" - }, - "cases": [ - { - "tag": 0, + "tag": 1, "fields": [ { "name": "Tag", @@ -9331,124 +10206,75 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "Signature.Public_key_hash", - "layout": { - "name": "public_key_hash", - "kind": "Ref" - }, - "data_kind": { - "size": 21, - "kind": "Float" - }, - "kind": "named" - } - ], - "name": "Implicit" - }, - { - "tag": 1, - "fields": [ - { - "name": "Tag", "layout": { - "size": "Uint8", - "kind": "Int" + "kind": "Bool" }, + "kind": "anon", "data_kind": { "size": 1, "kind": "Float" - }, - "kind": "named" + } }, { - "name": "Contract_hash", "layout": { "kind": "Bytes" }, + "kind": "anon", "data_kind": { - "size": 20, + "size": 32, "kind": "Float" - }, - "kind": "named" + } }, { - "name": "padding", "layout": { - "kind": "Padding" + "kind": "Bytes" }, + "kind": "anon", "data_kind": { - "size": 1, + "size": 32, "kind": "Float" - }, - "kind": "named" + } } ], - "name": "Originated" - } - ] - } - }, - { - "description": { - "title": "X_3" - }, - "encoding": { - "tag_size": "Uint16", - "kind": { - "size": 2, - "kind": "Float" - }, - "cases": [ + "name": "Proof of an input step" + }, { - "tag": 0, + "tag": 2, "fields": [ { "name": "Tag", "layout": { - "size": "Uint16", + "size": "Uint8", "kind": "Int" }, "data_kind": { - "size": 2, + "size": 1, "kind": "Float" }, "kind": "named" }, { "layout": { - "kind": "Zero_width" + "kind": "Bool" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 1, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "Example_arith smart contract rollup kind" - } - ] - } - }, - { - "description": { - "title": "X_2" - }, - "encoding": { - "fields": [ - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "layout": { - "kind": "String" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } + "name": "Proof that the PVM is blocked" } ] } @@ -9460,62 +10286,26 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "encoding": { "fields": [ { - "name": "compressed_state", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "inbox_level", - "layout": { - "size": "Int32", - "kind": "Int" - }, - "data_kind": { - "size": 4, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "predecessor", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "number_of_messages", "layout": { - "size": "Int32", - "kind": "Int" + "name": "public_key_hash", + "kind": "Ref" }, + "kind": "anon", "data_kind": { - "size": 4, + "size": 21, "kind": "Float" - }, - "kind": "named" + } }, { - "name": "number_of_ticks", "layout": { - "size": "Int32", - "kind": "Int" + "name": "public_key_hash", + "kind": "Ref" }, + "kind": "anon", "data_kind": { - "size": 4, + "size": 21, "kind": "Float" - }, - "kind": "named" + } } ] } @@ -12687,6 +13477,196 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind" ], "additionalProperties": false + }, + { + "title": "Sc_rollup_refute", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "sc_rollup_refute" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "rollup": { + "$ref": "#/definitions/alpha.rollup_address" + }, + "opponent": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "refutation": { + "type": "object", + "properties": { + "choice": { + "$ref": "#/definitions/positive_bignum" + }, + "step": { + "oneOf": [ + { + "title": "Dissection", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "oneOf": [ + { + "title": "Some", + "$ref": "#/definitions/state_hash" + }, + { + "title": "None", + "type": "null" + } + ] + }, + { + "$ref": "#/definitions/positive_bignum" + } + ], + "additionalItems": false + } + }, + { + "title": "Proof", + "oneOf": [ + { + "title": "Proof of a normal computation step", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + }, + { + "title": "Proof of an input step", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + }, + { + "title": "Proof that the PVM is blocked", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + } + ] + } + ] + } + }, + "required": [ + "step", + "choice" + ], + "additionalProperties": false + } + }, + "required": [ + "refutation", + "opponent", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Sc_rollup_timeout", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "sc_rollup_timeout" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "rollup": { + "$ref": "#/definitions/alpha.rollup_address" + }, + "stakers": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + { + "$ref": "#/definitions/Signature.Public_key_hash" + } + ], + "additionalItems": false + } + }, + "required": [ + "stakers", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false } ] }, diff --git a/tezt/_regressions/rpc/alpha.proxy.mempool.out b/tezt/_regressions/rpc/alpha.proxy.mempool.out index 80ef49f6a42a..91eea241c1a7 100644 --- a/tezt/_regressions/rpc/alpha.proxy.mempool.out +++ b/tezt/_regressions/rpc/alpha.proxy.mempool.out @@ -3215,6 +3215,196 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind" ], "additionalProperties": false + }, + { + "title": "Sc_rollup_refute", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "sc_rollup_refute" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "rollup": { + "$ref": "#/definitions/alpha.rollup_address" + }, + "opponent": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "refutation": { + "type": "object", + "properties": { + "choice": { + "$ref": "#/definitions/positive_bignum" + }, + "step": { + "oneOf": [ + { + "title": "Dissection", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "oneOf": [ + { + "title": "Some", + "$ref": "#/definitions/state_hash" + }, + { + "title": "None", + "type": "null" + } + ] + }, + { + "$ref": "#/definitions/positive_bignum" + } + ], + "additionalItems": false + } + }, + { + "title": "Proof", + "oneOf": [ + { + "title": "Proof of a normal computation step", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + }, + { + "title": "Proof of an input step", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + }, + { + "title": "Proof that the PVM is blocked", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + } + ] + } + ] + } + }, + "required": [ + "step", + "choice" + ], + "additionalProperties": false + } + }, + "required": [ + "refutation", + "opponent", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Sc_rollup_timeout", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "sc_rollup_timeout" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "rollup": { + "$ref": "#/definitions/alpha.rollup_address" + }, + "stakers": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + { + "$ref": "#/definitions/Signature.Public_key_hash" + } + ], + "additionalItems": false + } + }, + "required": [ + "stakers", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false } ] }, @@ -4378,7 +4568,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "parameters", "layout": { - "name": "X_137", + "name": "X_143", "kind": "Ref" }, "data_kind": { @@ -5043,7 +5233,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "commitment", "layout": { - "name": "X_135", + "name": "X_141", "kind": "Ref" }, "data_kind": { @@ -5406,7 +5596,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "message", "layout": { - "name": "X_8", + "name": "X_14", "kind": "Ref" }, "data_kind": { @@ -5475,7 +5665,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "previous_message_result", "layout": { - "name": "X_9", + "name": "X_15", "kind": "Ref" }, "data_kind": { @@ -5505,7 +5695,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "proof", "layout": { - "name": "X_134", + "name": "X_140", "kind": "Ref" }, "data_kind": { @@ -5661,7 +5851,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "name": "tickets_info", "layout": { "layout": { - "name": "X_4", + "name": "X_10", "kind": "Ref" }, "kind": "Seq" @@ -5902,7 +6092,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "kind", "layout": { - "name": "X_3", + "name": "X_9", "kind": "Ref" }, "data_kind": { @@ -6025,7 +6215,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "name": "message", "layout": { "layout": { - "name": "X_2", + "name": "X_8", "kind": "Ref" }, "kind": "Seq" @@ -6229,7 +6419,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' { "name": "commitment", "layout": { - "name": "X_1", + "name": "X_7", "kind": "Ref" }, "data_kind": { @@ -6240,65 +6430,9 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' } ], "name": "Sc_rollup_publish" - } - ] - } - }, - { - "description": { - "title": "alpha.inlined.endorsement" - }, - "encoding": { - "fields": [ - { - "name": "branch", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "operations", - "layout": { - "name": "alpha.inlined.endorsement_mempool.contents", - "kind": "Ref" - }, - "data_kind": { - "size": 43, - "kind": "Float" - }, - "kind": "named" }, { - "name": "signature", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" - } - ] - } - }, - { - "description": { - "title": "alpha.inlined.endorsement_mempool.contents" - }, - "encoding": { - "tag_size": "Uint8", - "kind": { - "size": 43, - "kind": "Float" - }, - "cases": [ - { - "tag": 21, + "tag": 204, "fields": [ { "name": "Tag", @@ -6313,83 +6447,354 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "slot", + "name": "source", "layout": { - "size": "Uint16", - "kind": "Int" + "name": "public_key_hash", + "kind": "Ref" }, "data_kind": { - "size": 2, + "size": 21, "kind": "Float" }, "kind": "named" }, { - "name": "level", + "name": "fee", "layout": { - "size": "Int32", - "kind": "Int" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "size": 4, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" }, { - "name": "round", + "name": "counter", "layout": { - "size": "Int32", - "kind": "Int" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "size": 4, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" }, { - "name": "block_payload_hash", + "name": "gas_limit", "layout": { - "kind": "Bytes" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" - } - ], - "name": "Endorsement" - } - ] - } - }, - { - "description": { - "title": "alpha.block_header.alpha.full_header" - }, - "encoding": { - "fields": [ - { - "name": "level", - "layout": { - "size": "Int32", - "kind": "Int" - }, - "data_kind": { - "size": 4, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "proto", - "layout": { - "size": "Uint8", - "kind": "Int" - }, - "data_kind": { + }, + { + "name": "storage_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "kind": "dyn", + "name": "alpha.rollup_address", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "rollup", + "layout": { + "kind": "String" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + }, + { + "name": "opponent", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "refutation", + "layout": { + "name": "X_2", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ], + "name": "Sc_rollup_refute" + }, + { + "tag": 205, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "source", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "fee", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "counter", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "gas_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "storage_limit", + "layout": { + "name": "N.t", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "kind": "dyn", + "name": "alpha.rollup_address", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "rollup", + "layout": { + "kind": "String" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + }, + { + "name": "stakers", + "layout": { + "name": "X_1", + "kind": "Ref" + }, + "data_kind": { + "size": 42, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Sc_rollup_timeout" + } + ] + } + }, + { + "description": { + "title": "alpha.inlined.endorsement" + }, + "encoding": { + "fields": [ + { + "name": "branch", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "operations", + "layout": { + "name": "alpha.inlined.endorsement_mempool.contents", + "kind": "Ref" + }, + "data_kind": { + "size": 43, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "signature", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + } + ] + } + }, + { + "description": { + "title": "alpha.inlined.endorsement_mempool.contents" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "size": 43, + "kind": "Float" + }, + "cases": [ + { + "tag": 21, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "slot", + "layout": { + "size": "Uint16", + "kind": "Int" + }, + "data_kind": { + "size": 2, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "level", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "round", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "block_payload_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Endorsement" + } + ] + } + }, + { + "description": { + "title": "alpha.block_header.alpha.full_header" + }, + "encoding": { + "fields": [ + { + "name": "level", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "proto", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { "size": 1, "kind": "Float" }, @@ -6914,7 +7319,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_137" + "title": "X_143" }, "encoding": { "fields": [ @@ -7297,7 +7702,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_135" + "title": "X_141" }, "encoding": { "fields": [ @@ -7324,50 +7729,238 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "layout": { "kind": "Bytes" }, - "kind": "Seq" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" - }, - { - "name": "predecessor", - "layout": { - "name": "X_136", - "kind": "Ref" - }, - "data_kind": { - "kind": "Dynamic" - }, - "kind": "named" + "kind": "Seq" + }, + "data_kind": { + "kind": "Variable" + }, + "kind": "named" + }, + { + "name": "predecessor", + "layout": { + "name": "X_142", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "inbox_merkle_root", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + } + ] + } + }, + { + "description": { + "title": "X_142" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "kind": "Zero_width" + }, + "kind": "anon", + "data_kind": { + "size": 0, + "kind": "Float" + } + } + ], + "name": "None" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "Commitment_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + } + ], + "name": "Some" + } + ] + } + }, + { + "description": { + "title": "X_140" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "size": "Int16", + "kind": "Int" + }, + "kind": "anon", + "data_kind": { + "size": 2, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "name": "X_16", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } + } + ], + "name": "case 0" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "size": "Int16", + "kind": "Int" + }, + "kind": "anon", + "data_kind": { + "size": 2, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "name": "X_16", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } + } + ], + "name": "case 1" }, { - "name": "inbox_merkle_root", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" - } - ] - } - }, - { - "description": { - "title": "X_136" - }, - "encoding": { - "tag_size": "Uint8", - "kind": { - "kind": "Dynamic" - }, - "cases": [ - { - "tag": 0, + "tag": 2, "fields": [ { "name": "Tag", @@ -7383,19 +7976,50 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "kind": "Zero_width" + "size": "Int16", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 2, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } + }, + { + "layout": { + "name": "X_16", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } } ], - "name": "None" + "name": "case 2" }, { - "tag": 1, + "tag": 3, "fields": [ { "name": "Tag", @@ -7410,25 +8034,55 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "Commitment_hash", + "layout": { + "size": "Int16", + "kind": "Int" + }, + "kind": "anon", + "data_kind": { + "size": 2, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + }, + { "layout": { "kind": "Bytes" }, + "kind": "anon", "data_kind": { "size": 32, "kind": "Float" + } + }, + { + "layout": { + "name": "X_16", + "kind": "Ref" }, - "kind": "named" + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } } ], - "name": "Some" + "name": "case 3" } ] } }, { "description": { - "title": "X_134" + "title": "X_139" }, "encoding": { "tag_size": "Uint8", @@ -7453,50 +8107,71 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int16", + "size": "Uint8", "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 2, + "size": 1, "kind": "Float" } }, { "layout": { - "kind": "Bytes" + "name": "X_138", + "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 0, "kind": "Float" } + } + ], + "name": "case 0" + }, + { + "tag": 1, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" }, { "layout": { - "kind": "Bytes" + "size": "Uint16", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 2, "kind": "Float" } }, { "layout": { - "name": "X_10", + "name": "X_138", "kind": "Ref" }, "kind": "anon", "data_kind": { - "kind": "Dynamic" + "size": 0, + "kind": "Float" } } ], - "name": "case 0" + "name": "case 1" }, { - "tag": 1, + "tag": 2, "fields": [ { "name": "Tag", @@ -7512,50 +8187,71 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int16", + "size": "Int32", "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 2, + "size": 4, "kind": "Float" } }, { "layout": { - "kind": "Bytes" + "name": "X_138", + "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 0, "kind": "Float" } + } + ], + "name": "case 2" + }, + { + "tag": 3, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" }, { "layout": { - "kind": "Bytes" + "size": "Int64", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 8, "kind": "Float" } }, { "layout": { - "name": "X_10", + "name": "X_138", "kind": "Ref" }, "kind": "anon", "data_kind": { - "kind": "Dynamic" + "size": 0, + "kind": "Float" } } ], - "name": "case 1" + "name": "case 3" }, { - "tag": 2, + "tag": 4, "fields": [ { "name": "Tag", @@ -7571,50 +8267,71 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int16", + "size": "Uint8", "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 2, + "size": 1, "kind": "Float" } }, { "layout": { - "kind": "Bytes" + "name": "X_134", + "kind": "Ref" }, "kind": "anon", "data_kind": { "size": 32, "kind": "Float" } + } + ], + "name": "case 4" + }, + { + "tag": 5, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" }, { "layout": { - "kind": "Bytes" + "size": "Uint16", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 2, "kind": "Float" } }, { "layout": { - "name": "X_10", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "kind": "Dynamic" + "size": 32, + "kind": "Float" } } ], - "name": "case 2" + "name": "case 5" }, { - "tag": 3, + "tag": 6, "fields": [ { "name": "Tag", @@ -7630,63 +8347,71 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int16", + "size": "Int32", "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 2, + "size": 4, "kind": "Float" } }, { "layout": { - "kind": "Bytes" + "name": "X_134", + "kind": "Ref" }, "kind": "anon", "data_kind": { "size": 32, "kind": "Float" } + } + ], + "name": "case 6" + }, + { + "tag": 7, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" }, { "layout": { - "kind": "Bytes" + "size": "Int64", + "kind": "Int" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 8, "kind": "Float" } }, { "layout": { - "name": "X_10", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "kind": "Dynamic" + "size": 32, + "kind": "Float" } } ], - "name": "case 3" - } - ] - } - }, - { - "description": { - "title": "X_133" - }, - "encoding": { - "tag_size": "Uint8", - "kind": { - "kind": "Dynamic" - }, - "cases": [ + "name": "case 7" + }, { - "tag": 0, + "tag": 8, "fields": [ { "name": "Tag", @@ -7713,20 +8438,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_132", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" } } ], - "name": "case 0" + "name": "case 8" }, { - "tag": 1, + "tag": 9, "fields": [ { "name": "Tag", @@ -7753,20 +8478,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_132", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" } } ], - "name": "case 1" + "name": "case 9" }, { - "tag": 2, + "tag": 10, "fields": [ { "name": "Tag", @@ -7793,20 +8518,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_132", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" } } ], - "name": "case 2" + "name": "case 10" }, { - "tag": 3, + "tag": 11, "fields": [ { "name": "Tag", @@ -7833,20 +8558,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_132", + "name": "X_134", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" } } ], - "name": "case 3" + "name": "case 11" }, { - "tag": 4, + "tag": 12, "fields": [ { "name": "Tag", @@ -7873,20 +8598,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_128", + "name": "X_126", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 64, "kind": "Float" } } ], - "name": "case 4" + "name": "case 12" }, { - "tag": 5, + "tag": 13, "fields": [ { "name": "Tag", @@ -7913,20 +8638,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_128", + "name": "X_126", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 64, "kind": "Float" } } ], - "name": "case 5" + "name": "case 13" }, { - "tag": 6, + "tag": 14, "fields": [ { "name": "Tag", @@ -7953,20 +8678,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_128", + "name": "X_126", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 64, "kind": "Float" } } ], - "name": "case 6" + "name": "case 14" }, { - "tag": 7, + "tag": 15, "fields": [ { "name": "Tag", @@ -7993,20 +8718,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_128", + "name": "X_126", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 32, + "size": 64, "kind": "Float" } } ], - "name": "case 7" + "name": "case 15" }, { - "tag": 8, + "tag": 128, "fields": [ { "name": "Tag", @@ -8021,32 +8746,55 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { + "layout": { + "kind": "Zero_width" + }, + "kind": "anon", + "data_kind": { + "size": 0, + "kind": "Float" + } + } + ], + "name": "case 128" + }, + { + "tag": 129, + "fields": [ + { + "name": "Tag", "layout": { "size": "Uint8", "kind": "Int" }, - "kind": "anon", "data_kind": { "size": 1, "kind": "Float" - } + }, + "kind": "named" }, { "layout": { - "name": "X_128", - "kind": "Ref" + "layout": { + "name": "X_21", + "kind": "Ref" + }, + "kind": "Seq", + "length_limit": { + "kind": "exactly", + "exactly": 1 + } }, "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Variable" } } ], - "name": "case 8" + "name": "case 129" }, { - "tag": 9, + "tag": 130, "fields": [ { "name": "Tag", @@ -8062,31 +8810,62 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Uint16", - "kind": "Int" + "layout": { + "name": "X_21", + "kind": "Ref" + }, + "kind": "Seq", + "length_limit": { + "kind": "exactly", + "exactly": 2 + } }, "kind": "anon", "data_kind": { - "size": 2, - "kind": "Float" + "kind": "Variable" } + } + ], + "name": "case 130" + }, + { + "tag": 131, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" }, { "layout": { - "name": "X_128", - "kind": "Ref" + "layout": { + "name": "X_21", + "kind": "Ref" + }, + "kind": "Seq" }, "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Variable" } } ], - "name": "case 9" + "name": "case 131" }, { - "tag": 10, + "tag": 192, "fields": [ { "name": "Tag", @@ -8101,32 +8880,24 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "layout": { - "size": "Int32", - "kind": "Int" - }, - "kind": "anon", - "data_kind": { - "size": 4, - "kind": "Float" - } + "kind": "dyn", + "num_fields": 1, + "size": "Uint8" }, { "layout": { - "name": "X_128", - "kind": "Ref" + "kind": "Bytes" }, "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Variable" } } ], - "name": "case 10" + "name": "case 192" }, { - "tag": 11, + "tag": 193, "fields": [ { "name": "Tag", @@ -8140,33 +8911,57 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, "kind": "named" }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint16" + }, { "layout": { - "size": "Int64", - "kind": "Int" + "kind": "Bytes" }, "kind": "anon", "data_kind": { - "size": 8, - "kind": "Float" + "kind": "Variable" } + } + ], + "name": "case 193" + }, + { + "tag": 195, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint8", + "kind": "Int" + }, + "data_kind": { + "size": 1, + "kind": "Float" + }, + "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" }, { "layout": { - "name": "X_128", - "kind": "Ref" + "kind": "Bytes" }, "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Variable" } } ], - "name": "case 11" + "name": "case 195" }, { - "tag": 12, + "tag": 224, "fields": [ { "name": "Tag", @@ -8193,20 +8988,29 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_120", + "name": "X_121", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 64, + "kind": "Dynamic" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "case 12" + "name": "case 224" }, { - "tag": 13, + "tag": 225, "fields": [ { "name": "Tag", @@ -8233,20 +9037,29 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_120", + "name": "X_121", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 64, + "kind": "Dynamic" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "case 13" + "name": "case 225" }, { - "tag": 14, + "tag": 226, "fields": [ { "name": "Tag", @@ -8273,20 +9086,29 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_120", + "name": "X_121", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 64, + "kind": "Dynamic" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "case 14" + "name": "case 226" }, { - "tag": 15, + "tag": 227, "fields": [ { "name": "Tag", @@ -8313,20 +9135,99 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "name": "X_120", + "name": "X_121", "kind": "Ref" }, "kind": "anon", "data_kind": { - "size": 64, + "kind": "Dynamic" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "case 15" + "name": "case 227" + } + ] + } + }, + { + "description": { + "title": "X_138" + }, + "encoding": { + "fields": [] + } + }, + { + "description": { + "title": "X_134" + }, + "encoding": { + "fields": [ + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + } + ] + } + }, + { + "description": { + "title": "X_126" + }, + "encoding": { + "fields": [ + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } }, { - "tag": 128, + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, + "kind": "Float" + } + } + ] + } + }, + { + "description": { + "title": "X_122" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "size": 33, + "kind": "Float" + }, + "cases": [ + { + "tag": 0, "fields": [ { "name": "Tag", @@ -8341,20 +9242,21 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { + "name": "Context_hash", "layout": { - "kind": "Zero_width" + "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "size": 0, + "size": 32, "kind": "Float" - } + }, + "kind": "named" } ], - "name": "case 128" + "name": "case 0" }, { - "tag": 129, + "tag": 1, "fields": [ { "name": "Tag", @@ -8369,62 +9271,145 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { + "name": "Context_hash", "layout": { - "layout": { - "name": "X_15", - "kind": "Ref" - }, - "kind": "Seq", - "length_limit": { - "kind": "exactly", - "exactly": 1 - } + "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "kind": "Variable" - } + "size": 32, + "kind": "Float" + }, + "kind": "named" } ], - "name": "case 129" + "name": "case 1" + } + ] + } + }, + { + "description": { + "title": "X_121" + }, + "encoding": { + "fields": [ + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint8" + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "kind": "Variable" + } + } + ] + } + }, + { + "description": { + "title": "X_16" + }, + "encoding": { + "fields": [ + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "layout": { + "layout": { + "name": "X_139", + "kind": "Ref" + }, + "kind": "Seq" + }, + "kind": "anon", + "data_kind": { + "kind": "Variable" + } + } + ] + } + }, + { + "description": { + "title": "X_21" + }, + "encoding": { + "fields": [ + { + "layout": { + "name": "X_121", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "kind": "Dynamic" + } + }, + { + "layout": { + "name": "X_122", + "kind": "Ref" + }, + "kind": "anon", + "data_kind": { + "size": 33, + "kind": "Float" + } + } + ] + } + }, + { + "description": { + "title": "X_15" + }, + "encoding": { + "fields": [ + { + "name": "context_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" }, { - "tag": 130, - "fields": [ - { - "name": "Tag", - "layout": { - "size": "Uint8", - "kind": "Int" - }, - "data_kind": { - "size": 1, - "kind": "Float" - }, - "kind": "named" - }, - { - "layout": { - "layout": { - "name": "X_15", - "kind": "Ref" - }, - "kind": "Seq", - "length_limit": { - "kind": "exactly", - "exactly": 2 - } - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } - } - ], - "name": "case 130" - }, + "name": "withdraw_list_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + } + ] + } + }, + { + "description": { + "title": "X_14" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ { - "tag": 131, + "tag": 0, "fields": [ { "name": "Tag", @@ -8444,55 +9429,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": "Uint30" }, { + "name": "batch", "layout": { - "layout": { - "name": "X_15", - "kind": "Ref" - }, - "kind": "Seq" + "kind": "String" }, - "kind": "anon", "data_kind": { "kind": "Variable" - } - } - ], - "name": "case 131" - }, - { - "tag": 192, - "fields": [ - { - "name": "Tag", - "layout": { - "size": "Uint8", - "kind": "Int" - }, - "data_kind": { - "size": 1, - "kind": "Float" }, "kind": "named" - }, - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint8" - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } } ], - "name": "case 192" + "name": "Batch" }, { - "tag": 193, + "tag": 1, "fields": [ { "name": "Tag", @@ -8507,56 +9457,88 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "kind": "dyn", - "num_fields": 1, - "size": "Uint16" - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } - } - ], - "name": "case 193" - }, - { - "tag": 195, - "fields": [ - { - "name": "Tag", + "name": "deposit", "layout": { - "size": "Uint8", - "kind": "Int" + "name": "X_12", + "kind": "Ref" }, "data_kind": { - "size": 1, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" - }, - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } } ], - "name": "case 195" + "name": "Deposit" + } + ] + } + }, + { + "description": { + "title": "X_12" + }, + "encoding": { + "fields": [ + { + "name": "sender", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, + "kind": "Float" + }, + "kind": "named" }, { - "tag": 224, + "name": "destination", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 20, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "ticket_hash", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "amount", + "layout": { + "name": "X_13", + "kind": "Ref" + }, + "data_kind": { + "kind": "Dynamic" + }, + "kind": "named" + } + ] + } + }, + { + "description": { + "title": "X_13" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ + { + "tag": 0, "fields": [ { "name": "Tag", @@ -8580,32 +9562,12 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": 1, "kind": "Float" } - }, - { - "layout": { - "name": "X_115", - "kind": "Ref" - }, - "kind": "anon", - "data_kind": { - "kind": "Dynamic" - } - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "size": 32, - "kind": "Float" - } } ], - "name": "case 224" + "name": "case 0" }, { - "tag": 225, + "tag": 1, "fields": [ { "name": "Tag", @@ -8629,32 +9591,12 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": 2, "kind": "Float" } - }, - { - "layout": { - "name": "X_115", - "kind": "Ref" - }, - "kind": "anon", - "data_kind": { - "kind": "Dynamic" - } - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "size": 32, - "kind": "Float" - } } ], - "name": "case 225" + "name": "case 1" }, { - "tag": 226, + "tag": 2, "fields": [ { "name": "Tag", @@ -8678,32 +9620,12 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": 4, "kind": "Float" } - }, - { - "layout": { - "name": "X_115", - "kind": "Ref" - }, - "kind": "anon", - "data_kind": { - "kind": "Dynamic" - } - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "size": 32, - "kind": "Float" - } } ], - "name": "case 226" + "name": "case 2" }, { - "tag": 227, + "tag": 3, "fields": [ { "name": "Tag", @@ -8727,97 +9649,95 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": 8, "kind": "Float" } - }, - { - "layout": { - "name": "X_115", - "kind": "Ref" - }, - "kind": "anon", - "data_kind": { - "kind": "Dynamic" - } - }, - { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "size": 32, - "kind": "Float" - } } ], - "name": "case 227" + "name": "case 3" } ] } }, { "description": { - "title": "X_132" - }, - "encoding": { - "fields": [] - } - }, - { - "description": { - "title": "X_128" + "title": "X_10" }, "encoding": { "fields": [ { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, + { + "name": "contents", "layout": { "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "size": 32, - "kind": "Float" - } - } - ] - } - }, - { - "description": { - "title": "X_120" - }, - "encoding": { - "fields": [ + "kind": "Variable" + }, + "kind": "named" + }, + { + "kind": "dyn", + "num_fields": 1, + "size": "Uint30" + }, { + "name": "ty", "layout": { "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "size": 32, + "kind": "Variable" + }, + "kind": "named" + }, + { + "name": "ticketer", + "layout": { + "name": "alpha.contract_id", + "kind": "Ref" + }, + "data_kind": { + "size": 22, "kind": "Float" - } + }, + "kind": "named" }, { + "name": "amount", "layout": { - "kind": "Bytes" + "name": "X_13", + "kind": "Ref" }, - "kind": "anon", "data_kind": { - "size": 32, + "kind": "Dynamic" + }, + "kind": "named" + }, + { + "name": "claimer", + "layout": { + "name": "public_key_hash", + "kind": "Ref" + }, + "data_kind": { + "size": 21, "kind": "Float" - } + }, + "kind": "named" } ] } }, { "description": { - "title": "X_116" + "title": "alpha.contract_id" }, "encoding": { "tag_size": "Uint8", "kind": { - "size": 33, + "size": 22, "kind": "Float" }, "cases": [ @@ -8837,18 +9757,19 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "Context_hash", + "name": "Signature.Public_key_hash", "layout": { - "kind": "Bytes" + "name": "public_key_hash", + "kind": "Ref" }, "data_kind": { - "size": 32, + "size": 21, "kind": "Float" }, "kind": "named" } ], - "name": "case 0" + "name": "Implicit" }, { "tag": 1, @@ -8866,48 +9787,78 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "Context_hash", + "name": "Contract_hash", "layout": { "kind": "Bytes" }, "data_kind": { - "size": 32, + "size": 20, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "padding", + "layout": { + "kind": "Padding" + }, + "data_kind": { + "size": 1, "kind": "Float" }, "kind": "named" } ], - "name": "case 1" + "name": "Originated" } ] } }, { "description": { - "title": "X_115" + "title": "X_9" }, "encoding": { - "fields": [ - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint8" - }, + "tag_size": "Uint16", + "kind": { + "size": 2, + "kind": "Float" + }, + "cases": [ { - "layout": { - "kind": "Bytes" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } + "tag": 0, + "fields": [ + { + "name": "Tag", + "layout": { + "size": "Uint16", + "kind": "Int" + }, + "data_kind": { + "size": 2, + "kind": "Float" + }, + "kind": "named" + }, + { + "layout": { + "kind": "Zero_width" + }, + "kind": "anon", + "data_kind": { + "size": 0, + "kind": "Float" + } + } + ], + "name": "Example_arith smart contract rollup kind" } ] } }, { "description": { - "title": "X_10" + "title": "X_8" }, "encoding": { "fields": [ @@ -8918,11 +9869,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "layout": { - "name": "X_133", - "kind": "Ref" - }, - "kind": "Seq" + "kind": "String" }, "kind": "anon", "data_kind": { @@ -8934,59 +9881,96 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_15" + "title": "X_7" }, "encoding": { "fields": [ { + "name": "compressed_state", "layout": { - "name": "X_115", - "kind": "Ref" + "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "kind": "Dynamic" - } + "size": 32, + "kind": "Float" + }, + "kind": "named" }, { + "name": "inbox_level", "layout": { - "name": "X_116", - "kind": "Ref" + "size": "Int32", + "kind": "Int" }, - "kind": "anon", "data_kind": { - "size": 33, + "size": 4, "kind": "Float" - } + }, + "kind": "named" + }, + { + "name": "predecessor", + "layout": { + "kind": "Bytes" + }, + "data_kind": { + "size": 32, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "number_of_messages", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" + }, + { + "name": "number_of_ticks", + "layout": { + "size": "Int32", + "kind": "Int" + }, + "data_kind": { + "size": 4, + "kind": "Float" + }, + "kind": "named" } ] } }, { "description": { - "title": "X_9" + "title": "X_2" }, "encoding": { "fields": [ { - "name": "context_hash", + "name": "choice", "layout": { - "kind": "Bytes" + "name": "N.t", + "kind": "Ref" }, "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" }, { - "name": "withdraw_list_hash", + "name": "step", "layout": { - "kind": "Bytes" + "name": "X_6", + "kind": "Ref" }, "data_kind": { - "size": 32, - "kind": "Float" + "kind": "Dynamic" }, "kind": "named" } @@ -8995,7 +9979,7 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "description": { - "title": "X_8" + "title": "X_6" }, "encoding": { "tag_size": "Uint8", @@ -9024,17 +10008,20 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "size": "Uint30" }, { - "name": "batch", "layout": { - "kind": "String" + "layout": { + "name": "X_4", + "kind": "Ref" + }, + "kind": "Seq" }, + "kind": "anon", "data_kind": { "kind": "Variable" - }, - "kind": "named" + } } ], - "name": "Batch" + "name": "Dissection" }, { "tag": 1, @@ -9052,79 +10039,53 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "deposit", "layout": { - "name": "X_6", + "name": "X_3", "kind": "Ref" }, + "kind": "anon", "data_kind": { "kind": "Dynamic" - }, - "kind": "named" + } } ], - "name": "Deposit" + "name": "Proof" } ] } }, { "description": { - "title": "X_6" + "title": "X_4" }, "encoding": { "fields": [ { - "name": "sender", "layout": { - "name": "public_key_hash", + "name": "X_5", "kind": "Ref" }, + "kind": "anon", "data_kind": { - "size": 21, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "destination", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 20, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "ticket_hash", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" + "kind": "Dynamic" + } }, { - "name": "amount", "layout": { - "name": "X_7", + "name": "N.t", "kind": "Ref" }, + "kind": "anon", "data_kind": { "kind": "Dynamic" - }, - "kind": "named" + } } ] } }, { "description": { - "title": "X_7" + "title": "X_5" }, "encoding": { "tag_size": "Uint8", @@ -9149,17 +10110,16 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Uint8", - "kind": "Int" + "kind": "Zero_width" }, "kind": "anon", "data_kind": { - "size": 1, + "size": 0, "kind": "Float" } } ], - "name": "case 0" + "name": "None" }, { "tag": 1, @@ -9177,21 +10137,34 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { + "name": "state_hash", "layout": { - "size": "Uint16", - "kind": "Int" + "kind": "Bytes" }, - "kind": "anon", "data_kind": { - "size": 2, + "size": 32, "kind": "Float" - } + }, + "kind": "named" } ], - "name": "case 1" - }, + "name": "Some" + } + ] + } + }, + { + "description": { + "title": "X_3" + }, + "encoding": { + "tag_size": "Uint8", + "kind": { + "kind": "Dynamic" + }, + "cases": [ { - "tag": 2, + "tag": 0, "fields": [ { "name": "Tag", @@ -9207,137 +10180,39 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' }, { "layout": { - "size": "Int32", - "kind": "Int" + "kind": "Bool" }, "kind": "anon", "data_kind": { - "size": 4, + "size": 1, "kind": "Float" } - } - ], - "name": "case 2" - }, - { - "tag": 3, - "fields": [ + }, { - "name": "Tag", "layout": { - "size": "Uint8", - "kind": "Int" + "kind": "Bytes" }, + "kind": "anon", "data_kind": { - "size": 1, + "size": 32, "kind": "Float" - }, - "kind": "named" + } }, { "layout": { - "size": "Int64", - "kind": "Int" + "kind": "Bytes" }, "kind": "anon", "data_kind": { - "size": 8, + "size": 32, "kind": "Float" } } ], - "name": "case 3" - } - ] - } - }, - { - "description": { - "title": "X_4" - }, - "encoding": { - "fields": [ - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "name": "contents", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" - }, - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "name": "ty", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "kind": "Variable" - }, - "kind": "named" - }, - { - "name": "ticketer", - "layout": { - "name": "alpha.contract_id", - "kind": "Ref" - }, - "data_kind": { - "size": 22, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "amount", - "layout": { - "name": "X_7", - "kind": "Ref" - }, - "data_kind": { - "kind": "Dynamic" - }, - "kind": "named" + "name": "Proof of a normal computation step" }, { - "name": "claimer", - "layout": { - "name": "public_key_hash", - "kind": "Ref" - }, - "data_kind": { - "size": 21, - "kind": "Float" - }, - "kind": "named" - } - ] - } - }, - { - "description": { - "title": "alpha.contract_id" - }, - "encoding": { - "tag_size": "Uint8", - "kind": { - "size": 22, - "kind": "Float" - }, - "cases": [ - { - "tag": 0, + "tag": 1, "fields": [ { "name": "Tag", @@ -9352,124 +10227,75 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind": "named" }, { - "name": "Signature.Public_key_hash", - "layout": { - "name": "public_key_hash", - "kind": "Ref" - }, - "data_kind": { - "size": 21, - "kind": "Float" - }, - "kind": "named" - } - ], - "name": "Implicit" - }, - { - "tag": 1, - "fields": [ - { - "name": "Tag", "layout": { - "size": "Uint8", - "kind": "Int" + "kind": "Bool" }, + "kind": "anon", "data_kind": { "size": 1, "kind": "Float" - }, - "kind": "named" + } }, { - "name": "Contract_hash", "layout": { "kind": "Bytes" }, + "kind": "anon", "data_kind": { - "size": 20, + "size": 32, "kind": "Float" - }, - "kind": "named" + } }, { - "name": "padding", "layout": { - "kind": "Padding" + "kind": "Bytes" }, + "kind": "anon", "data_kind": { - "size": 1, + "size": 32, "kind": "Float" - }, - "kind": "named" + } } ], - "name": "Originated" - } - ] - } - }, - { - "description": { - "title": "X_3" - }, - "encoding": { - "tag_size": "Uint16", - "kind": { - "size": 2, - "kind": "Float" - }, - "cases": [ + "name": "Proof of an input step" + }, { - "tag": 0, + "tag": 2, "fields": [ { "name": "Tag", "layout": { - "size": "Uint16", + "size": "Uint8", "kind": "Int" }, "data_kind": { - "size": 2, + "size": 1, "kind": "Float" }, "kind": "named" }, { "layout": { - "kind": "Zero_width" + "kind": "Bool" }, "kind": "anon", "data_kind": { - "size": 0, + "size": 1, + "kind": "Float" + } + }, + { + "layout": { + "kind": "Bytes" + }, + "kind": "anon", + "data_kind": { + "size": 32, "kind": "Float" } } ], - "name": "Example_arith smart contract rollup kind" - } - ] - } - }, - { - "description": { - "title": "X_2" - }, - "encoding": { - "fields": [ - { - "kind": "dyn", - "num_fields": 1, - "size": "Uint30" - }, - { - "layout": { - "kind": "String" - }, - "kind": "anon", - "data_kind": { - "kind": "Variable" - } + "name": "Proof that the PVM is blocked" } ] } @@ -9481,62 +10307,26 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "encoding": { "fields": [ { - "name": "compressed_state", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "inbox_level", - "layout": { - "size": "Int32", - "kind": "Int" - }, - "data_kind": { - "size": 4, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "predecessor", - "layout": { - "kind": "Bytes" - }, - "data_kind": { - "size": 32, - "kind": "Float" - }, - "kind": "named" - }, - { - "name": "number_of_messages", "layout": { - "size": "Int32", - "kind": "Int" + "name": "public_key_hash", + "kind": "Ref" }, + "kind": "anon", "data_kind": { - "size": 4, + "size": 21, "kind": "Float" - }, - "kind": "named" + } }, { - "name": "number_of_ticks", "layout": { - "size": "Int32", - "kind": "Int" + "name": "public_key_hash", + "kind": "Ref" }, + "kind": "anon", "data_kind": { - "size": 4, + "size": 21, "kind": "Float" - }, - "kind": "named" + } } ] } @@ -12708,6 +13498,196 @@ curl -s 'http://localhost:[PORT]/describe/chains/main/mempool?recurse=yes' "kind" ], "additionalProperties": false + }, + { + "title": "Sc_rollup_refute", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "sc_rollup_refute" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "rollup": { + "$ref": "#/definitions/alpha.rollup_address" + }, + "opponent": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "refutation": { + "type": "object", + "properties": { + "choice": { + "$ref": "#/definitions/positive_bignum" + }, + "step": { + "oneOf": [ + { + "title": "Dissection", + "type": "array", + "items": { + "type": "array", + "items": [ + { + "oneOf": [ + { + "title": "Some", + "$ref": "#/definitions/state_hash" + }, + { + "title": "None", + "type": "null" + } + ] + }, + { + "$ref": "#/definitions/positive_bignum" + } + ], + "additionalItems": false + } + }, + { + "title": "Proof", + "oneOf": [ + { + "title": "Proof of a normal computation step", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + }, + { + "title": "Proof of an input step", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + }, + { + "title": "Proof that the PVM is blocked", + "type": "array", + "items": [ + { + "type": "boolean" + }, + { + "$ref": "#/definitions/state_hash" + } + ], + "additionalItems": false + } + ] + } + ] + } + }, + "required": [ + "step", + "choice" + ], + "additionalProperties": false + } + }, + "required": [ + "refutation", + "opponent", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false + }, + { + "title": "Sc_rollup_timeout", + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "sc_rollup_timeout" + ] + }, + "source": { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + "fee": { + "$ref": "#/definitions/alpha.mutez" + }, + "counter": { + "$ref": "#/definitions/positive_bignum" + }, + "gas_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "storage_limit": { + "$ref": "#/definitions/positive_bignum" + }, + "rollup": { + "$ref": "#/definitions/alpha.rollup_address" + }, + "stakers": { + "type": "array", + "items": [ + { + "$ref": "#/definitions/Signature.Public_key_hash" + }, + { + "$ref": "#/definitions/Signature.Public_key_hash" + } + ], + "additionalItems": false + } + }, + "required": [ + "stakers", + "rollup", + "storage_limit", + "gas_limit", + "counter", + "fee", + "source", + "kind" + ], + "additionalProperties": false } ] }, -- GitLab From 989c7d9e851f3d41285eabd39cfb75d96415be3f Mon Sep 17 00:00:00 2001 From: Thomas Athorne Date: Thu, 28 Apr 2022 12:04:30 +0100 Subject: [PATCH 4/4] Proto: SCURU: lots of rework from review comments --- .../lib_client/operation_result.ml | 4 +- .../lib_protocol/alpha_context.mli | 14 +- src/proto_alpha/lib_protocol/apply.ml | 7 +- .../lib_protocol/sc_rollup_game_repr.ml | 10 +- .../lib_protocol/sc_rollup_game_repr.mli | 48 +- .../lib_protocol/sc_rollup_storage.ml | 65 +- .../lib_protocol/sc_rollup_storage.mli | 101 +- src/proto_alpha/lib_protocol/storage.ml | 13 + src/proto_alpha/lib_protocol/storage.mli | 19 +- src/proto_alpha/lib_protocol/test/pbt/dune | 1 - .../test/pbt/refutation_game_pbt.ml | 876 ------------------ src/proto_alpha/lib_tx_rollup/l1_operation.ml | 4 + 12 files changed, 180 insertions(+), 982 deletions(-) delete mode 100644 src/proto_alpha/lib_protocol/test/pbt/refutation_game_pbt.ml diff --git a/src/proto_alpha/lib_client/operation_result.ml b/src/proto_alpha/lib_client/operation_result.ml index 279d8c173f56..b7b894f7c6c1 100644 --- a/src/proto_alpha/lib_client/operation_result.ml +++ b/src/proto_alpha/lib_client/operation_result.ml @@ -330,8 +330,8 @@ let pp_manager_operation_content (type kind) source internal pp_result ppf | Sc_rollup_timeout {rollup; stakers} -> Format.fprintf ppf - "@[Punish one of the two stakers %a and %a by timeout in the smart \ - contract rollup at address %a%a@]" + "@[Punish one of the two stakers %a and %a by timeout in the \ + smart contract rollup at address %a%a@]" Sc_rollup.Staker.pp (fst stakers) Sc_rollup.Staker.pp diff --git a/src/proto_alpha/lib_protocol/alpha_context.mli b/src/proto_alpha/lib_protocol/alpha_context.mli index 175ebf15a967..eb9ca6c4b6c3 100644 --- a/src/proto_alpha/lib_protocol/alpha_context.mli +++ b/src/proto_alpha/lib_protocol/alpha_context.mli @@ -2703,23 +2703,23 @@ module Sc_rollup : sig val update_game : context -> t -> - refuter:Staker.t -> - defender:Staker.t -> + player:Staker.t -> + opponent:Staker.t -> Game.refutation -> (Game.outcome option * context) tzresult Lwt.t - val apply_outcome : + val timeout : context -> t -> Staker.t * Staker.t -> - Game.outcome -> - (Game.status * context) tzresult Lwt.t + (Game.outcome * context) tzresult Lwt.t - val timeout : + val apply_outcome : context -> t -> Staker.t * Staker.t -> - (Game.outcome * context) tzresult Lwt.t + Game.outcome -> + (Game.status * context) tzresult Lwt.t module Internal_for_tests : sig val originated_sc_rollup : Origination_nonce.Internal_for_tests.t -> t diff --git a/src/proto_alpha/lib_protocol/apply.ml b/src/proto_alpha/lib_protocol/apply.ml index ce9d8c8b33d3..1b3d01fe2517 100644 --- a/src/proto_alpha/lib_protocol/apply.ml +++ b/src/proto_alpha/lib_protocol/apply.ml @@ -1761,12 +1761,7 @@ let apply_external_manager_operation_content : let result = Sc_rollup_publish_result {staked_hash; consumed_gas} in return (ctxt, result, []) | Sc_rollup_refute {rollup; opponent; refutation} -> - Sc_rollup.update_game - ctxt - rollup - ~refuter:source - ~defender:opponent - refutation + Sc_rollup.update_game ctxt rollup ~player:source ~opponent refutation >>=? fun (outcome, ctxt) -> (match outcome with | None -> return (Sc_rollup.Game.Ongoing, ctxt) diff --git a/src/proto_alpha/lib_protocol/sc_rollup_game_repr.ml b/src/proto_alpha/lib_protocol/sc_rollup_game_repr.ml index c437a2f567c3..e0df36088222 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_game_repr.ml +++ b/src/proto_alpha/lib_protocol/sc_rollup_game_repr.ml @@ -27,7 +27,7 @@ open Sc_rollup_repr module Proof = struct - (* TODO #2759: these proof cases are dummy ones for testing. Replace + (* TODO: #2759 these proof cases are dummy ones for testing. Replace them with the proper proofs. *) type t = | Computation_step of { @@ -69,24 +69,24 @@ module Proof = struct (fun (valid, start) -> Blocked_step {valid; start}); ]) - (* TODO #2759 *) + (* TODO: #2759 *) let pp _ _ = () - (* TODO #2759 *) + (* TODO: #2759 *) let start p = match p with | Computation_step x -> x.start | Input_step x -> x.start | Blocked_step x -> x.start - (* TODO #2759 *) + (* TODO: #2759 *) let stop p = match p with | Computation_step x -> Some x.stop | Input_step x -> Some x.stop | Blocked_step _ -> None - (* TODO #2759 *) + (* TODO: #2759 *) let valid p = match p with | Computation_step x -> x.valid diff --git a/src/proto_alpha/lib_protocol/sc_rollup_game_repr.mli b/src/proto_alpha/lib_protocol/sc_rollup_game_repr.mli index 701e3043fa65..04c4a6705bd0 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_game_repr.mli +++ b/src/proto_alpha/lib_protocol/sc_rollup_game_repr.mli @@ -27,7 +27,7 @@ (** The smart contract rollup refutation game types are defined here, as well as the basic pure logic for: - - how to create a new game from a pair of commits in the commit tree + - how to create a new game from a pair of commits in the commit tree; - how to update a game or complete a game when a move is played. @@ -38,8 +38,8 @@ ==================== At any given moment, the game stores a list [dissection] of state - hashes and tick counts. These are the claims made by the player who - has just moved about the PVM history. + hashes and tick counts. These are the claims about the PVM history + made by the player who has just moved. The next player to move will specify a tick count which appears in the [dissection]; this is the last of the state hashes which she @@ -58,11 +58,13 @@ Initializing a game =================== - The game begins when the refuter plays a first move. + In order to trigger the start of a game, one player must publish a + first move. - At this point the [initial] function will be called to convert the - defender's commitment into an initial [dissection]. The first move - is immediately applied to this to give the first state of the game. + The [initial] function is called at this point. It converts a + parent-child pair of commitments (belonging to the other player) into + an initial [dissection]. The first move is immediately applied to + this to give the first state of the game. Note: it is quite possible for the game to end immediately after this first move, either if the commitment has a tick count of one or @@ -96,7 +98,8 @@ P2' - If [dissection] is dishonest, the next player has a winning strategy. - This allows us to see that + This allows us to see the following. (We use [refuter] to mean the + first player to move, and [defender] to mean the other player.) Honest refuter wins: An honest refuter will be refuting a dishonest commitment, because @@ -133,7 +136,7 @@ module Proof : sig will include a proof that the machine is in a blocked state and a proof that the next message to be read is correct. The inbox proof part of this will refer to the [inbox_snapshot] stored in the game - type. + type (see {!Sc_rollup_game_repr.t}). [Blocked_step]: similar to an input step, this is a step where the machine is in a blocked state. However, it includes a proof that @@ -163,9 +166,9 @@ module Proof : sig val valid : t -> bool end -(** The two stakers index the game in the storage, as an ordered pair - of public key hashes. We use [Alice] and [Bob] to represent the - first and second player respectively. *) +(** The two stakers index the game in the storage as a pair of public + key hashes which is in lexical order. We use [Alice] and [Bob] to + represent the first and second player in the pair respectively. *) type player = Alice | Bob (** @@ -205,6 +208,8 @@ val pp : Format.formatter -> t -> unit module Index : sig type t = Staker.t * Staker.t + (** [to_path i p] returns a new path with the path to the game indexed + by [i] added as a prefix to path [p]. See [Path_encoding] module. *) val to_path : t -> string list -> string list val of_path : string list -> t option @@ -217,8 +222,8 @@ module Index : sig val compare : t -> t -> int - (** The 'normal form' for indices is when the two stakers are - ordered (we just use [Staker.compare]). *) + (** The 'normal form' for indices is when the two stakers appear in + the pair in lexical order (we just use [Staker.compare]). *) val normalize : t -> t (** Given an index in normal form, resolve a given [player] ([Alice] @@ -228,7 +233,7 @@ end (** To begin a game, first the conflict point in the commit tree is found, and then this function is applied. - + [initial inbox parent child refuter defender] will construct an initial game where [refuter] is next to play. The game has [dissection] with three states: @@ -242,7 +247,7 @@ end - thirdly, a [None] state which is a single tick after the [child] commitment. This represents the claim, implicit in the commitment, that the state given is blocked. - + This gives [refuter] a binary choice: she can refute the commit itself by providing a new dissection between the two committed states, or she can refute the claim that the [child] commit is a @@ -294,9 +299,10 @@ val pp_status : Format.formatter -> status -> unit val status_encoding : status Data_encoding.t (** A game ends with a single [loser] and the [reason] for the game - ending. This type is 'internal' to the game logic, it uses - [Alice] or [Bob] to refer to the players without knowing which - stakers they are. *) + ending. This type uses [Alice] or [Bob] to refer to the players + without knowing which stakers they are---so it cannot identify an + actual staker who should be punished without the associated game + index. *) type outcome = {loser : player; reason : reason} val pp_outcome : Format.formatter -> outcome -> unit @@ -305,8 +311,8 @@ val outcome_encoding : outcome Data_encoding.t (** Applies the move [refutation] to the game. Checks the move is valid and returns an [Invalid_move] outcome if not. - + In the case of the game continuing, this swaps the current player and updates the [dissection]. In the case of a [Proof] - being provided this returns a [Conflict_resolved] outcome. *) + being provided this returns an [outcome]. *) val play : t -> refutation -> (outcome, t) Either.t diff --git a/src/proto_alpha/lib_protocol/sc_rollup_storage.ml b/src/proto_alpha/lib_protocol/sc_rollup_storage.ml index 78f4a182a72d..4ba49deffe36 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_storage.ml +++ b/src/proto_alpha/lib_protocol/sc_rollup_storage.ml @@ -44,6 +44,7 @@ type error += | (* `Temporary *) Sc_rollup_max_number_of_available_messages_reached | (* `Temporary *) Sc_rollup_wrong_turn | (* `Temporary *) Sc_rollup_no_game + | (* `Temporary *) Sc_rollup_staker_in_game | (* `Temporary *) Sc_rollup_timeout_level_not_reached let () = @@ -57,6 +58,14 @@ let () = | Sc_rollup_max_number_of_available_messages_reached -> Some () | _ -> None) (fun () -> Sc_rollup_max_number_of_available_messages_reached) ; + register_error_kind + `Temporary + ~id:"Sc_rollup_staker_in_game" + ~title:"Staker is already playing a game" + ~description:"Attempt to start a game where one staker is already busy" + Data_encoding.unit + (function Sc_rollup_staker_in_game -> Some () | _ -> None) + (fun () -> Sc_rollup_staker_in_game) ; register_error_kind `Temporary ~id:"Sc_rollup_timeout_level_not_reached" @@ -811,12 +820,12 @@ let last_cemented_commitment_hash_with_level ctxt rollup = in (commitment_hash, inbox_level, ctxt) -(** TODO #2902: replace with protocol constant and consider good value. *) -let timeout_period = 500 +(** TODO: #2902 replace with protocol constant and consider good value. *) +let timeout_period_in_blocks = 500 let timeout_level ctxt = let level = Raw_context.current_level ctxt in - Raw_level_repr.add level.level timeout_period + Raw_level_repr.add level.level timeout_period_in_blocks let get_or_init_game ctxt rollup ~refuter ~defender = let open Lwt_tzresult_syntax in @@ -825,6 +834,13 @@ let get_or_init_game ctxt rollup ~refuter ~defender = match game with | Some g -> return (g, ctxt) | None -> + let* (ctxt, opp_1) = Store.Opponent.find (ctxt, rollup) refuter in + let* (ctxt, opp_2) = Store.Opponent.find (ctxt, rollup) defender in + let* _ = + match (opp_1, opp_2) with + | (None, None) -> return () + | _ -> fail Sc_rollup_staker_in_game + in let* ((_, child), ctxt) = get_conflict_point ctxt rollup refuter defender in @@ -840,20 +856,21 @@ let get_or_init_game ctxt rollup ~refuter ~defender = let* (ctxt, _) = Store.Game_timeout.init (ctxt, rollup) stakers (timeout_level ctxt) in + let* (ctxt, _) = Store.Opponent.init (ctxt, rollup) refuter defender in + let* (ctxt, _) = Store.Opponent.init (ctxt, rollup) defender refuter in return (game, ctxt) -let update_game ctxt rollup ~refuter ~defender refutation = +(* TODO: #2926 this requires carbonation *) +let update_game ctxt rollup ~player ~opponent refutation = let open Lwt_tzresult_syntax in - let (alice, bob) = Sc_rollup_game_repr.Index.normalize (refuter, defender) in - let* (game, ctxt) = get_or_init_game ctxt rollup ~refuter ~defender in + let (alice, bob) = Sc_rollup_game_repr.Index.normalize (player, opponent) in + let* (game, ctxt) = + get_or_init_game ctxt rollup ~refuter:player ~defender:opponent + in let* _ = - match game.turn with - | Alice -> - if Sc_rollup_repr.Staker.equal alice refuter then return () - else fail Sc_rollup_wrong_turn - | Bob -> - if Sc_rollup_repr.Staker.equal bob refuter then return () - else fail Sc_rollup_wrong_turn + let turn = match game.turn with Alice -> alice | Bob -> bob in + if Sc_rollup_repr.Staker.equal turn player then return () + else fail Sc_rollup_wrong_turn in match Sc_rollup_game_repr.play game refutation with | Either.Left outcome -> return (Some outcome, ctxt) @@ -867,15 +884,7 @@ let update_game ctxt rollup ~refuter ~defender refutation = in return (None, ctxt) -let apply_outcome ctxt rollup stakers (outcome : Sc_rollup_game_repr.outcome) = - let open Lwt_tzresult_syntax in - let (alice, bob) = Sc_rollup_game_repr.Index.normalize stakers in - let losing_staker = Sc_rollup_game_repr.Index.staker stakers outcome.loser in - let* ctxt = remove_staker ctxt rollup losing_staker in - let* (ctxt, _, _) = Store.Game.remove (ctxt, rollup) (alice, bob) in - let* (ctxt, _, _) = Store.Game_timeout.remove (ctxt, rollup) (alice, bob) in - return (Sc_rollup_game_repr.Ended (outcome.reason, losing_staker), ctxt) - +(* TODO: #2926 this requires carbonation *) let timeout ctxt rollup stakers = let open Lwt_tzresult_syntax in let level = (Raw_context.current_level ctxt).level in @@ -890,3 +899,15 @@ let timeout ctxt rollup stakers = if Raw_level_repr.(level > timeout_level) then return (Sc_rollup_game_repr.{loser = game.turn; reason = Timeout}, ctxt) else fail Sc_rollup_timeout_level_not_reached + +(* TODO: #2926 this requires carbonation *) +let apply_outcome ctxt rollup stakers (outcome : Sc_rollup_game_repr.outcome) = + let open Lwt_tzresult_syntax in + let (alice, bob) = Sc_rollup_game_repr.Index.normalize stakers in + let losing_staker = Sc_rollup_game_repr.Index.staker stakers outcome.loser in + let* ctxt = remove_staker ctxt rollup losing_staker in + let* (ctxt, _, _) = Store.Game.remove (ctxt, rollup) (alice, bob) in + let* (ctxt, _, _) = Store.Game_timeout.remove (ctxt, rollup) (alice, bob) in + let* (ctxt, _, _) = Store.Opponent.remove (ctxt, rollup) alice in + let* (ctxt, _, _) = Store.Opponent.remove (ctxt, rollup) bob in + return (Sc_rollup_game_repr.Ended (outcome.reason, losing_staker), ctxt) diff --git a/src/proto_alpha/lib_protocol/sc_rollup_storage.mli b/src/proto_alpha/lib_protocol/sc_rollup_storage.mli index c2034064f6fc..cb7f1cf9ad37 100644 --- a/src/proto_alpha/lib_protocol/sc_rollup_storage.mli +++ b/src/proto_alpha/lib_protocol/sc_rollup_storage.mli @@ -138,6 +138,8 @@ type error += Sc_rollup_not_staked | (* `Temporary *) Sc_rollup_remove_lcc + | (* `Temporary *) + Sc_rollup_staker_in_game | (* `Temporary *) Sc_rollup_bad_inbox_level @@ -434,16 +436,28 @@ val last_cemented_commitment_hash_with_level : (Sc_rollup_repr.Commitment_hash.t * Raw_level_repr.t * Raw_context.t) tzresult Lwt.t -(** [get_or_init_game ctxt rollup refuter defender] will simply return - the current game between the two stakers [refuter] and [defender] if - it exists. +(** [get_or_init_game ctxt rollup refuter defender] returns the current + game between the two stakers [refuter] and [defender] if it exists. - If it does not already exist, it will be created with [refuter] as the + If it does not already exist, it creates one with [refuter] as the first player to move. The initial state of the game will be obtained from the commitment pair belonging to [defender] at the conflict point. See [Sc_rollup_game_repr.initial] for documentation on how a pair of commitments is turned into an initial game state. + This also deals with the other bits of data in the storage around + the game. It checks neither staker is already in a game (and also + marks them as in a game once the new game is created). The reason we + only allow a staker to play one game at a time is to keep the + end-of-game logic simple---this way, a game can't end suddenly in + the middle because one player lost their stake in another game, it + can only end due to it's own moves or timeouts. + + It also initialises the timeout level to the current level plus + [timeout_period_in_blocks] (which will become a protocol constant + soon) to mark the block level at which it becomes possible for + anyone to end the game by timeout. + May fail with: {ul {li [Sc_rollup_does_not_exist] if [rollup] does not exist} @@ -451,6 +465,8 @@ val last_cemented_commitment_hash_with_level : the commitment staked on by [defender], or vice versa} {li [Sc_rollup_not_staked] if one of the [refuter] or [defender] is not actually staked} + {li [Sc_rollup_staker_in_game] if one of the [refuter] or [defender] + is already playing a game} } *) val get_or_init_game : Raw_context.t -> @@ -459,11 +475,11 @@ val get_or_init_game : defender:Sc_rollup_repr.Staker.t -> (Sc_rollup_game_repr.t * Raw_context.t) tzresult Lwt.t -(** [update_game ctxt rollup refuter defender refutation] handles the +(** [update_game ctxt rollup player opponent refutation] handles the storage-side logic for when one of the players makes a move in the game. It initializes the game if necessary (the first move looks - much like any other). It checks that [refuter] is the player whose - turn it is; if so, it applies [refutation] using the [play]. + much like any other). It checks that [player] is the player whose + turn it is; if so, it applies [refutation] using the [play] function. If the result is a new game, this is stored and the timeout level is updated. @@ -473,23 +489,53 @@ val get_or_init_game : May fail with: {ul {li [Sc_rollup_does_not_exist] if [rollup] does not exist} - {li [Sc_rollup_no_conflict] if [refuter] is staked on an ancestor of - the commitment staked on by [defender], or vice versa} - {li [Sc_rollup_not_staked] if one of the [refuter] or [defender] is + {li [Sc_rollup_no_conflict] if [player] is staked on an ancestor of + the commitment staked on by [opponent], or vice versa} + {li [Sc_rollup_not_staked] if one of the [player] or [opponent] is not actually staked} + {li [Sc_rollup_staker_in_game] if one of the [player] or [opponent] + is already playing a game} {li [Sc_rollup_wrong_turn] if a player is trying to move out of turn} } *) val update_game : Raw_context.t -> Sc_rollup_repr.t -> - refuter:Sc_rollup_repr.Staker.t -> - defender:Sc_rollup_repr.Staker.t -> + player:Sc_rollup_repr.Staker.t -> + opponent:Sc_rollup_repr.Staker.t -> Sc_rollup_game_repr.refutation -> (Sc_rollup_game_repr.outcome option * Raw_context.t) tzresult Lwt.t -(** [apply_outcome ctxt rollup outcome] will take an [outcome] produced - by [timeout] or [update_game] and perform the necessary end-of-game +(* TODO: #2902 update reference to timeout period in doc-string *) + +(** [timeout ctxt rollup stakers] checks that the timeout has + elapsed and if this function returns a game outcome that punishes whichever + of [stakers] is supposed to have played a move. + + The timeout period is currently defined in + [timeout_period_in_blocks]. This should become a protocol constant + soon. + + May fail with: + {ul + {li [Sc_rollup_no_game] if the game does not in fact exist} + {li [Sc_rollup_timeout_level_not_reached] if the player still has + time in which to play} + } + + Note: this function takes the two stakers as a pair rather than + separate arguments. This reflects the fact that for this function + the two players are symmetric. This function will normalize the + order of the players if necessary to get a valid game index, so the + argument [stakers] doesn't have to be in normal form. *) +val timeout : + Raw_context.t -> + Sc_rollup_repr.t -> + Sc_rollup_repr.Staker.t * Sc_rollup_repr.Staker.t -> + (Sc_rollup_game_repr.outcome * Raw_context.t) tzresult Lwt.t + +(** [apply_outcome ctxt rollup outcome] takes an [outcome] produced + by [timeout] or [update_game] and performs the necessary end-of-game cleanup: remove the game itself from the store and punish the losing player by removing their stake. @@ -499,37 +545,16 @@ val update_game : This is mostly just calling [remove_staker], so it can fail with the same errors as that. However, if it is called on an [outcome] - generated by [update_game] or [timeout] it should not fail unless - something has gone wrong. + generated by [update_game] or [timeout] it should not fail. Note: this function takes the two stakers as a pair rather than separate arguments. This reflects the fact that for this function the two players are symmetric. This function will normalize the - order of the players if necessary to create a valid game index. *) + order of the players if necessary to get a valid game index, so the + argument [stakers] doesn't have to be in normal form. *) val apply_outcome : Raw_context.t -> Sc_rollup_repr.t -> Sc_rollup_repr.Staker.t * Sc_rollup_repr.Staker.t -> Sc_rollup_game_repr.outcome -> (Sc_rollup_game_repr.status * Raw_context.t) tzresult Lwt.t - -(** [timeout ctxt rollup stakers] will check that the timeout has - elapsed and if so return a game outcome that punishes whichever - of [stakers] is supposed to have played a move. - - May fail with: - {ul - {li [Sc_rollup_no_game] if the game does not in fact exist} - {li [Sc_rollup_timeout_level_not_reached] if the player still has - time in which to play} - } - - Note: this function takes the two stakers as a pair rather than - separate arguments. This reflects the fact that for this function - the two players are symmetric. This function will normalize the - order of the players if necessary to create a valid game index. *) -val timeout : - Raw_context.t -> - Sc_rollup_repr.t -> - Sc_rollup_repr.Staker.t * Sc_rollup_repr.Staker.t -> - (Sc_rollup_game_repr.outcome * Raw_context.t) tzresult Lwt.t diff --git a/src/proto_alpha/lib_protocol/storage.ml b/src/proto_alpha/lib_protocol/storage.ml index cc3dd73537b4..2c77354f98fb 100644 --- a/src/proto_alpha/lib_protocol/storage.ml +++ b/src/proto_alpha/lib_protocol/storage.ml @@ -1628,4 +1628,17 @@ module Sc_rollup = struct let encoding = Raw_level_repr.encoding end) + + module Opponent = + Make_indexed_carbonated_data_storage + (Make_subcontext (Registered) (Indexed_context.Raw_context) + (struct + let name = ["opponent"] + end)) + (Public_key_hash_index) + (struct + type t = Sc_rollup_repr.Staker.t + + let encoding = Sc_rollup_repr.Staker.encoding + end) end diff --git a/src/proto_alpha/lib_protocol/storage.mli b/src/proto_alpha/lib_protocol/storage.mli index d853151ecae1..19d08a472ba7 100644 --- a/src/proto_alpha/lib_protocol/storage.mli +++ b/src/proto_alpha/lib_protocol/storage.mli @@ -766,8 +766,8 @@ module Sc_rollup : sig and type t = Raw_context.t * Sc_rollup_repr.t (** Refutation games are indexed by the rollup and the pair of - competing stakers. The staker pair should always be ordered to - ensure that games are not duplicated. + competing stakers. The staker pair should always be in lexical + order to ensure that games are not duplicated. *) module Game : Non_iterable_indexed_carbonated_data_storage @@ -777,12 +777,23 @@ module Sc_rollup : sig (** [Game_timeout] stores the block level at which the staker whose turn it is to move will (become vulnerable to) timeout. The staker - pair should always be ordered to ensure that this value is not - duplicated. + pair should always be in lexical order to ensure that this value is + not duplicated. *) module Game_timeout : Non_iterable_indexed_carbonated_data_storage with type key = Sc_rollup_game_repr.Index.t and type value = Raw_level_repr.t and type t = Raw_context.t * Sc_rollup_repr.t + + (** [Opponent] stores the current opponent of the staker. This is + mainly used to enforce the requirement that each staker should + only play one refutation game at a time. It will also be useful + for searching for current game by staker. + *) + module Opponent : + Non_iterable_indexed_carbonated_data_storage + with type key = Signature.Public_key_hash.t + and type value = Sc_rollup_repr.Staker.t + and type t = Raw_context.t * Sc_rollup_repr.t end diff --git a/src/proto_alpha/lib_protocol/test/pbt/dune b/src/proto_alpha/lib_protocol/test/pbt/dune index 7a94c6ae263a..f167f24942a9 100644 --- a/src/proto_alpha/lib_protocol/test/pbt/dune +++ b/src/proto_alpha/lib_protocol/test/pbt/dune @@ -10,7 +10,6 @@ test_tx_rollup_l2_encoding test_bitset test_sc_rollup_tick_repr - refutation_game_pbt test_carbonated_map) (libraries tezos-base tezos-micheline diff --git a/src/proto_alpha/lib_protocol/test/pbt/refutation_game_pbt.ml b/src/proto_alpha/lib_protocol/test/pbt/refutation_game_pbt.ml deleted file mode 100644 index f548b4824d99..000000000000 --- a/src/proto_alpha/lib_protocol/test/pbt/refutation_game_pbt.ml +++ /dev/null @@ -1,876 +0,0 @@ -(*****************************************************************************) -(* *) -(* Open Source License *) -(* Copyright (c) 2022 Nomadic Labs *) -(* Copyright (c) 2022 Trili Tech, *) -(* *) -(* 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. *) -(* *) -(*****************************************************************************) - -(** Testing - ------- - Component: PBT for the SCORU refutation game - Invocation: dune exec \ - src/proto_alpha/lib_protocol/test/pbt/refutation_game_pbt.exe - Subject: SCORU refutation game -*) -open Protocol - -open Alpha_context -open Sc_rollup -open Lwt_syntax - -exception TickNotFound of Sc_rollup_tick_repr.t - -open Lib_test.Qcheck_helpers - -(** - - Helpers - -*) -let assume_some opt f = match opt with Some x -> f x | None -> assert false - -let hash_state state number = - Digest.bytes @@ Bytes.of_string @@ state ^ string_of_int number - -module type TestPVM = sig - include Sc_rollup_PVM_sem.S - - module Internal_for_tests : sig - val initial_state : state - - val random_state : int -> state -> state - - val make_proof : state -> state -> proof - end -end - -(** - - [MakeCountingPVM (P)] is a PVM whose state is an integer and that - can count up to a certain [P.target]. - - This PVM has no input states. - -*) -module MakeCountingPVM (P : sig - val target : int -end) : TestPVM with type state = int = struct - type state = int - - type hash = State_hash.t - - type context = unit - - type proof = state * state - - let proof_start_state ((a, _) : proof) = - State_hash.hash_string [Int.to_string a] - - let proof_stop_state (_, a) = State_hash.hash_string [Int.to_string a] - - let state_hash (x : state) = - Lwt.return (State_hash.hash_string [Int.to_string x]) - - let is_input_state _ = Lwt.return None - - let initial_state _ _ = Lwt.return P.target - - let set_input _ s = Lwt.return s - - module Internal_for_tests = struct - let initial_state = P.target - - let random_state _ _ = Random.bits () - - let make_proof s1 s2 = (s1, s2) - end - - let equal_states = ( = ) - - let encoding = Data_encoding.int16 - - let proof_encoding = Data_encoding.tup2 encoding encoding - - let eval state = - if state >= P.target then Lwt.return state else Lwt.return (state + 1) - - let verify_proof ~input:_ ((a, b) : proof) = - eval a >>= fun x -> Lwt.return (equal_states x b) -end - -(** This is a random PVM. Its state is a pair of a string and a - list of integers. An evaluation step consumes the next integer - of the list and concatenates its representation to the string. *) -module MakeRandomPVM (P : sig - val initial_prog : int list -end) : TestPVM with type state = string * int list = struct - type state = string * int list - - type context = unit - - type proof = state * state - - type hash = State_hash.t - - let to_string (a, b) = - Format.sprintf "(%s, [%s])" a (String.concat ";" @@ List.map Int.to_string b) - - let proof_start_state (a, _) = State_hash.hash_string [to_string a] - - let proof_stop_state (_, a) = State_hash.hash_string [to_string a] - - let state_hash (x : state) = - Lwt.return @@ State_hash.hash_string [to_string x] - - let initial_state _ _ = Lwt.return ("hello", P.initial_prog) - - let is_input_state _ = Lwt.return None - - let set_input _ state = Lwt.return state - - module Internal_for_tests = struct - let initial_state = ("hello", P.initial_prog) - - let random_state length ((_, program) : state) = - let remaining_program = TzList.drop_n length program in - let (stop_state : state) = - (hash_state "" (Random.bits ()), remaining_program) - in - stop_state - - let make_proof s1 s2 = (s1, s2) - end - - let equal_states = ( = ) - - let encoding = - let open Data_encoding in - conv - (fun (value, list) -> (value, list)) - (fun (value, list) -> (value, list)) - (tup2 string (list int16)) - - let proof_encoding = Data_encoding.tup2 encoding encoding - - let eval (hash, continuation) = - match continuation with - | [] -> Lwt.return (hash, continuation) - | h :: tl -> Lwt.return (hash_state hash h, tl) - - let verify_proof ~input:_ ((a, b) : proof) = - eval a >>= fun x -> Lwt.return (equal_states x b) -end - -(** This module introduces some testing strategies for a game created from a PVM -*) -module Strategies (P : TestPVM) = struct - module Game = Sc_rollup_game.Make (P) - open Game - - (** [execute_until tick stat prop] runs eval until the tick satisfies pred. - It returns the new tick and the modified state - *) - let execute_until tick state pred = - let rec loop state tick = - if pred tick state then Lwt.return (tick, state) - else - PVM.eval state >>= fun s -> - if s = state then Lwt.return (tick, state) - else loop s (Sc_rollup_tick_repr.next tick) - in - loop state tick - - let remember history t s = Sc_rollup_tick_repr.Map.add t s history - - (** [state_at history tick initial_state] is a lookup in the history. - If no value at tick exists then it runs eval from the tick t0 to t starting - from initial_state. Here t0 is the last known tick smaller that t - (or the intial tick if no such exits) *) - let state_at history tick initial_state = - let (lower, ostate, _) = Sc_rollup_tick_repr.Map.split tick history in - match ostate with - | Some state -> Lwt.return (state, history) - | None -> - let (tick0, state0) = - match Sc_rollup_tick_repr.Map.max_binding lower with - | Some (t, s) -> (t, s) - | None -> (Sc_rollup_tick_repr.initial, initial_state) - in - - execute_until tick0 state0 (fun tick' _ -> - Sc_rollup_tick_repr.(tick' = tick)) - >>= fun (t, s) -> Lwt.return (s, remember history t s) - - (** This function takes a section and an integer branching and creates a - dissection with branching number of pieces that are (roughly) equal - and whose states come from the history. - Assume that length of the initial section is len - and len mod branching = r. - We have the following invariants: - - if branching >len then we make branching = len - (split the section into one tick sections) - - valid_disection section - (dissection_of_section history branching section)=true - - The first r pieces are one tick longer than the rest - (the alternative would have been for the last piece to be a lot longer) - *) - let dissection_of_section history (branching : int) - (section : Section.section) = - let open Section in - if - Sc_rollup_tick_repr.( - next section.section_start_at >= section.section_stop_at) - then Lwt.return (None, history) - else - assume_some (Sc_rollup_tick_repr.to_int section.section_start_at) - @@ fun start -> - assume_some (Sc_rollup_tick_repr.to_int section.section_stop_at) - @@ fun stop -> - let len = stop - start in - let branching = min len branching in - let bucket = len / branching in - let reminder = len mod branching in - let rec aux history index branch rem (map : dissection) = - if index = branch then Lwt.return map - else - let start_at = start + (bucket * index) + min rem index in - let stop_at = start + (bucket * (index + 1)) + min rem (index + 1) in - let section_start_at = - Option.value - ~default:Sc_rollup_tick_repr.initial - (Sc_rollup_tick_repr.of_int start_at) - and section_stop_at = - Option.value - ~default:Sc_rollup_tick_repr.initial - (Sc_rollup_tick_repr.of_int stop_at) - in - let* (starting_state, history) = - state_at history section_start_at P.Internal_for_tests.initial_state - in - let* section_start_state = P.state_hash starting_state in - let* (stoping_state, history) = - state_at history section_stop_at P.Internal_for_tests.initial_state - in - let* section_stop_state = P.state_hash stoping_state in - - let section = - { - section_start_at; - section_start_state; - section_stop_at; - section_stop_state; - } - in - let new_map = add_section section map in - aux history (index + 1) branching rem new_map - in - - let* (dissection : dissection) = - aux history 0 branching reminder empty_dissection - in - Lwt.return @@ (Some dissection, history) - - type ('from, 'initial) client = { - initial : 'from -> 'initial Lwt.t; - next_move : Section.dissection -> move Lwt.t; - } - - let run ~start_at ~(start_state : PVM.state) ~committer ~refuter = - let* (Commit commit) = committer.initial (start_at, start_state) in - let* (RefuteByConflict refutation) = - refuter.initial (start_state, Commit commit) - in - let outcome = - let rec loop game move = - play game move >>= function - | Over outcome -> Lwt.return outcome - | Ongoing game -> - let game = {game with turn = opponent game.turn} in - let* move = - match game.turn with - | Committer -> - committer.next_move - (Option.value - ~default:Game.Section.empty_dissection - game.current_dissection) - | Refuter -> - refuter.next_move - (Option.value - ~default:Game.Section.empty_dissection - game.current_dissection) - in - loop game move - in - - let (game, move) = initial (Commit commit) refutation in - loop game move - in - outcome - - (** this exception is needed for a small that alows a "fold-with break" -*) - exception Section of Game.Section.section - - let random_tick ?(from = 0) () = - Option.value - ~default:Sc_rollup_tick_repr.initial - (Sc_rollup_tick_repr.of_int (from + Random.int 31)) - - (** this picks a random section between start_at and stop_at. The states - are determined by the random_state function.*) - let random_section (start_at : Sc_rollup_tick_repr.t) start_state - (stop_at : Sc_rollup_tick_repr.t) = - let x = - min 10000 @@ Z.to_int (Sc_rollup_tick_repr.distance start_at stop_at) - in - let length = 1 + try Random.int x with _ -> 0 in - let stop_at = - assume_some (Sc_rollup_tick_repr.to_int start_at) @@ fun start_at -> - Sc_rollup_tick_repr.(of_int (start_at + length)) - in - let section_stop_at = Option.value ~default:start_at stop_at in - - let random_stop_state = - P.Internal_for_tests.(random_state length initial_state) - in - let* section_stop_state = P.state_hash random_stop_state in - Lwt.return - Section. - { - section_start_at = start_at; - section_start_state = start_state; - section_stop_at; - section_stop_state; - } - - (** this picks a random dissection of a given section. - The sections involved are random and their states have no connection - with the initial section.*) - let random_dissection (gsection : Section.section) = - let open Sc_rollup_tick_repr in - let rec aux dissection start_at start_state = - if start_at = gsection.section_stop_at then Lwt.return @@ Some dissection - else - let* section = - random_section start_at start_state gsection.section_stop_at - in - if - section.section_start_at = gsection.section_start_at - && section.section_stop_at = gsection.section_stop_at - then aux dissection start_at start_state - else - aux - (Section.add_section section dissection) - section.section_stop_at - section.section_stop_state - in - if - Compare.Int.( - Z.to_int (distance gsection.section_stop_at gsection.section_start_at) - > 1) - then - aux - Section.empty_dissection - gsection.section_start_at - gsection.section_start_state - else Lwt.return None - - (** this function assmbles a random decision from a given dissection. - It first picks a random section from the dissection and modifies randomly - its states. - If the length of this section is one tick the returns a conclusion with - the given modified states. - If the length is longer it creates a random decision and outputs a Refine - decisoon with this dissection.*) - let random_decision d = - let open Section in - let cardinal = dissection_cardinal d in - let x = Random.int cardinal in - let (_, section) = - try - fold_over_dissection - (fun _ s (n, _) -> if n = x then raise (Section s) else (n + 1, None)) - d - (0, None) - with Section sec -> (0, Some sec) - in - let section = - match section with None -> raise Not_found | Some section -> section - in - let section_start_at = section.section_start_at in - let section_stop_at = section.section_stop_at in - let start_state = P.Internal_for_tests.(random_state 0 initial_state) in - let* section_start_state = P.state_hash start_state in - let stop_state = - assume_some (Sc_rollup_tick_repr.to_int section_stop_at) - @@ fun section_stop_at -> - assume_some (Sc_rollup_tick_repr.to_int section_start_at) - @@ fun section_start_at -> - P.Internal_for_tests.( - random_state (section_stop_at - section_start_at) initial_state) - in - let* section_stop_state = P.state_hash stop_state in - - let* next_dissection = random_dissection section in - let section = - { - section_start_state; - section_start_at; - section_stop_state; - section_stop_at; - } - in - let conflict_resolution_step = - match next_dissection with - | None -> - Conclude (None, P.Internal_for_tests.make_proof start_state stop_state) - | Some next_dissection -> - Refine {stop_state = section.section_stop_state; next_dissection} - in - Lwt.return @@ ConflictInside {choice = section; conflict_resolution_step} - - (** technical params for machine directed strategies, branching is the number - of pieces for a dissection failing level*) - type parameters = { - branching : int; - failing_level : int; - max_failure : int option; - } - - type checkpoint = Sc_rollup_tick_repr.t -> bool - - (** there are two kinds of strategies, random and machine dirrected by a - params and a checkpoint*) - type strategy = Random | MachineDirected of parameters * checkpoint - - (** - checks that the stop state of a section conflicts with the one in the history. - *) - let conflicting_section history (section : Section.section) = - let* (new_state, _) = - state_at - history - section.section_stop_at - P.Internal_for_tests.initial_state - in - let* new_hash = P.state_hash new_state in - Lwt.return @@ not (section.section_stop_state = new_hash) - - (** - Finds the section (if it exists) is a dissection that conflicts - with the history. This is where the trick with the exception - appears*) - let find_conflict history dissection = - try - Section.fold_over_dissection - (fun _ v _ -> - let* conflict = conflicting_section history v in - if conflict then raise (Section v) else Lwt.return None) - dissection - (Lwt.return None) - with Section section -> Lwt.return @@ Some section - - (** [next_move history branching dissection] - If finds the next move based on a dissection and history. - It finds the first section of dissection that conflicts with the history. - If the section has length one tick it returns a move with a Conclude - conflict_resolution_step. - If the section is longer it creates a new dissection with branching - many pieces, updates the history based on this dissection and returns - a move with a Refine type conflict_resolution_step. - *) - let next_move history branching dissection = - let* section = - find_conflict history dissection >>= function - | None -> raise (TickNotFound Sc_rollup_tick_repr.initial) - | Some s -> Lwt.return s - in - Game.Section.pp_section Format.std_formatter section ; - let* (next_dissection, history) = - dissection_of_section history branching section - in - let empty_history = Sc_rollup_tick_repr.Map.empty in - let* (conflict_resolution_step, history) = - match next_dissection with - | None -> - let* (stop_state, history) = - state_at - history - (Sc_rollup_tick_repr.next section.section_start_at) - P.Internal_for_tests.initial_state - in - let* (start_state, _) = - state_at - history - section.section_start_at - P.Internal_for_tests.initial_state - in - Lwt.return - ( Conclude - (None, P.Internal_for_tests.make_proof start_state stop_state), - empty_history ) - | Some next_dissection -> - let* (state, history) = - state_at - history - section.section_stop_at - P.Internal_for_tests.initial_state - in - let* stop_state = P.state_hash state in - Lwt.return (Refine {stop_state; next_dissection}, history) - in - Lwt.return - (ConflictInside {choice = section; conflict_resolution_step}, history) - - (** this is an outomatic commuter client. It generates a "perfect" client - for the committer.*) - let machine_directed_committer {branching; _} pred = - let history = ref Sc_rollup_tick_repr.Map.empty in - let initial ((section_start_at : Sc_rollup_tick_repr.t), start_state) = - let* (section_stop_at, stop_state) = - execute_until section_start_at start_state @@ fun tick _ -> pred tick - in - let* section_start_state = P.state_hash start_state in - let* section_stop_state = P.state_hash stop_state in - history := remember !history section_start_at start_state ; - history := remember !history section_stop_at stop_state ; - Lwt.return - @@ Commit - { - section_start_state; - section_start_at; - section_stop_state; - section_stop_at; - } - in - let next_move dissection = - let* (move, history') = next_move !history branching dissection in - history := history' ; - Lwt.return move - in - ({initial; next_move} : _ client) - - (** this is an outomatic refuter client. It generates a "perfect" client - for the refuter.*) - let machine_directed_refuter {branching; _} = - let history = Sc_rollup_tick_repr.Map.empty in - let initial (section_start_state, Commit section) = - let ({section_start_at; section_stop_at; _} : Section.section) = - section - in - let* (_stop_at, stop_state) = - execute_until section_start_at section_start_state @@ fun tick _ -> - tick >= section_stop_at - in - - let history = remember history section_start_at section_start_state in - let history = remember history section_stop_at stop_state in - let* section_stop_state = P.state_hash stop_state in - let* (next_dissection, history) = - dissection_of_section - history - branching - {section with section_stop_state} - in - let* conflict_resolution_step = - match next_dissection with - | None -> - let* (state, _) = - state_at - history - section_start_at - P.Internal_for_tests.initial_state - in - Lwt.return - @@ Conclude (None, P.Internal_for_tests.make_proof state stop_state) - | Some next_dissection -> - Lwt.return - @@ Refine {stop_state = section_stop_state; next_dissection} - in - Lwt.return @@ RefuteByConflict conflict_resolution_step - in - let next_move dissection = - let* (move, _) = next_move history branching dissection in - Lwt.return move - in - ({initial; next_move} : _ client) - - (** This builds a commiter client from a strategy. - If the strategy is MachineDirected it uses the above constructions. - If the strategy is random then it usesa random section for the initial - commitments and the random decision for the next move.*) - let committer_from_strategy : strategy -> _ client = function - | Random -> - { - initial = - (fun ((section_start_at : Sc_rollup_tick_repr.t), start_state) -> - let section_stop_at = - assume_some (Sc_rollup_tick_repr.to_int section_start_at) - @@ fun start_at -> random_tick ~from:start_at () - in - let* start_state = P.state_hash start_state in - let* section = - random_section section_start_at start_state section_stop_at - in - Lwt.return @@ Commit section); - next_move = random_decision; - } - | MachineDirected (parameters, checkpoint) -> - machine_directed_committer parameters checkpoint - - (** This builds a refuter client from a strategy. - If the strategy is MachineDirected it uses the above constructions. - If the strategy is random then it uses a randomdissection - of the commited section for the initial refutation - and the random decision for the next move.*) - let refuter_from_strategy : strategy -> _ client = function - | Random -> - { - initial = - (fun (_, Commit section) -> - let* conflict_resolution_step = - let* next_dissection = - random_dissection section >>= function - | None -> - Lwt.return - @@ Section.(add_section section empty_dissection) - | Some dissection -> Lwt.return dissection - in - let (_, section) = - Option.value - ~default:(Sc_rollup_tick_repr.initial, section) - (Section.last_section next_dissection) - in - Lwt.return - @@ Refine - {stop_state = section.section_stop_state; next_dissection} - in - Lwt.return @@ RefuteByConflict conflict_resolution_step); - next_move = random_decision; - } - | MachineDirected (parameters, _) -> machine_directed_refuter parameters - - (** [test_strategies committer_strategy refuter_strategy expectation] - runs a game based oin the two given strategies and checks that the resulting - outcome fits the expectations. *) - let test_strategies committer_strategy refuter_strategy expectation = - let start_state = P.Internal_for_tests.initial_state in - let committer = committer_from_strategy committer_strategy in - let refuter = refuter_from_strategy refuter_strategy in - let outcome = - run ~start_at:Sc_rollup_tick_repr.initial ~start_state ~committer ~refuter - in - expectation outcome - - (** This is a commuter client having a perfect strategy*) - let perfect_committer = - MachineDirected - ( {failing_level = 0; branching = 2; max_failure = None}, - fun tick -> - assume_some (Sc_rollup_tick_repr.to_int tick) @@ fun tick -> - tick >= 20 + Random.int 100 ) - (** This is a refuter client having a perfect strategy*) - - let perfect_refuter = - MachineDirected - ( {failing_level = 0; branching = 2; max_failure = None}, - fun _ -> assert false ) - - (** This is a commuter client having a strategy that forgets a tick*) - let failing_committer max_failure = - MachineDirected - ( {failing_level = 1; branching = 2; max_failure}, - fun tick -> - let s = match max_failure with None -> 20 | Some x -> x in - assume_some (Sc_rollup_tick_repr.to_int tick) @@ fun tick -> tick >= s - ) - - (** This is a commuter client having a strategy that forgets a tick*) - let failing_refuter max_failure = - MachineDirected - ({failing_level = 1; branching = 2; max_failure}, fun _ -> assert false) - - (** the possible expectation functions *) - let commiter_wins x = - Lwt_main.run - (x >>= function - | {winner = Some Committer; _} -> Lwt.return true - | _ -> Lwt.return false) - - let refuter_wins x = - Lwt_main.run - (x >>= function - | {winner = Some Refuter; _} -> Lwt.return true - | _ -> Lwt.return false) - - let all_win _ = true -end - -(** the following are the possible combinations of strategies*) -let perfect_perfect (module P : TestPVM) _max_failure = - let module R = Strategies (P) in - R.test_strategies R.perfect_committer R.perfect_refuter R.commiter_wins - -let random_random (module P : TestPVM) _max_failure = - let module S = Strategies (P) in - S.test_strategies Random Random S.all_win - -let random_perfect (module P : TestPVM) _max_failure = - let module S = Strategies (P) in - S.test_strategies Random S.perfect_refuter S.refuter_wins - -let perfect_random (module P : TestPVM) _max_failure = - let module S = Strategies (P) in - S.test_strategies S.perfect_committer Random S.commiter_wins - -let failing_perfect (module P : TestPVM) max_failure = - let module S = Strategies (P) in - S.test_strategies - (S.failing_committer max_failure) - S.perfect_refuter - S.refuter_wins - -let perfect_failing (module P : TestPVM) max_failure = - let module S = Strategies (P) in - S.test_strategies - S.perfect_committer - (S.failing_refuter max_failure) - S.commiter_wins - -(** this assembles a test from a RandomPVM and a function that choses the -type of strategies *) -let testing_randomPVM (f : (module TestPVM) -> int option -> bool) name = - let open QCheck2 in - Test.make - ~name - (Gen.list_size Gen.small_int (Gen.int_range 0 100)) - (fun initial_prog -> - assume (initial_prog <> []) ; - f - (module MakeRandomPVM (struct - let initial_prog = initial_prog - end)) - (Some (List.length initial_prog))) - -(** this assembles a test from a CountingPVM and a function that choses -the type of strategies *) -let testing_countPVM (f : (module TestPVM) -> int option -> bool) name = - let open QCheck2 in - Test.make ~name Gen.small_int (fun target -> - assume (target > 0) ; - f - (module MakeCountingPVM (struct - let target = target - end)) - (Some target)) - -let test_random_dissection (module P : TestPVM) start_at length branching = - let open P in - let module S = Strategies (P) in - let* section_start_state = P.state_hash @@ Internal_for_tests.initial_state in - let section_stop_at = - match Sc_rollup_tick_repr.of_int (start_at + length) with - | None -> assert false - | Some x -> x - in - let section_start_at = - match Sc_rollup_tick_repr.of_int start_at with - | None -> assert false - | Some x -> x - in - let* section_stop_state = - P.state_hash @@ Internal_for_tests.(random_state length initial_state) - in - let section = - S.Game.Section. - { - section_start_at; - section_start_state; - section_stop_at; - section_stop_state; - } - in - let* (option_dissection, _) = - let empty_history = Sc_rollup_tick_repr.Map.empty in - S.dissection_of_section empty_history branching section - in - let dissection = - match option_dissection with - | None -> raise (Invalid_argument "no dissection") - | Some x -> x - in - Lwt.return @@ S.Game.Section.valid_dissection section dissection - -let testDissection = - let open QCheck2 in - [ - Test.make - ~name:"randomVPN" - (Gen.quad - (Gen.list_size Gen.small_int (Gen.int_range 0 100)) - Gen.small_int - Gen.small_int - Gen.small_int) - (fun (initial_prog, start_at, length, branching) -> - assume - (start_at >= 0 && length > 1 - && List.length initial_prog > start_at + length - && 1 < branching) ; - let module P = MakeRandomPVM (struct - let initial_prog = initial_prog - end) in - Lwt_main.run - @@ test_random_dissection (module P) start_at length branching); - Test.make - ~name:"count" - (Gen.quad Gen.small_int Gen.small_int Gen.small_int Gen.small_int) - (fun (target, start_at, length, branching) -> - assume (start_at >= 0 && length > 1 && 1 < branching) ; - let module P = MakeCountingPVM (struct - let target = target - end) in - Lwt_main.run - @@ test_random_dissection (module P) start_at length branching); - ] - -let () = - Alcotest.run - "Refutation Game" - [ - ("Dissection tests", qcheck_wrap testDissection); - ( "RandomPVM", - qcheck_wrap - [ - testing_randomPVM perfect_perfect "perfect-perfect"; - testing_randomPVM random_random "random-random"; - testing_randomPVM random_perfect "random-perfect"; - testing_randomPVM perfect_random "perfect-random"; - ] ); - ( "CountingPVM", - qcheck_wrap - [ - testing_countPVM perfect_perfect "perfect-perfect"; - testing_countPVM random_random "random-random"; - testing_countPVM random_perfect "random-perfect"; - testing_countPVM perfect_random "perfect-random"; - ] ); - ] diff --git a/src/proto_alpha/lib_tx_rollup/l1_operation.ml b/src/proto_alpha/lib_tx_rollup/l1_operation.ml index 178bafdd1c26..3663d5fa41c8 100644 --- a/src/proto_alpha/lib_tx_rollup/l1_operation.ml +++ b/src/proto_alpha/lib_tx_rollup/l1_operation.ml @@ -63,6 +63,8 @@ module Manager_operation = struct make sc_rollup_add_messages_case; make sc_rollup_cement_case; make sc_rollup_publish_case; + make sc_rollup_refute_case; + make sc_rollup_timeout_case; ] let get_case : @@ -90,6 +92,8 @@ module Manager_operation = struct | Sc_rollup_add_messages _ -> sc_rollup_add_messages_case | Sc_rollup_cement _ -> sc_rollup_cement_case | Sc_rollup_publish _ -> sc_rollup_publish_case + | Sc_rollup_refute _ -> sc_rollup_refute_case + | Sc_rollup_timeout _ -> sc_rollup_timeout_case let pp_kind ppf op = let open Operation.Encoding.Manager_operations in -- GitLab