From c784dcb31e1bc9cd1e1f4e02742a6fe6746b036f Mon Sep 17 00:00:00 2001 From: Sylvain Ribstein Date: Thu, 17 Aug 2023 15:25:53 +0200 Subject: [PATCH 1/5] soru/node: add recover operation to config --- src/lib_smart_rollup_node/configuration.ml | 23 ++++++++++++++++----- src/lib_smart_rollup_node/configuration.mli | 10 +++++++-- src/lib_smart_rollup_node/injector.ml | 1 + 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/lib_smart_rollup_node/configuration.ml b/src/lib_smart_rollup_node/configuration.ml index 761469244442..2e3f0bf41047 100644 --- a/src/lib_smart_rollup_node/configuration.ml +++ b/src/lib_smart_rollup_node/configuration.ml @@ -34,13 +34,19 @@ type mode = | Operator | Custom -type operation_kind = Publish | Add_messages | Cement | Timeout | Refute +type operation_kind = + | Publish + | Add_messages + | Cement + | Timeout + | Refute + | Recover -type purpose = Operating | Batching | Cementing +type purpose = Operating | Batching | Cementing | Recovering -let operation_kinds = [Publish; Add_messages; Cement; Timeout; Refute] +let operation_kinds = [Publish; Add_messages; Cement; Timeout; Refute; Recover] -let purposes = [Operating; Batching; Cementing] +let purposes = [Operating; Batching; Cementing; Recovering] module Operation_kind_map = Map.Make (struct type t = operation_kind @@ -176,6 +182,7 @@ let default_burn_cap = mutez 0L *) let default_fee = function | Cement -> tez 1 + | Recover -> tez 1 | Publish -> tez 2 | Add_messages -> (* We keep this limit even though it depends on the size of the message @@ -195,6 +202,7 @@ let default_burn = function tez 1 | Add_messages -> tez 0 | Cement -> tez 0 + | Recover -> tez 0 | Timeout -> tez 0 | Refute -> (* A refutation move can store data, e.g. opening a game. *) @@ -257,6 +265,7 @@ let operation_kinds_of_purpose = function | Batching -> [Add_messages] | Cementing -> [Cement] | Operating -> [Publish; Refute; Timeout] + | Recovering -> [Recover] let string_of_operation_kind = function | Publish -> "publish" @@ -264,6 +273,7 @@ let string_of_operation_kind = function | Cement -> "cement" | Timeout -> "timeout" | Refute -> "refute" + | Recover -> "recover" let operation_kind_of_string = function | "publish" -> Some Publish @@ -271,6 +281,7 @@ let operation_kind_of_string = function | "cement" -> Some Cement | "timeout" -> Some Timeout | "refute" -> Some Refute + | "recover" -> Some Recover | _ -> None let operation_kind_of_string_exn s = @@ -282,6 +293,7 @@ let string_of_purpose = function | Operating -> "operating" | Batching -> "batching" | Cementing -> "cementing" + | Recovering -> "recovering" let purpose_of_string = function (* For backward compability: @@ -292,6 +304,7 @@ let purpose_of_string = function | "operating" | "publish" | "refute" | "timeout" -> Some Operating | "batching" | "add_messages" -> Some Batching | "cementing" | "cement" -> Some Cementing + | "recovering" -> Some Recovering | _ -> None let purpose_of_string_exn s = @@ -759,7 +772,7 @@ let check_mode config = | Observer -> narrow_purposes [] | Batcher -> narrow_purposes [Batching] | Accuser -> narrow_purposes [Operating] - | Bailout -> narrow_purposes [Operating; Cementing] + | Bailout -> narrow_purposes [Operating; Cementing; Recovering] | Maintenance -> narrow_purposes [Operating; Cementing] | Operator -> narrow_purposes [Operating; Cementing; Batching] | Custom -> return config diff --git a/src/lib_smart_rollup_node/configuration.mli b/src/lib_smart_rollup_node/configuration.mli index b1d48adb751f..083c02cebfef 100644 --- a/src/lib_smart_rollup_node/configuration.mli +++ b/src/lib_smart_rollup_node/configuration.mli @@ -38,11 +38,17 @@ type mode = the signers *) (** The kind of operations that can be injected by the rollup node. *) -type operation_kind = Publish | Add_messages | Cement | Timeout | Refute +type operation_kind = + | Publish + | Add_messages + | Cement + | Timeout + | Refute + | Recover (** Purposes for operators, indicating their role and thus the kinds of operations that they sign. *) -type purpose = Operating | Batching | Cementing +type purpose = Operating | Batching | Cementing | Recovering module Operation_kind_map : Map.S with type key = operation_kind diff --git a/src/lib_smart_rollup_node/injector.ml b/src/lib_smart_rollup_node/injector.ml index 6e7c072f0bea..7a93dfa2d151 100644 --- a/src/lib_smart_rollup_node/injector.ml +++ b/src/lib_smart_rollup_node/injector.ml @@ -71,6 +71,7 @@ module Parameters : | Cement -> 1 | Timeout -> 1 | Refute -> 1 + | Recover -> 1 let operation_tag : Operation.t -> Tag.t = function | Add_messages _ -> Add_messages -- GitLab From 6125c2696097128fbf767acdcb02117198020a8f Mon Sep 17 00:00:00 2001 From: Sylvain Ribstein Date: Thu, 17 Aug 2023 15:46:34 +0200 Subject: [PATCH 2/5] injector/soru: add recover operation --- src/lib_smart_rollup/l1_operation.ml | 14 +++++++++++++- src/lib_smart_rollup/l1_operation.mli | 3 ++- src/lib_smart_rollup_node/injector.ml | 1 + .../lib_sc_rollup_node/sc_rollup_injector.ml | 3 +++ .../lib_sc_rollup_node/sc_rollup_injector.ml | 3 +++ .../lib_sc_rollup_node/sc_rollup_injector.ml | 3 +++ 6 files changed, 25 insertions(+), 2 deletions(-) diff --git a/src/lib_smart_rollup/l1_operation.ml b/src/lib_smart_rollup/l1_operation.ml index 74de8ffb2d7f..daf80dc28cf0 100644 --- a/src/lib_smart_rollup/l1_operation.ml +++ b/src/lib_smart_rollup/l1_operation.ml @@ -33,6 +33,7 @@ type t = refutation : Game.refutation; } | Timeout of {rollup : Address.t; stakers : Game.index} + | Recover_bond of {rollup : Address.t; staker : Signature.Public_key_hash.t} let encoding : t Data_encoding.t = let open Data_encoding in @@ -95,6 +96,16 @@ let encoding : t Data_encoding.t = (function | Timeout {rollup; stakers} -> Some (rollup, stakers) | _ -> None) (fun (rollup, stakers) -> Timeout {rollup; stakers}); + case + 5 + "recover" + (obj2 + (req "rollup" Address.encoding) + (req "staker" Signature.Public_key_hash.encoding)) + (function + | Recover_bond {rollup; staker} -> Some (rollup, staker) + | _ -> None) + (fun (rollup, staker) -> Recover_bond {rollup; staker}); ] let pp ppf = function @@ -149,7 +160,8 @@ let pp ppf = function Signature.Public_key_hash.pp opponent | Timeout {rollup = _; stakers = _} -> Format.fprintf ppf "timeout" + | Recover_bond {rollup = _; staker = _} -> Format.fprintf ppf "recover" let unique = function | Add_messages _ | Cement _ -> false - | Publish _ | Refute _ | Timeout _ -> true + | Publish _ | Refute _ | Timeout _ | Recover_bond _ -> true diff --git a/src/lib_smart_rollup/l1_operation.mli b/src/lib_smart_rollup/l1_operation.mli index 5e111d5d60b2..7ac0c415f5d6 100644 --- a/src/lib_smart_rollup/l1_operation.mli +++ b/src/lib_smart_rollup/l1_operation.mli @@ -34,8 +34,9 @@ type t = refutation : Game.refutation; } | Timeout of {rollup : Address.t; stakers : Game.index} + | Recover_bond of {rollup : Address.t; staker : Signature.Public_key_hash.t} + (** Encoding for L1 operations (used by injector for on-disk persistence). *) -(** Encoding for L1 operations (used by injector for on-disk persistence). *) val encoding : t Data_encoding.t (** Pretty printer (human readable) for L1 operations. *) diff --git a/src/lib_smart_rollup_node/injector.ml b/src/lib_smart_rollup_node/injector.ml index 7a93dfa2d151..4bd24ce2c60e 100644 --- a/src/lib_smart_rollup_node/injector.ml +++ b/src/lib_smart_rollup_node/injector.ml @@ -79,6 +79,7 @@ module Parameters : | Publish _ -> Publish | Timeout _ -> Timeout | Refute _ -> Refute + | Recover_bond _ -> Recover let fee_parameter {fee_parameters; _} operation = let operation_kind = operation_tag operation in diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/sc_rollup_injector.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/sc_rollup_injector.ml index 7d85baf92c37..d45ec0291c17 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/sc_rollup_injector.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/sc_rollup_injector.ml @@ -54,6 +54,9 @@ let injector_operation_to_manager : let rollup = Sc_rollup_proto_types.Address.of_octez rollup in let stakers = Sc_rollup_proto_types.Game.index_of_octez stakers in Manager (Sc_rollup_timeout {rollup; stakers}) + | Recover_bond {rollup; staker} -> + let rollup = Sc_rollup_proto_types.Address.of_octez rollup in + Manager (Sc_rollup_recover_bond {sc_rollup = rollup; staker}) let injector_operation_of_manager : type kind. diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/sc_rollup_injector.ml b/src/proto_018_Proxford/lib_sc_rollup_node/sc_rollup_injector.ml index bb28bab19547..72e4357d5d3a 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/sc_rollup_injector.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/sc_rollup_injector.ml @@ -51,6 +51,9 @@ let injector_operation_to_manager : let rollup = Sc_rollup_proto_types.Address.of_octez rollup in let stakers = Sc_rollup_proto_types.Game.index_of_octez stakers in Manager (Sc_rollup_timeout {rollup; stakers}) + | Recover_bond {rollup; staker} -> + let rollup = Sc_rollup_proto_types.Address.of_octez rollup in + Manager (Sc_rollup_recover_bond {sc_rollup = rollup; staker}) let injector_operation_of_manager : type kind. diff --git a/src/proto_alpha/lib_sc_rollup_node/sc_rollup_injector.ml b/src/proto_alpha/lib_sc_rollup_node/sc_rollup_injector.ml index 67aa70089c78..00883fc02d21 100644 --- a/src/proto_alpha/lib_sc_rollup_node/sc_rollup_injector.ml +++ b/src/proto_alpha/lib_sc_rollup_node/sc_rollup_injector.ml @@ -51,6 +51,9 @@ let injector_operation_to_manager : let rollup = Sc_rollup_proto_types.Address.of_octez rollup in let stakers = Sc_rollup_proto_types.Game.index_of_octez stakers in Manager (Sc_rollup_timeout {rollup; stakers}) + | Recover_bond {rollup; staker} -> + let rollup = Sc_rollup_proto_types.Address.of_octez rollup in + Manager (Sc_rollup_recover_bond {sc_rollup = rollup; staker}) let injector_operation_of_manager : type kind. -- GitLab From e71f3755edcb034e83c31693186ae610e90effb4 Mon Sep 17 00:00:00 2001 From: Sylvain Ribstein Date: Thu, 17 Aug 2023 17:28:05 +0200 Subject: [PATCH 3/5] rollup/node: recover bond when ope has no commitment anymore --- src/lib_smart_rollup_node/commitment_event.ml | 10 ++++++++ .../commitment_event.mli | 4 ++++ src/lib_smart_rollup_node/publisher.ml | 24 ++++++++++++++++++- src/lib_smart_rollup_node/publisher.mli | 6 +++++ .../lib_sc_rollup_node/daemon_helpers.ml | 22 +++++++++++++++++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/lib_smart_rollup_node/commitment_event.ml b/src/lib_smart_rollup_node/commitment_event.ml index 4f87f9042d5d..fb79a5208686 100644 --- a/src/lib_smart_rollup_node/commitment_event.ml +++ b/src/lib_smart_rollup_node/commitment_event.ml @@ -101,6 +101,14 @@ module Simple = struct ("hash", Commitment.Hash.encoding) ("level", Data_encoding.int32) + let recover_bond = + declare_1 + ~section + ~name:"sc_rollup_node_recover_bond" + ~msg:"Recover bond for {staker}." + ~level:Notice + ("staker", Signature.Public_key_hash.encoding) + let commitment_parent_is_not_lcc = declare_3 ~section @@ -197,6 +205,8 @@ let compute_commitment level = Simple.(emit compute_commitment level) let publish_commitment head level = Simple.(emit publish_commitment (head, level)) +let recover_bond staker = Simple.(emit recover_bond staker) + let commitment_parent_is_not_lcc level predecessor_hash lcc_hash = Simple.(emit commitment_parent_is_not_lcc (level, predecessor_hash, lcc_hash)) diff --git a/src/lib_smart_rollup_node/commitment_event.mli b/src/lib_smart_rollup_node/commitment_event.mli index 07a809a12d5b..64a37c7dc2a8 100644 --- a/src/lib_smart_rollup_node/commitment_event.mli +++ b/src/lib_smart_rollup_node/commitment_event.mli @@ -71,6 +71,10 @@ val compute_commitment : int32 -> unit Lwt.t being published. *) val publish_commitment : Commitment.Hash.t -> int32 -> unit Lwt.t +(** [recover_bond staker] emits the event that a recover bond + operation is being submitted. *) +val recover_bond : Signature.Public_key_hash.t -> unit Lwt.t + (** Events emmitted by the Publisher worker *) module Publisher : sig (** [request_failed view status errors] emits the event that a worker diff --git a/src/lib_smart_rollup_node/publisher.ml b/src/lib_smart_rollup_node/publisher.ml index eb39ab47ead3..8298bfb9f340 100644 --- a/src/lib_smart_rollup_node/publisher.ml +++ b/src/lib_smart_rollup_node/publisher.ml @@ -285,6 +285,16 @@ let publish_commitment (node_ctxt : _ Node_context.t) ~source let* _hash = Injector.add_pending_operation ~source publish_operation in return_unit +let inject_recover_bond (node_ctxt : _ Node_context.t) ~source + (staker : Signature.Public_key_hash.t) = + let open Lwt_result_syntax in + let recover_operation = + L1_operation.Recover_bond {rollup = node_ctxt.rollup_address; staker} + in + let*! () = Commitment_event.recover_bond staker in + let* _hash = Injector.add_pending_operation ~source recover_operation in + return_unit + let on_publish_commitments (node_ctxt : state) = let open Lwt_result_syntax in let operator = Node_context.get_operator node_ctxt Operating in @@ -294,7 +304,7 @@ let on_publish_commitments (node_ctxt : state) = else match operator with | None -> - (* Configured to not publish commitments *) + (* No known operator we can recover bond for. *) return_unit | Some source -> let* commitments = missing_commitments node_ctxt in @@ -313,6 +323,18 @@ let publish_single_commitment node_ctxt when_ (commitment.inbox_level > lcc.level) @@ fun () -> publish_commitment node_ctxt ~source commitment +let recover_bond node_ctxt = + let open Lwt_result_syntax in + let operator = Node_context.get_operator node_ctxt Operating in + let recovery_operator = Node_context.get_operator node_ctxt Recovering in + match operator with + | None -> + (* No known operator to recover bond for. *) + return_unit + | Some committer -> + let source = Option.value recovery_operator ~default:committer in + inject_recover_bond node_ctxt ~source committer + (* Commitments can only be cemented after [sc_rollup_challenge_window] has passed since they were first published. *) let earliest_cementing_level node_ctxt commitment_hash = diff --git a/src/lib_smart_rollup_node/publisher.mli b/src/lib_smart_rollup_node/publisher.mli index af5afb221c72..15bf4a008867 100644 --- a/src/lib_smart_rollup_node/publisher.mli +++ b/src/lib_smart_rollup_node/publisher.mli @@ -60,6 +60,12 @@ val process_head : val publish_single_commitment : _ Node_context.t -> Commitment.t -> unit tzresult Lwt.t +(** [recover_bond node_ctxt] publishes a recover bond operator for the + Operating key. The submitter is either the operator or another + address depending of the rollup node configuration. This function + is intended to be used by the {e bailout} mode. *) +val recover_bond : _ Node_context.t -> unit tzresult Lwt.t + (** Initialize worker for publishing and cementing commitments. *) val init : _ Node_context.t -> unit tzresult Lwt.t diff --git a/src/proto_alpha/lib_sc_rollup_node/daemon_helpers.ml b/src/proto_alpha/lib_sc_rollup_node/daemon_helpers.ml index 5e4a5a985102..4c072573b6a4 100644 --- a/src/proto_alpha/lib_sc_rollup_node/daemon_helpers.ml +++ b/src/proto_alpha/lib_sc_rollup_node/daemon_helpers.ml @@ -105,6 +105,27 @@ let accuser_publish_commitment_when_refutable node_ctxt ~other rollup assert (Sc_rollup.Address.(node_ctxt.rollup_address = rollup)) ; Publisher.publish_single_commitment node_ctxt our_commitment +(** If in bailout mode and when the operator is not staked on any + commitment, the bond is recovered. *) +let maybe_recover_bond node_ctxt = + let open Lwt_result_syntax in + if Node_context.is_bailout node_ctxt then + let operating_pkh = Node_context.get_operator node_ctxt Operating in + match operating_pkh with + | None -> return_unit + | Some operating_pkh -> ( + let* staked_on_commitment = + RPC.Sc_rollup.staked_on_commitment + (new Protocol_client_context.wrap_full node_ctxt.cctxt) + (node_ctxt.cctxt#chain, `Head 0) + node_ctxt.rollup_address + operating_pkh + in + match staked_on_commitment with + | None -> Publisher.recover_bond node_ctxt + | Some _ (* operator still staked on something *) -> return_unit) + else return_unit + (** Process an L1 SCORU operation (for the node's rollup) which is included for the first time. {b Note}: this function does not process inboxes for the rollup, which is done instead by {!Inbox.process_head}. *) @@ -213,6 +234,7 @@ let process_included_l1_operation (type kind) (node_ctxt : Node_context.rw) inbox_level) else Lwt.return_unit in + let* () = maybe_recover_bond node_ctxt in return_unit | ( Sc_rollup_refute _, Sc_rollup_refute_result {game_status = Ended end_status; _} ) -- GitLab From b0c3b26093f754c5834eef244bcdadb5c17df308 Mon Sep 17 00:00:00 2001 From: Sylvain Ribstein Date: Thu, 17 Aug 2023 17:29:29 +0200 Subject: [PATCH 4/5] tezt/soru: bailout mode submit recover bond --- tezt/tests/sc_rollup.ml | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/tezt/tests/sc_rollup.ml b/tezt/tests/sc_rollup.ml index 307ad94670c2..08fcd27d0f03 100644 --- a/tezt/tests/sc_rollup.ml +++ b/tezt/tests/sc_rollup.ml @@ -5895,12 +5895,12 @@ let test_rollup_whitelist_outdated_update ~kind = ~msg:(rex ".*Outdated whitelist update: got outbox level") process -(** This test uses the rollup node, first it is running in an - Operator mode, it bakes some blocks, then terminate. Then we - restart the node in a Bailout mode, and make sure that there are - no new commitments have been published *) +(** This test uses the rollup node, first it is running in an Operator + mode, it bakes some blocks, then terminate. Then we restart the + node in a Bailout mode, initiate the recover_bond process, and + make sure that no new commitments are published. *) let bailout_mode_not_publish ~kind = - let operator = Constant.bootstrap1.public_key_hash in + let operator = Constant.bootstrap5.public_key_hash in let commitment_period = 5 in let challenge_window = 5 in test_full_scenario @@ -5955,7 +5955,6 @@ let bailout_mode_not_publish ~kind = [] ~mode:Bailout in - let* () = Sc_rollup_node.wait_for_ready sc_rollup_node in (* The challenge window is neded to compute the correct number of block before cementation, we also add 2 times of commitment period to make sure no commit are published. *) @@ -5963,6 +5962,11 @@ let bailout_mode_not_publish ~kind = repeat ((2 * commitment_period) + challenge_window) (fun () -> Client.bake_for_and_wait tezos_client) + and* () = + Sc_rollup_node.wait_for + sc_rollup_node + "sc_rollup_node_recover_bond.v0" + (Fun.const (Some ())) in let* _ = Sc_rollup_node.wait_sync sc_rollup_node ~timeout:100. in let* published_commitment_after = @@ -5988,14 +5992,16 @@ let bailout_mode_not_publish ~kind = Check.string ~error_msg:"Last published commitment have been updated." in - let* () = Sc_rollup_node.terminate sc_rollup_node in - Log.info "Client submits the recover_bond operation." ; - let*! () = - Client.Sc_rollup.submit_recover_bond - ~rollup:sc_rollup - ~src:operator - ~staker:operator - tezos_client + Log.info + "The node has submitted the recover_bond operation, and the operator is no \ + longer staked." ; + let* operator_balance = contract_balances ~pkh:operator tezos_client in + let () = + Check.( + (operator_balance.frozen = 0) + int + ~error_msg: + "The operator should not have a stake nor holds a frozen balance.") in unit -- GitLab From 35d979be0d6facf60bda855cf3ea79f34102adb7 Mon Sep 17 00:00:00 2001 From: Sylvain Ribstein Date: Fri, 18 Aug 2023 09:35:17 +0200 Subject: [PATCH 5/5] rollup/node: backport inject recover bond --- .../lib_sc_rollup_node/daemon_helpers.ml | 22 +++++++++++++++++++ .../lib_sc_rollup_node/daemon_helpers.ml | 22 +++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/proto_017_PtNairob/lib_sc_rollup_node/daemon_helpers.ml b/src/proto_017_PtNairob/lib_sc_rollup_node/daemon_helpers.ml index 291b988c9598..1aa60be37de5 100644 --- a/src/proto_017_PtNairob/lib_sc_rollup_node/daemon_helpers.ml +++ b/src/proto_017_PtNairob/lib_sc_rollup_node/daemon_helpers.ml @@ -102,6 +102,27 @@ let accuser_publish_commitment_when_refutable node_ctxt ~other rollup assert (Octez_smart_rollup.Address.(node_ctxt.rollup_address = rollup)) ; Publisher.publish_single_commitment node_ctxt our_commitment +(** If in bailout mode and when the operator is not staked on any + commitment, the bond is recovered. *) +let maybe_recover_bond node_ctxt = + let open Lwt_result_syntax in + if Node_context.is_bailout node_ctxt then + let operating_pkh = Node_context.get_operator node_ctxt Operating in + match operating_pkh with + | None -> return_unit + | Some operating_pkh -> ( + let* staked_on_commitment = + RPC.Sc_rollup.staked_on_commitment + (new Protocol_client_context.wrap_full node_ctxt.cctxt) + (node_ctxt.cctxt#chain, `Head 0) + (Sc_rollup_proto_types.Address.of_octez node_ctxt.rollup_address) + operating_pkh + in + match staked_on_commitment with + | None -> Publisher.recover_bond node_ctxt + | Some _ (* operator still staked on something *) -> return_unit) + else return_unit + (** Process an L1 SCORU operation (for the node's rollup) which is included for the first time. {b Note}: this function does not process inboxes for the rollup, which is done instead by {!Inbox.process_head}. *) @@ -216,6 +237,7 @@ let process_included_l1_operation (type kind) (node_ctxt : Node_context.rw) inbox_level) else Lwt.return_unit in + let* () = maybe_recover_bond node_ctxt in return_unit | ( Sc_rollup_refute _, Sc_rollup_refute_result {game_status = Ended end_status; _} ) diff --git a/src/proto_018_Proxford/lib_sc_rollup_node/daemon_helpers.ml b/src/proto_018_Proxford/lib_sc_rollup_node/daemon_helpers.ml index 5e4a5a985102..4c072573b6a4 100644 --- a/src/proto_018_Proxford/lib_sc_rollup_node/daemon_helpers.ml +++ b/src/proto_018_Proxford/lib_sc_rollup_node/daemon_helpers.ml @@ -105,6 +105,27 @@ let accuser_publish_commitment_when_refutable node_ctxt ~other rollup assert (Sc_rollup.Address.(node_ctxt.rollup_address = rollup)) ; Publisher.publish_single_commitment node_ctxt our_commitment +(** If in bailout mode and when the operator is not staked on any + commitment, the bond is recovered. *) +let maybe_recover_bond node_ctxt = + let open Lwt_result_syntax in + if Node_context.is_bailout node_ctxt then + let operating_pkh = Node_context.get_operator node_ctxt Operating in + match operating_pkh with + | None -> return_unit + | Some operating_pkh -> ( + let* staked_on_commitment = + RPC.Sc_rollup.staked_on_commitment + (new Protocol_client_context.wrap_full node_ctxt.cctxt) + (node_ctxt.cctxt#chain, `Head 0) + node_ctxt.rollup_address + operating_pkh + in + match staked_on_commitment with + | None -> Publisher.recover_bond node_ctxt + | Some _ (* operator still staked on something *) -> return_unit) + else return_unit + (** Process an L1 SCORU operation (for the node's rollup) which is included for the first time. {b Note}: this function does not process inboxes for the rollup, which is done instead by {!Inbox.process_head}. *) @@ -213,6 +234,7 @@ let process_included_l1_operation (type kind) (node_ctxt : Node_context.rw) inbox_level) else Lwt.return_unit in + let* () = maybe_recover_bond node_ctxt in return_unit | ( Sc_rollup_refute _, Sc_rollup_refute_result {game_status = Ended end_status; _} ) -- GitLab