package tezos-protocol-013-PtJakart
Tezos/Protocol: economic-protocol definition
Install
Dune Dependency
Authors
Maintainers
Sources
tezos-16.0.tar.gz
sha256=ad9e08819871c75ba6f4530b125f7d157799398e4d77a1e6bfea9d91ff37ff55
sha512=c5dc4d40cc09bc6980fbbdb5c2e105bf4252cf9cfcb2b49660b0ebe4dc789f6709ec3b3bf2f87d81580d3eed9521eeb1c960f24d9b14eb0285aaba1f84d10a9b
doc/src/tezos-protocol-013-PtJakart.raw/sc_rollup_storage.ml.html
Source file sc_rollup_storage.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740
(*****************************************************************************) (* *) (* Open Source License *) (* Copyright (c) 2021 Nomadic Labs <contact@nomadic-labs.com> *) (* Copyright (c) 2022 TriliTech <contact@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. *) (* *) (*****************************************************************************) type error += | (* `Temporary *) Sc_rollup_already_staked | (* `Temporary *) Sc_rollup_disputed | (* `Temporary *) Sc_rollup_does_not_exist of Sc_rollup_repr.t | (* `Temporary *) Sc_rollup_no_conflict | (* `Temporary *) Sc_rollup_no_stakers | (* `Temporary *) Sc_rollup_not_staked | (* `Temporary *) Sc_rollup_not_staked_on_lcc | (* `Temporary *) Sc_rollup_parent_not_lcc | (* `Temporary *) Sc_rollup_remove_lcc | (* `Temporary *) Sc_rollup_staker_backtracked | (* `Temporary *) Sc_rollup_too_far_ahead | (* `Temporary *) Sc_rollup_too_recent | (* `Temporary *) Sc_rollup_unknown_commitment of Sc_rollup_repr.Commitment_hash.t | (* `Temporary *) Sc_rollup_bad_inbox_level | (* `Temporary *) Sc_rollup_max_number_of_available_messages_reached let () = register_error_kind `Temporary ~id:"Sc_rollup_max_number_of_available_messages_reached" ~title:"Maximum number of available messages reached" ~description:"Maximum number of available messages reached" Data_encoding.unit (function | Sc_rollup_max_number_of_available_messages_reached -> Some () | _ -> None) (fun () -> Sc_rollup_max_number_of_available_messages_reached) ; let description = "Already staked." in register_error_kind `Temporary ~id:"Sc_rollup_already_staked" ~title:"Already staked" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_already_staked -> Some () | _ -> None) (fun () -> Sc_rollup_already_staked) ; let description = "Attempted to cement a disputed commitment." in register_error_kind `Temporary ~id:"Sc_rollup_disputed" ~title:"Commitment disputed" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_disputed -> Some () | _ -> None) (fun () -> Sc_rollup_disputed) ; let description = "Attempted to use a rollup that has not been originated." in register_error_kind `Temporary ~id:"Sc_rollup_does_not_exist" ~title:"Rollup does not exist" ~description ~pp:(fun ppf x -> Format.fprintf ppf "Rollup %a does not exist" Sc_rollup_repr.pp x) Data_encoding.(obj1 (req "rollup" Sc_rollup_repr.encoding)) (function Sc_rollup_does_not_exist x -> Some x | _ -> None) (fun x -> Sc_rollup_does_not_exist x) ; let description = "No conflict." in register_error_kind `Temporary ~id:"Sc_rollup_no_conflict" ~title:"No conflict" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_no_conflict -> Some () | _ -> None) (fun () -> Sc_rollup_no_conflict) ; let description = "No stakers." in register_error_kind `Temporary ~id:"Sc_rollup_no_stakers" ~title:"No stakers" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_no_stakers -> Some () | _ -> None) (fun () -> Sc_rollup_no_stakers) ; let description = "Unknown staker." in register_error_kind `Temporary ~id:"Sc_rollup_not_staked" ~title:"Unknown staker" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_not_staked -> Some () | _ -> None) (fun () -> Sc_rollup_not_staked) ; let description = "Attempted to withdraw while not staked on the last cemented commitment." in register_error_kind `Temporary ~id:"Sc_rollup_not_staked_on_lcc" ~title:"Rollup not staked on LCC" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_not_staked_on_lcc -> Some () | _ -> None) (fun () -> Sc_rollup_not_staked_on_lcc) ; let description = "Parent is not cemented." in register_error_kind `Temporary ~id:"Sc_rollup_parent_not_lcc" ~title:"Parent not cemented" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_parent_not_lcc -> Some () | _ -> None) (fun () -> Sc_rollup_parent_not_lcc) ; let description = "Can not remove a cemented commitment." in register_error_kind `Temporary ~id:"Sc_rollup_remove_lcc" ~title:"Can not remove cemented" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_remove_lcc -> Some () | _ -> None) (fun () -> Sc_rollup_remove_lcc) ; let description = "Staker backtracked." in register_error_kind `Temporary ~id:"Sc_rollup_staker_backtracked" ~title:"Staker backtracked" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_staker_backtracked -> Some () | _ -> None) (fun () -> Sc_rollup_staker_backtracked) ; let description = "Commitment is too far ahead of the last cemented commitment." in register_error_kind `Temporary ~id:"Sc_rollup_too_far_ahead" ~title:"Commitment too far ahead" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_too_far_ahead -> Some () | _ -> None) (fun () -> Sc_rollup_too_far_ahead) ; let description = "Attempted to cement a commitment before its refutation deadline." in register_error_kind `Temporary ~id:"Sc_rollup_too_recent" ~title:"Commitment too recent" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_too_recent -> Some () | _ -> None) (fun () -> Sc_rollup_too_recent) ; let description = "Unknown commitment." in register_error_kind `Temporary ~id:"Sc_rollup_unknown_commitment" ~title:"Rollup does not exist" ~description ~pp:(fun ppf x -> Format.fprintf ppf "Commitment %a does not exist" Sc_rollup_repr.Commitment_hash.pp x) Data_encoding.( obj1 (req "commitment" Sc_rollup_repr.Commitment_hash.encoding)) (function Sc_rollup_unknown_commitment x -> Some x | _ -> None) (fun x -> Sc_rollup_unknown_commitment x) ; let description = "Attempted to commit to a bad inbox level." in register_error_kind `Temporary ~id:"Sc_rollup_bad_inbox_level" ~title:"Committed too soon" ~description ~pp:(fun ppf () -> Format.fprintf ppf "%s" description) Data_encoding.empty (function Sc_rollup_bad_inbox_level -> Some () | _ -> None) (fun () -> Sc_rollup_bad_inbox_level) ; () (** To be removed once [Lwt_tzresult_syntax] is in the environment. *) module Lwt_tzresult_syntax = struct let ( let* ) = ( >>=? ) let return = return end module Store = Storage.Sc_rollup module Commitment = Sc_rollup_repr.Commitment module Commitment_hash = Sc_rollup_repr.Commitment_hash 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 >>= fun ctxt -> Storage.Sc_rollup.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.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) -> let addresses_size = 2 * Sc_rollup_repr.Address.size in let stored_kind_size = 2 (* because tag_size of kind encoding is 16bits. *) in let boot_sector_size = Data_encoding.Binary.length Data_encoding.string boot_sector in let origination_size = Constants_storage.sc_rollup_origination_size ctxt in let size = Z.of_int (origination_size + stored_kind_size + boot_sector_size + addresses_size + size_diff + lcc_size_diff + stakers_size_diff) in return (address, size, ctxt) let kind ctxt address = Storage.Sc_rollup.PVM_kind.find ctxt address let last_cemented_commitment ctxt rollup = let open Lwt_tzresult_syntax in let* (ctxt, res) = Store.Last_cemented_commitment.find ctxt rollup in match res with | None -> fail (Sc_rollup_does_not_exist rollup) | Some lcc -> return (lcc, ctxt) (** 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 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 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 match res with | None -> fail (Sc_rollup_does_not_exist rollup) | Some inbox -> return (inbox, ctxt) let assert_inbox_size_ok ctxt next_size = let max_size = Constants_storage.sc_rollup_max_available_messages ctxt in fail_unless Compare.Z.(next_size <= Z.of_int max_size) Sc_rollup_max_number_of_available_messages_reached let add_messages ctxt rollup messages = let open Raw_context in inbox ctxt rollup >>=? fun (inbox, ctxt) -> let next_size = Z.add (Sc_rollup_inbox_repr.number_of_available_messages inbox) (Z.of_int (List.length messages)) in assert_inbox_size_ok ctxt next_size >>=? fun () -> Sc_rollup_in_memory_inbox.current_messages ctxt rollup |> fun current_messages -> let {Level_repr.level; _} = Raw_context.current_level ctxt in (* Notice that the protocol is forgetful: it throws away the inbox history. On the contrary, the history is stored by the rollup node to produce inclusion proofs when needed. *) Sc_rollup_inbox_repr.( add_messages_no_history inbox level messages current_messages) >>=? fun (current_messages, inbox) -> Sc_rollup_in_memory_inbox.set_current_messages ctxt rollup current_messages |> fun ctxt -> Storage.Sc_rollup.Inbox.update ctxt rollup inbox >>=? fun (ctxt, size) -> return (inbox, Z.of_int size, ctxt) (* This function is called in other functions in the module only after they have checked for the existence of the rollup, and therefore it is not necessary for it to check for the existence of the rollup again. It is not directly exposed by the module. Instead, a different public function [get_commitment] is provided, which checks for the existence of [rollup] before calling [get_commitment_internal]. *) let get_commitment_internal ctxt rollup commitment = let open Lwt_tzresult_syntax in let* (ctxt, res) = Store.Commitments.find (ctxt, rollup) commitment in match res with | None -> fail (Sc_rollup_unknown_commitment commitment) | Some commitment -> return (commitment, ctxt) let get_commitment ctxt rollup commitment = let open Lwt_tzresult_syntax in (* Assert that a last cemented commitment exists. *) let* (_lcc, ctxt) = last_cemented_commitment ctxt rollup in get_commitment_internal ctxt rollup commitment let get_predecessor ctxt rollup node = let open Lwt_tzresult_syntax in let* (commitment, ctxt) = get_commitment_internal ctxt rollup node in return (commitment.predecessor, ctxt) let find_staker ctxt rollup staker = let open Lwt_tzresult_syntax in let* (ctxt, res) = Store.Stakers.find (ctxt, rollup) staker in match res with | None -> fail Sc_rollup_not_staked | Some branch -> return (branch, ctxt) let modify_staker_count ctxt rollup f = let open Lwt_tzresult_syntax in let* (ctxt, maybe_count) = Store.Staker_count.find ctxt rollup in let count = Option.value ~default:0l maybe_count in let* (ctxt, size_diff, _was_bound) = Store.Staker_count.add ctxt rollup (f count) in assert (Compare.Int.(size_diff = 0)) ; return ctxt let get_commitment_stake_count ctxt rollup node = let open Lwt_tzresult_syntax in let* (ctxt, maybe_staked_on_commitment) = Store.Commitment_stake_count.find (ctxt, rollup) node in return (Option.value ~default:0l maybe_staked_on_commitment, ctxt) (** [set_commitment_added ctxt rollup node current] sets the commitment addition time of [node] to [current] iff the commitment time was not previously set, and leaves it unchanged otherwise. *) let set_commitment_added ctxt rollup node new_value = let open Lwt_tzresult_syntax in let* (ctxt, res) = Store.Commitment_added.find (ctxt, rollup) node in let new_value = match res with None -> new_value | Some old_value -> old_value in let* (ctxt, size_diff, _was_bound) = Store.Commitment_added.add (ctxt, rollup) node new_value in return (size_diff, ctxt) let deallocate ctxt rollup node = let open Lwt_tzresult_syntax in if Commitment_hash.(node = zero) then return ctxt else let* (ctxt, _size_freed) = Store.Commitments.remove_existing (ctxt, rollup) node in let* (ctxt, _size_freed) = Store.Commitment_added.remove_existing (ctxt, rollup) node in let* (ctxt, _size_freed) = Store.Commitment_stake_count.remove_existing (ctxt, rollup) node in return ctxt let modify_commitment_stake_count ctxt rollup node f = let open Lwt_tzresult_syntax in let* (count, ctxt) = get_commitment_stake_count ctxt rollup node in let new_count = f count in let* (ctxt, size_diff, _was_bound) = Store.Commitment_stake_count.add (ctxt, rollup) node new_count in return (new_count, size_diff, ctxt) let increase_commitment_stake_count ctxt rollup node = let open Lwt_tzresult_syntax in let* (_new_count, size_diff, ctxt) = modify_commitment_stake_count ctxt rollup node Int32.succ in return (size_diff, ctxt) let decrease_commitment_stake_count ctxt rollup node = let open Lwt_tzresult_syntax in let* (new_count, _size_diff, ctxt) = modify_commitment_stake_count ctxt rollup node Int32.pred in if Compare.Int32.(new_count <= 0l) then deallocate ctxt rollup node else return ctxt let deposit_stake ctxt rollup staker = let open Lwt_tzresult_syntax in let* (lcc, ctxt) = last_cemented_commitment ctxt rollup in let* (ctxt, res) = Store.Stakers.find (ctxt, rollup) staker in match res with | None -> (* TODO: https://gitlab.com/tezos/tezos/-/issues/2449 We should lock stake here, and fail if there aren't enough funds. *) let* (ctxt, _size) = Store.Stakers.init (ctxt, rollup) staker lcc in let* ctxt = modify_staker_count ctxt rollup Int32.succ in return ctxt | Some _ -> fail Sc_rollup_already_staked let withdraw_stake ctxt rollup staker = let open Lwt_tzresult_syntax in let* (lcc, ctxt) = last_cemented_commitment ctxt rollup in let* (ctxt, res) = Store.Stakers.find (ctxt, rollup) staker in match res with | None -> fail Sc_rollup_not_staked | Some staked_on_commitment -> if Commitment_hash.(staked_on_commitment = lcc) then (* TODO: https://gitlab.com/tezos/tezos/-/issues/2449 We should refund stake here. *) let* (ctxt, _size_freed) = Store.Stakers.remove_existing (ctxt, rollup) staker in let* ctxt = modify_staker_count ctxt rollup Int32.pred in modify_staker_count ctxt rollup Int32.pred else fail Sc_rollup_not_staked_on_lcc (* TODO https://gitlab.com/tezos/tezos/-/issues/2548 These should be protocol constants. See issue for invariant that should be tested for. *) let sc_rollup_max_lookahead = 30_000l let sc_rollup_commitment_frequency = (* Think twice before changing this. And then avoid doing it. *) 20 (* 76 for Commitments entry + 4 for Commitment_stake_count entry + 4 for Commitment_added entry *) let sc_rollup_commitment_storage_size_in_bytes = 84 let assert_commitment_not_too_far_ahead ctxt rollup lcc commitment = let open Lwt_tzresult_syntax in let* (ctxt, min_level) = if Commitment_hash.(lcc = zero) then let* level = Store.Initial_level.get ctxt rollup in return (ctxt, level) else let* (lcc, ctxt) = get_commitment_internal ctxt rollup lcc in return (ctxt, Commitment.(lcc.inbox_level)) in let max_level = Commitment.(commitment.inbox_level) in if Compare.Int32.( sc_rollup_max_lookahead < Raw_level_repr.diff max_level min_level) then fail Sc_rollup_too_far_ahead else return ctxt (** Enfore that a commitment's inbox level increases by an exact fixed amount over its predecessor. This property is used in several places - not obeying it causes severe breakage. *) let assert_commitment_frequency ctxt rollup commitment = let open Lwt_tzresult_syntax in let pred = Commitment.(commitment.predecessor) in let* (ctxt, pred_level) = if Commitment_hash.(pred = zero) then let* level = Store.Initial_level.get ctxt rollup in return (ctxt, level) else let* (pred, ctxt) = get_commitment_internal ctxt rollup commitment.predecessor in return (ctxt, Commitment.(pred.inbox_level)) in (* We want to check the following inequalities on [commitment.inbox_level], [commitment.predecessor.inbox_level] and the constant [sc_rollup_commitment_frequency]. - Greater-than-or-equal (>=), to ensure inbox_levels are monotonically increasing. along each branch of commitments. Together with [assert_commitment_not_too_far_ahead] this is sufficient to limit the depth of the commitment tree, which is also the number commitments stored per staker. This constraint must be enforced at submission time. - Equality (=), so that that L2 blocks are produced at a regular rate. This ensures that there is only ever one branch of correct commitments, simplifying refutation logic. This could also be enforced at refutation time rather than submission time, but doing it here works too. Because [a >= b && a = b] is equivalent to [a = b], we can the latter as an optimization. *) if Raw_level_repr.( commitment.inbox_level = add pred_level sc_rollup_commitment_frequency) then return ctxt else fail Sc_rollup_bad_inbox_level (** Check invariants on [inbox_level], enforcing overallocation of storage and regularity of block prorudction. The constants used by [assert_refine_conditions_met] must be chosen such that the maximum cost of storage allocated by each staker at most the size of their deposit. *) let assert_refine_conditions_met ctxt rollup lcc commitment = let open Lwt_tzresult_syntax in let* ctxt = assert_commitment_not_too_far_ahead ctxt rollup lcc commitment in assert_commitment_frequency ctxt rollup commitment let refine_stake ctxt rollup staker commitment = let open Lwt_tzresult_syntax in let* (lcc, ctxt) = last_cemented_commitment ctxt rollup in let* (staked_on, ctxt) = find_staker ctxt rollup staker in let* ctxt = assert_refine_conditions_met ctxt rollup lcc commitment in let new_hash = Commitment.hash commitment in (* TODO: https://gitlab.com/tezos/tezos/-/issues/2559 Add a test checking that L2 nodes can catch up after going offline. *) let rec go node ctxt = (* WARNING: Do NOT reorder this sequence of ifs. we must check for staked_on before LCC, since refining from the LCC to another commit is a valid operation. *) if Commitment_hash.(node = staked_on) then ( (* Previously staked commit found: Insert new commitment if not existing *) let* (ctxt, commitment_size_diff, _was_bound) = Store.Commitments.add (ctxt, rollup) new_hash commitment in let level = (Raw_context.current_level ctxt).level in let* (commitment_added_size_diff, ctxt) = set_commitment_added ctxt rollup new_hash level in let* (ctxt, staker_count_diff) = Store.Stakers.update (ctxt, rollup) staker new_hash in let* (stake_count_size_diff, ctxt) = increase_commitment_stake_count ctxt rollup new_hash in let size_diff = commitment_size_diff + commitment_added_size_diff + stake_count_size_diff + staker_count_diff in (* First submission adds [sc_rollup_commitment_storage_size_in_bytes] to storage. Later submission adds 0 due to content-addressing. *) assert ( Compare.Int.( size_diff = 0 || size_diff = sc_rollup_commitment_storage_size_in_bytes)) ; return (new_hash, ctxt) (* See WARNING above. *)) else if Commitment_hash.(node = lcc) then (* We reached the LCC, but [staker] is not staked directly on it. Thus, we backtracked. Note that everyone is staked indirectly on the LCC. *) fail Sc_rollup_staker_backtracked else let* (pred, ctxt) = get_predecessor ctxt rollup node in let* (_size, ctxt) = increase_commitment_stake_count ctxt rollup node in (go [@ocaml.tailcall]) pred ctxt in go Commitment.(commitment.predecessor) ctxt let publish_commitment ctxt rollup staker commitment = let open Lwt_tzresult_syntax in let* (ctxt, res) = Store.Stakers.find (ctxt, rollup) staker in match res with | None -> let* ctxt = deposit_stake ctxt rollup staker in refine_stake ctxt rollup staker commitment | Some _ -> refine_stake ctxt rollup staker commitment let cement_commitment ctxt rollup new_lcc = let open Lwt_tzresult_syntax in let refutation_deadline_blocks = Constants_storage.sc_rollup_challenge_window_in_blocks ctxt in (* Calling [last_final_commitment] first to trigger failure in case of non-existing rollup. *) let* (old_lcc, ctxt) = last_cemented_commitment ctxt rollup in (* Get is safe, as [Stakers_size] is initialized on origination. *) let* (ctxt, total_staker_count) = Store.Staker_count.get ctxt rollup in if Compare.Int32.(total_staker_count <= 0l) then fail Sc_rollup_no_stakers else let* (new_lcc_commitment, ctxt) = get_commitment_internal ctxt rollup new_lcc in let* (ctxt, new_lcc_added) = Store.Commitment_added.get (ctxt, rollup) new_lcc in if Commitment_hash.(new_lcc_commitment.predecessor <> old_lcc) then fail Sc_rollup_parent_not_lcc else let* (new_lcc_stake_count, ctxt) = get_commitment_stake_count ctxt rollup new_lcc in if Compare.Int32.(total_staker_count <> new_lcc_stake_count) then fail Sc_rollup_disputed else if let level = (Raw_context.current_level ctxt).level in Raw_level_repr.(level < add new_lcc_added refutation_deadline_blocks) then fail Sc_rollup_too_recent else (* update LCC *) let* (ctxt, lcc_size_diff) = Store.Last_cemented_commitment.update ctxt rollup new_lcc in assert (Compare.Int.(lcc_size_diff = 0)) ; (* At this point we know all stakers are implicitly staked on the new LCC, and no one is directly staked on the old LCC. We can safely deallocate the old LCC. *) let* ctxt = deallocate ctxt rollup old_lcc in consume_n_messages ctxt rollup (Int32.to_int @@ Sc_rollup_repr.Number_of_messages.to_int32 new_lcc_commitment.number_of_messages) type conflict_point = Commitment_hash.t * Commitment_hash.t (** [goto_inbox_level ctxt rollup inbox_level commit] Follows the predecessors of [commit] until it arrives at the exact [inbox_level]. The result is the commit hash at the given inbox level. *) let goto_inbox_level ctxt rollup inbox_level commit = let open Lwt_tzresult_syntax in let rec go ctxt commit = let* (info, ctxt) = get_commitment_internal ctxt rollup commit in if Raw_level_repr.(info.Commitment.inbox_level <= inbox_level) then ( (* Assert that we're exactly at that level. If this isn't the case, we're most likely in a situation where inbox levels are inconsistent. *) assert (Raw_level_repr.(info.inbox_level = inbox_level)) ; return (commit, ctxt)) else (go [@ocaml.tailcall]) ctxt info.predecessor in go ctxt commit let get_conflict_point ctxt rollup staker1 staker2 = let open Lwt_tzresult_syntax in (* Ensure the LCC is set. *) let* (lcc, ctxt) = last_cemented_commitment ctxt rollup in (* Find out on which commitments the competitors are staked. *) let* (commit1, ctxt) = find_staker ctxt rollup staker1 in let* (commit2, ctxt) = find_staker ctxt rollup staker2 in let* () = fail_when Commitment_hash.( (* If PVM is in pre-boot state, there might be stakes on the zero commitment. *) commit1 = zero || commit2 = zero (* If either commit is the LCC, that also means there can't be a conflict. *) || commit1 = lcc || commit2 = lcc) Sc_rollup_no_conflict in let* (commit1_info, ctxt) = get_commitment_internal ctxt rollup commit1 in let* (commit2_info, ctxt) = get_commitment_internal ctxt rollup commit2 in (* Make sure that both commits are at the same inbox level. In case they are not move the commit that is farther ahead to the exact inbox level of the other. We do this instead of an alternating traversal of either commit to ensure the we can detect wonky inbox level increases. For example, if the inbox levels decrease in different intervals between commits for either history, we risk going past the conflict point and accidentally determined that the commits are not in conflict by joining at the same commit. *) let target_inbox_level = Raw_level_repr.min commit1_info.inbox_level commit2_info.inbox_level in let* (commit1, ctxt) = goto_inbox_level ctxt rollup target_inbox_level commit1 in let* (commit2, ctxt) = goto_inbox_level ctxt rollup target_inbox_level commit2 in (* The inbox level of a commitment increases by a fixed amount over the preceding commitment. We use this fact in the following to efficiently traverse both commitment histories towards the conflict points. *) let rec traverse_in_parallel ctxt commit1 commit2 = if Commitment_hash.(commit1 = commit2) then (* This case will most dominantly happen when either commit is part of the other's history. It occurs when the commit that is farther ahead gets dereferenced to its predecessor often enough to land at the other commit. *) fail Sc_rollup_no_conflict else let* (commit1_info, ctxt) = get_commitment_internal ctxt rollup commit1 in let* (commit2_info, ctxt) = get_commitment_internal ctxt rollup commit2 in assert ( Raw_level_repr.(commit1_info.inbox_level = commit2_info.inbox_level)) ; if Commitment_hash.(commit1_info.predecessor = commit2_info.predecessor) then (* Same predecessor means we've found the conflict points. *) return ((commit1, commit2), ctxt) else (* Different predecessors means they run in parallel. *) (traverse_in_parallel [@ocaml.tailcall]) ctxt commit1_info.predecessor commit2_info.predecessor in traverse_in_parallel ctxt commit1 commit2 let remove_staker ctxt rollup staker = let open Lwt_tzresult_syntax in let* (lcc, ctxt) = last_cemented_commitment ctxt rollup in let* (ctxt, res) = Store.Stakers.find (ctxt, rollup) staker in match res with | None -> fail Sc_rollup_not_staked | Some staked_on -> if Commitment_hash.(staked_on = lcc) then fail Sc_rollup_remove_lcc else let* (ctxt, _size_diff) = Store.Stakers.remove_existing (ctxt, rollup) staker in let* ctxt = modify_staker_count ctxt rollup Int32.pred in let rec go node ctxt = if Commitment_hash.(node = lcc) then return ctxt else let* (pred, ctxt) = get_predecessor ctxt rollup node in let* ctxt = decrease_commitment_stake_count ctxt rollup node in (go [@ocaml.tailcall]) pred ctxt in go staked_on ctxt let list ctxt = Storage.Sc_rollup.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 match level with | None -> fail (Sc_rollup_does_not_exist rollup) | Some level -> return level
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>