package octez-protocol-alpha-libs
Octez protocol alpha libraries
Install
Dune Dependency
Authors
Maintainers
Sources
octez-19.0.tar.gz
sha256=c6df840ebbf115e454db949028c595bec558a59a66cade73b52a6d099d6fa4d4
sha512=d8aee903b9fe130d73176bc8ec38b78c9ff65317da3cb4f3415f09af0c625b4384e7498201fdb61aa39086a7d5d409d0ab3423f9bc3ab989a680cf444a79bc13
doc/src/octez-protocol-alpha-libs.test-helpers/adaptive_issuance_helpers.ml.html
Source file adaptive_issuance_helpers.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 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296
(*****************************************************************************) (* *) (* Open Source License *) (* Copyright (c) 2023 Nomadic Labs <contact@nomadic-labs.com> *) (* *) (* Permission is hereby granted, free of charge, to any person obtaining a *) (* copy of this software and associated documentation files (the "Software"),*) (* to deal in the Software without restriction, including without limitation *) (* the rights to use, copy, modify, merge, publish, distribute, sublicense, *) (* and/or sell copies of the Software, and to permit persons to whom the *) (* Software is furnished to do so, subject to the following conditions: *) (* *) (* The above copyright notice and this permission notice shall be included *) (* in all copies or substantial portions of the Software. *) (* *) (* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR*) (* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *) (* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *) (* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER*) (* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING *) (* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER *) (* DEALINGS IN THE SOFTWARE. *) (* *) (*****************************************************************************) let join_errors e1 e2 = let open Lwt_result_syntax in match (e1, e2) with | Ok (), Ok () -> return_unit | Error e, Ok () | Ok (), Error e -> fail e | Error e1, Error e2 -> fail (e1 @ e2) (** Tez manipulation module *) module Tez = struct include Protocol.Alpha_context.Tez let ( + ) a b = let open Lwt_result_wrap_syntax in let*?@ s = a +? b in return s let ( - ) a b = let open Lwt_result_wrap_syntax in let*?@ s = a -? b in return s let ( +! ) a b = let a = to_mutez a in let b = to_mutez b in Int64.add a b |> of_mutez_exn let ( -! ) a b = let a = to_mutez a in let b = to_mutez b in Int64.sub a b |> of_mutez_exn let of_mutez = of_mutez_exn let of_z a = Z.to_int64 a |> of_mutez let of_q ~round_up Q.{num; den} = (if round_up then Z.cdiv num den else Z.div num den) |> of_z let ratio num den = Q.make (Z.of_int64 (to_mutez num)) (Z.of_int64 (to_mutez den)) let mul_q tez portion = let tez_z = to_mutez tez |> Z.of_int64 in Q.(mul portion ~$$tez_z) end (** Representation of Tez with non integer values *) module Partial_tez = struct include Q let of_tez a = Tez.to_mutez a |> of_int64 let to_tez_rem {num; den} = let tez, rem = Z.div_rem num den in (Tez.of_z tez, rem /// den) let to_tez ~round_up = Tez.of_q ~round_up let get_rem a = snd (to_tez_rem a) let pp fmt a = let tez, rem = to_tez_rem a in (* If rem = 0, we keep the (+ 0), to indicate that it's a partial tez *) Format.fprintf fmt "%a ( +%aµꜩ )" Tez.pp tez Q.pp_print rem end module Cycle = Protocol.Alpha_context.Cycle (** [Frozen_tez] represents frozen stake and frozen unstaked funds. Properties: - sum of all current partial tez is an integer - Can only add integer amounts - Can always subtract integer amount (if lower than frozen amount) - If subtracting partial amount, must be the whole frozen amount (for given contract). The remainder is then distributed equally amongst remaining accounts, to keep property 1. - All entries of current are positive, non zero. *) module Frozen_tez = struct (* The map in current maps the stakers' name with their staked value. It contains only delegators of the delegate which owns the frozen tez *) type t = { delegate : string; initial : Tez.t; self_current : Tez.t; co_current : Partial_tez.t String.Map.t; } let zero = { delegate = ""; initial = Tez.zero; self_current = Tez.zero; co_current = String.Map.empty; } let init amount account delegate = if account = delegate then { delegate; initial = amount; self_current = amount; co_current = String.Map.empty; } else { delegate; initial = amount; self_current = Tez.zero; co_current = String.Map.singleton account (Partial_tez.of_tez amount); } let union a b = assert (a.delegate = b.delegate) ; { delegate = a.delegate; initial = Tez.(a.initial +! b.initial); self_current = Tez.(a.self_current +! b.self_current); co_current = String.Map.union (fun _ x y -> Some Partial_tez.(x + y)) a.co_current b.co_current; } let get account frozen_tez = if account = frozen_tez.delegate then Partial_tez.of_tez frozen_tez.self_current else match String.Map.find account frozen_tez.co_current with | None -> Partial_tez.zero | Some p -> p let total_co_current_q co_current = String.Map.fold (fun _ x acc -> Partial_tez.(x + acc)) co_current Partial_tez.zero let total_current a = let r = total_co_current_q a.co_current in let tez, rem = Partial_tez.to_tez_rem r in assert (Q.(equal rem zero)) ; Tez.(tez +! a.self_current) let add_q_to_all_co_current quantity co_current = let s = total_co_current_q co_current in let f p_amount = let q = Q.div p_amount s in Partial_tez.add p_amount (Q.mul quantity q) in String.Map.map f co_current (* For rewards, distribute equally *) let add_tez_to_all_current tez a = let self_portion = Tez.ratio a.self_current (total_current a) in let self_quantity = Tez.mul_q tez self_portion |> Tez.of_q ~round_up:true in let co_quantity = Partial_tez.of_tez Tez.(tez -! self_quantity) in let co_current = add_q_to_all_co_current co_quantity a.co_current in {a with co_current; self_current = Tez.(a.self_current +! self_quantity)} (* For slashing, slash equally *) let sub_tez_from_all_current tez a = let self_portion = Tez.ratio a.self_current (total_current a) in let self_quantity = Tez.mul_q tez self_portion |> Tez.of_q ~round_up:false in let self_current = if Tez.(self_quantity >= a.self_current) then Tez.zero else Tez.(a.self_current -! self_quantity) in let co_quantity = Tez.(tez -! self_quantity) in let s = total_co_current_q a.co_current in if Partial_tez.(geq (of_tez co_quantity) s) then {a with self_current; co_current = String.Map.empty} else let f p_amount = let q = Q.div p_amount s in Partial_tez.sub p_amount (Tez.mul_q co_quantity q) (* > 0 *) in {a with self_current; co_current = String.Map.map f a.co_current} (* Adds frozen to account. Happens each stake in frozen deposits *) let add_current amount account a = if account = a.delegate then {a with self_current = Tez.(a.self_current +! amount)} else { a with co_current = String.Map.update account (function | None -> Some (Partial_tez.of_tez amount) | Some q -> Some Partial_tez.(add q (of_tez amount))) a.co_current; } (* Adds frozen to account. Happens each unstake to unstaked frozen deposits *) let add_init amount account a = union a (init amount account a.delegate) (* Allows amount greater than current frozen amount. Happens each unstake in frozen deposits *) let sub_current amount account a = if account = a.delegate then let amount = Tez.min amount a.self_current in ({a with self_current = Tez.(a.self_current -! amount)}, amount) else match String.Map.find account a.co_current with | None -> (a, Tez.zero) | Some frozen -> let amount_q = Partial_tez.of_tez amount in if Q.(geq amount_q frozen) then let removed, remainder = Partial_tez.to_tez_rem frozen in let co_current = String.Map.remove account a.co_current in let co_current = add_q_to_all_co_current remainder co_current in ({a with co_current}, removed) else let co_current = String.Map.add account Q.(frozen - amount_q) a.co_current in ({a with co_current}, amount) let sub_current_and_init amount account a = let a, amount = sub_current amount account a in ({a with initial = Tez.(a.initial -! amount)}, amount) let slash base_amount (pct : Protocol.Int_percentage.t) a = let pct_times_100 = (pct :> int) in let slashed_amount = Tez.mul_q base_amount Q.(pct_times_100 // 100) |> Tez.of_q ~round_up:false in let total_current = total_current a in let slashed_amount_final = Tez.min slashed_amount total_current in (sub_tez_from_all_current slashed_amount a, slashed_amount_final) end (** Representation of Unstaked frozen deposits *) module Unstaked_frozen = struct type r = { cycle : Cycle.t; (* initial total requested amount (slash ∝ initial) *) initial : Tez.t; (* current amount, slashes applied here *) current : Tez.t; (* initial requests, don't apply slash unless finalize or balance query *) requests : Tez.t String.Map.t; (* slash pct memory for requests *) slash_pct : int; } type t = r list type get_info = {cycle : Cycle.t; request : Tez.t; current : Tez.t} type get_info_list = get_info list type finalizable_info = { amount : Tez.t; slashed_requests : Tez.t String.Map.t; } let zero = [] let init_r cycle request account = { cycle; initial = request; current = request; requests = String.Map.singleton account request; slash_pct = 0; } let apply_slash_to_request slash_pct amount = let slashed_amount = Tez.mul_q amount Q.(slash_pct // 100) |> Tez.of_q ~round_up:true in Tez.(amount -! slashed_amount) let apply_slash_to_current slash_pct initial current = let slashed_amount = Tez.mul_q initial Q.(slash_pct // 100) |> Tez.of_q ~round_up:false in Tez.sub_opt current slashed_amount |> Option.value ~default:Tez.zero let remove_zeros (a : t) : t = List.filter (fun ({current; _} : r) -> Tez.(current > zero)) a let get account unstaked : get_info_list = List.filter_map (fun {cycle; requests; slash_pct; _} -> String.Map.find account requests |> Option.map (fun request -> { cycle; request; current = apply_slash_to_request slash_pct request; })) unstaked let get_total account unstaked = get account unstaked |> List.fold_left (fun acc ({current; _} : get_info) -> Tez.(acc +! current)) Tez.zero let sum_current unstaked = List.fold_left (fun acc ({current; _} : r) -> Tez.(acc +! current)) Tez.zero unstaked (* Happens each unstake operation *) let rec add_unstake cycle amount account : t -> t = function | [] -> [init_r cycle amount account] | ({cycle = c; requests; initial; current; slash_pct} as h) :: t -> let open Tez in if Cycle.equal c cycle then ( assert (Int.equal slash_pct 0) ; { cycle; initial = initial +! amount; current = current +! amount; slash_pct; requests = String.Map.update account (function | None -> Some amount | Some x -> Some Tez.(x +! amount)) requests; } :: t) else h :: add_unstake cycle amount account t (* Happens in stake from unstake *) let sub_unstake amount account : r -> r = fun {cycle; requests; initial; current; slash_pct} -> assert (slash_pct = 0) ; let open Tez in { cycle; initial = initial -! amount; current = current -! amount; slash_pct; requests = String.Map.update account (function | None -> assert (Tez.(amount = zero)) ; None | Some x -> if Tez.(x = amount) then None else Some Tez.(x -! amount)) requests; } (* Makes given cycle finalizable (and unslashable) *) let rec pop_cycle cycle : t -> finalizable_info * t = function | [] -> ({amount = Tez.zero; slashed_requests = String.Map.empty}, []) | ({cycle = c; requests; initial = _; current; slash_pct} as h) :: t -> if Cycle.(c = cycle) then let amount = current in let slashed_requests = String.Map.map (apply_slash_to_request slash_pct) requests in ({amount; slashed_requests}, t) else if Cycle.(c < cycle) then Stdlib.failwith "Unstaked_frozen: found unfinalized cycle before given [cycle]. \ Make sure to call [apply_unslashable] every cycle" else let info, rest = pop_cycle cycle t in (info, h :: rest) let slash ~preserved_cycles slashed_cycle pct_times_100 a = remove_zeros a |> List.map (fun ({cycle; requests = _; initial; current; slash_pct = old_slash_pct} as r) -> if Cycle.( cycle > slashed_cycle || add cycle preserved_cycles < slashed_cycle) then (r, Tez.zero) else let new_current = apply_slash_to_current pct_times_100 initial current in let slashed = Tez.(current -! new_current) in let slash_pct = min 100 (pct_times_100 + old_slash_pct) in ({r with slash_pct; current = new_current}, slashed)) |> List.split end (** Representation of unstaked finalizable tez *) module Unstaked_finalizable = struct (* Slashing might put inaccessible tez in this container: they are represented in the remainder. They still count towards the total supply, but are currently owned by noone. At most one mutez per unstaking account per slashed cycle *) type t = {map : Tez.t String.Map.t; remainder : Tez.t} let zero = {map = String.Map.empty; remainder = Tez.zero} (* Called when unstaked frozen for some cycle becomes finalizable *) let add_from_poped_ufd ({amount; slashed_requests} : Unstaked_frozen.finalizable_info) {map; remainder} = let total_requested = String.Map.fold (fun _ x acc -> Tez.(x +! acc)) slashed_requests Tez.zero in let remainder = Tez.(remainder +! amount -! total_requested) in let map = String.Map.union (fun _ a b -> Some Tez.(a +! b)) map slashed_requests in {map; remainder} let total {map; remainder} = String.Map.fold (fun _ x acc -> Tez.(x +! acc)) map remainder let get account {map; _} = match String.Map.find account map with None -> Tez.zero | Some x -> x end (** Abstraction of the staking parameters for tests *) type staking_parameters = { limit_of_staking_over_baking : Q.t; edge_of_baking_over_staking : Q.t; } module CycleMap = Map.Make (Cycle) (** Abstract information of accounts *) type account_state = { pkh : Signature.Public_key_hash.t; contract : Protocol.Alpha_context.Contract.t; delegate : string option; parameters : staking_parameters; liquid : Tez.t; bonds : Tez.t; (* The three following fields contain maps from the account's stakers to, respectively, their frozen stake, their unstaked frozen balance, and their unstaked finalizable funds. Additionally, [unstaked_frozen] indexes the maps with the cycle at which the unstake operation occurred. *) frozen_deposits : Frozen_tez.t; unstaked_frozen : Unstaked_frozen.t; unstaked_finalizable : Unstaked_finalizable.t; staking_delegator_numerator : Z.t; staking_delegate_denominator : Z.t; frozen_rights : Tez.t CycleMap.t; slashed_cycles : Cycle.t list; } let init_account ?delegate ~pkh ~contract ~parameters ?(liquid = Tez.zero) ?(bonds = Tez.zero) ?(frozen_deposits = Frozen_tez.zero) ?(unstaked_frozen = Unstaked_frozen.zero) ?(unstaked_finalizable = Unstaked_finalizable.zero) ?(staking_delegator_numerator = Z.zero) ?(staking_delegate_denominator = Z.zero) ?(frozen_rights = CycleMap.empty) ?(slashed_cycles = []) () = { pkh; contract; delegate; parameters; liquid; bonds; frozen_deposits; unstaked_frozen; unstaked_finalizable; staking_delegator_numerator; staking_delegate_denominator; frozen_rights; slashed_cycles; } type account_map = account_state String.Map.t (** Balance returned by RPCs. Partial tez are rounded down *) type balance = { liquid_b : Tez.t; bonds_b : Tez.t; staked_b : Partial_tez.t; unstaked_frozen_b : Tez.t; unstaked_finalizable_b : Tez.t; staking_delegator_numerator_b : Z.t; staking_delegate_denominator_b : Z.t; } let balance_zero = { liquid_b = Tez.zero; bonds_b = Tez.zero; staked_b = Partial_tez.zero; unstaked_frozen_b = Tez.zero; unstaked_finalizable_b = Tez.zero; staking_delegator_numerator_b = Z.zero; staking_delegate_denominator_b = Z.zero; } let balance_of_account account_name (account_map : account_map) = match String.Map.find account_name account_map with | None -> raise Not_found | Some { pkh = _; contract = _; delegate; parameters = _; liquid; bonds; frozen_deposits = _; unstaked_frozen = _; unstaked_finalizable = _; staking_delegator_numerator; staking_delegate_denominator; frozen_rights = _; slashed_cycles = _; } -> let balance = { balance_zero with liquid_b = liquid; bonds_b = bonds; staking_delegator_numerator_b = staking_delegator_numerator; staking_delegate_denominator_b = staking_delegate_denominator; } in let balance = match delegate with | None -> balance | Some d -> ( match String.Map.find d account_map with | None -> raise Not_found | Some delegate_account -> { balance with staked_b = Frozen_tez.get account_name delegate_account.frozen_deposits; }) in (* Because an account can still have frozen or finalizable funds from a delegate that is not its own, we iterate over all of them *) let unstaked_frozen_b, unstaked_finalizable_b = String.Map.fold (fun _delegate_name delegate (frozen, finalzbl) -> let frozen = Tez.( frozen +! Unstaked_frozen.get_total account_name delegate.unstaked_frozen) in let finalzbl = Tez.( finalzbl +! Unstaked_finalizable.get account_name delegate.unstaked_finalizable) in (frozen, finalzbl)) account_map (Tez.zero, Tez.zero) in {balance with unstaked_frozen_b; unstaked_finalizable_b} let balance_pp fmt { liquid_b; bonds_b; staked_b; unstaked_frozen_b; unstaked_finalizable_b; staking_delegator_numerator_b; staking_delegate_denominator_b; } = Format.fprintf fmt "{@;\ @[<v 2> liquid : %a@;\ bonds : %a@;\ staked : %a@;\ unstaked_frozen : %a@;\ unstaked_finalizable : %a@;\ staking_delegator_numerator : %a@;\ staking_delegate_denominator : %a@;\ }@." Tez.pp liquid_b Tez.pp bonds_b Partial_tez.pp staked_b Tez.pp unstaked_frozen_b Tez.pp unstaked_finalizable_b Z.pp_print staking_delegator_numerator_b Z.pp_print staking_delegate_denominator_b let balance_update_pp fmt ( { liquid_b = a_liquid_b; bonds_b = a_bonds_b; staked_b = a_staked_b; unstaked_frozen_b = a_unstaked_frozen_b; unstaked_finalizable_b = a_unstaked_finalizable_b; staking_delegator_numerator_b = a_staking_delegator_numerator_b; staking_delegate_denominator_b = a_staking_delegate_denominator_b; }, { liquid_b = b_liquid_b; bonds_b = b_bonds_b; staked_b = b_staked_b; unstaked_frozen_b = b_unstaked_frozen_b; unstaked_finalizable_b = b_unstaked_finalizable_b; staking_delegator_numerator_b = b_staking_delegator_numerator_b; staking_delegate_denominator_b = b_staking_delegate_denominator_b; } ) = Format.fprintf fmt "{@;\ @[<v 2> liquid : %a -> %a@;\ bonds : %a -> %a@;\ staked : %a -> %a@;\ unstaked_frozen : %a -> %a@;\ unstaked_finalizable : %a -> %a@;\ staking_delegator_numerator : %a -> %a@;\ staking_delegate_denominator : %a -> %a@;\ }@." Tez.pp a_liquid_b Tez.pp b_liquid_b Tez.pp a_bonds_b Tez.pp b_bonds_b Partial_tez.pp a_staked_b Partial_tez.pp b_staked_b Tez.pp a_unstaked_frozen_b Tez.pp b_unstaked_frozen_b Tez.pp a_unstaked_finalizable_b Tez.pp b_unstaked_finalizable_b Z.pp_print a_staking_delegator_numerator_b Z.pp_print b_staking_delegator_numerator_b Z.pp_print a_staking_delegate_denominator_b Z.pp_print b_staking_delegate_denominator_b let assert_balance_equal ~loc account_name { liquid_b = a_liquid_b; bonds_b = a_bonds_b; staked_b = a_staked_b; unstaked_frozen_b = a_unstaked_frozen_b; unstaked_finalizable_b = a_unstaked_finalizable_b; staking_delegator_numerator_b = a_staking_delegator_numerator_b; staking_delegate_denominator_b = a_staking_delegate_denominator_b; } { liquid_b = b_liquid_b; bonds_b = b_bonds_b; staked_b = b_staked_b; unstaked_frozen_b = b_unstaked_frozen_b; unstaked_finalizable_b = b_unstaked_finalizable_b; staking_delegator_numerator_b = b_staking_delegator_numerator_b; staking_delegate_denominator_b = b_staking_delegate_denominator_b; } = let open Lwt_result_syntax in let f s = Format.asprintf "%s: %s" account_name s in let* () = List.fold_left (fun a b -> let*! a in let*! b in join_errors a b) return_unit [ Assert.equal ~loc Tez.equal (f "Liquid balances do not match") Tez.pp a_liquid_b b_liquid_b; Assert.equal ~loc Tez.equal (f "Bonds balances do not match") Tez.pp a_bonds_b b_bonds_b; Assert.equal ~loc Tez.equal (f "Staked balances do not match") Tez.pp (Partial_tez.to_tez ~round_up:false a_staked_b) (Partial_tez.to_tez ~round_up:false b_staked_b); Assert.equal ~loc Tez.equal (f "Unstaked frozen balances do not match") Tez.pp a_unstaked_frozen_b b_unstaked_frozen_b; Assert.equal ~loc Tez.equal (f "Unstaked finalizable balances do not match") Tez.pp a_unstaked_finalizable_b b_unstaked_finalizable_b; Assert.equal ~loc Z.equal (f "Staking delegator numerators do not match") Z.pp_print a_staking_delegator_numerator_b b_staking_delegator_numerator_b; Assert.equal ~loc Z.equal (f "Staking delegate denominators do not match") Z.pp_print a_staking_delegate_denominator_b b_staking_delegate_denominator_b; ] in return_unit let update_account ~f account_name account_map = String.Map.update account_name (function None -> raise Not_found | Some x -> Some (f x)) account_map let add_liquid_rewards amount account_name account_map = let f account = let liquid = Tez.(account.liquid +! amount) in {account with liquid} in update_account ~f account_name account_map let add_frozen_rewards amount account_name account_map = let f account = let frozen_deposits = Frozen_tez.add_tez_to_all_current amount account.frozen_deposits in {account with frozen_deposits} in update_account ~f account_name account_map let apply_burn amount src_name account_map = let f src = {src with liquid = Tez.(src.liquid -! amount)} in update_account ~f src_name account_map let apply_transfer amount src_name dst_name account_map = match (String.Map.find src_name account_map, String.Map.find dst_name account_map) with | Some src, Some _ -> if Tez.(src.liquid < amount) then (* Invalid amount: operation will fail *) account_map else let f_src src = let liquid = Tez.(src.liquid -! amount) in {src with liquid} in let f_dst dst = let liquid = Tez.(dst.liquid +! amount) in {dst with liquid} in let account_map = update_account ~f:f_src src_name account_map in update_account ~f:f_dst dst_name account_map | _ -> raise Not_found let stake_from_unstake amount current_cycle preserved_cycles delegate_name account_map = match String.Map.find delegate_name account_map with | None -> raise Not_found | Some ({unstaked_frozen; frozen_deposits; slashed_cycles; _} as account) -> let oldest_slashable_cycle = Cycle.(sub current_cycle (preserved_cycles + 1)) |> Option.value ~default:Cycle.root in if List.exists (fun x -> Cycle.(x >= oldest_slashable_cycle)) slashed_cycles then (account_map, amount) else let unstaked_frozen = List.sort (fun (Unstaked_frozen.{cycle = cycle1; _} : Unstaked_frozen.r) {cycle = cycle2; _} -> Cycle.compare cycle2 cycle1) unstaked_frozen in let rec aux acc_unstakes rem_amount rem_unstakes = match rem_unstakes with | [] -> (acc_unstakes, rem_amount) | (Unstaked_frozen.{initial; _} as h) :: t -> if Tez.(rem_amount = zero) then (acc_unstakes @ rem_unstakes, Tez.zero) else if Tez.(rem_amount >= initial) then let h = Unstaked_frozen.sub_unstake initial delegate_name h in let rem_amount = Tez.(rem_amount -! initial) in aux (acc_unstakes @ [h]) rem_amount t else let h = Unstaked_frozen.sub_unstake rem_amount delegate_name h in (acc_unstakes @ [h] @ t, Tez.zero) in let unstaked_frozen, rem_amount = aux [] amount unstaked_frozen in let frozen_deposits = Frozen_tez.add_current Tez.(amount -! rem_amount) delegate_name frozen_deposits in let account = {account with unstaked_frozen; frozen_deposits} in let account_map = update_account ~f:(fun _ -> account) delegate_name account_map in (account_map, rem_amount) let apply_stake amount current_cycle preserved_cycles staker_name account_map = match String.Map.find staker_name account_map with | None -> raise Not_found | Some staker -> ( match staker.delegate with | None -> (* Invalid operation: no delegate *) account_map | Some delegate_name -> let old_account_map = account_map in let account_map, amount = if delegate_name = staker_name then stake_from_unstake amount current_cycle preserved_cycles staker_name account_map else (account_map, amount) in if Tez.(staker.liquid < amount) then (* Invalid amount: operation will fail *) old_account_map else let f_staker staker = let liquid = Tez.(staker.liquid -! amount) in {staker with liquid} in let f_delegate delegate = let frozen_deposits = Frozen_tez.add_current amount staker_name delegate.frozen_deposits in {delegate with frozen_deposits} in let account_map = update_account ~f:f_staker staker_name account_map in update_account ~f:f_delegate delegate_name account_map) let apply_unstake cycle amount staker_name account_map = match String.Map.find staker_name account_map with | None -> raise Not_found | Some staker -> ( match staker.delegate with | None -> (* Invalid operation: no delegate *) account_map | Some delegate_name -> ( match String.Map.find delegate_name account_map with | None -> raise Not_found | Some delegate -> let frozen_deposits, amount_unstaked = Frozen_tez.sub_current amount staker_name delegate.frozen_deposits in let delegate = {delegate with frozen_deposits} in let account_map = String.Map.add delegate_name delegate account_map in let f delegate = let unstaked_frozen = Unstaked_frozen.add_unstake cycle amount_unstaked staker_name delegate.unstaked_frozen in {delegate with unstaked_frozen} in update_account ~f delegate_name account_map)) let apply_unslashable_f cycle delegate = let amount_unslashable, unstaked_frozen = Unstaked_frozen.pop_cycle cycle delegate.unstaked_frozen in let unstaked_finalizable = Unstaked_finalizable.add_from_poped_ufd amount_unslashable delegate.unstaked_finalizable in {delegate with unstaked_frozen; unstaked_finalizable} (* Updates unstaked unslashable values for given account *) let apply_unslashable cycle account_name account_map = update_account ~f:(apply_unslashable_f cycle) account_name account_map (* Updates unstaked unslashable values in all accounts *) let apply_unslashable_for_all cycle account_map = String.Map.map (apply_unslashable_f cycle) account_map let apply_finalize staker_name account_map = match String.Map.find staker_name account_map with | None -> raise Not_found | Some _staker -> (* Because an account can still have finalizable funds from a delegate that is not its own, we iterate over all of them *) String.Map.fold (fun delegate_name delegate account_map_acc -> match String.Map.find staker_name delegate.unstaked_finalizable.map with | None -> account_map_acc | Some amount -> let f_staker staker = let liquid = Tez.(staker.liquid +! amount) in {staker with liquid} in let f_delegate delegate = let map = String.Map.remove staker_name delegate.unstaked_finalizable.map in { delegate with unstaked_finalizable = {delegate.unstaked_finalizable with map}; } in let account_map_acc = update_account ~f:f_staker staker_name account_map_acc in update_account ~f:f_delegate delegate_name account_map_acc) account_map account_map let balance_and_total_balance_of_account account_name account_map = let ({ liquid_b; bonds_b; staked_b; unstaked_frozen_b; unstaked_finalizable_b; staking_delegator_numerator_b = _; staking_delegate_denominator_b = _; } as balance) = balance_of_account account_name account_map in ( balance, Tez.( liquid_b +! bonds_b +! Partial_tez.to_tez ~round_up:false staked_b +! unstaked_frozen_b +! unstaked_finalizable_b) ) let apply_slashing ( culprit, Protocol.Denunciations_repr. {rewarded; misbehaviour; misbehaviour_cycle; operation_hash = _} ) current_cycle constants account_map = let find_account_name_from_pkh_exn pkh account_map = match Option.map fst String.Map.( choose @@ filter (fun _ account -> Signature.Public_key_hash.equal pkh account.pkh) account_map) with | None -> assert false | Some x -> x in let culprit_name = find_account_name_from_pkh_exn culprit account_map in let rewarded_name = find_account_name_from_pkh_exn rewarded account_map in let slashed_cycle = match misbehaviour_cycle with | Current -> current_cycle | Previous -> Cycle.pred current_cycle |> Option.value_f ~default:(fun _ -> assert false) in let slashed_pct = match misbehaviour with | Double_baking -> constants .Protocol.Alpha_context.Constants.Parametric .percentage_of_frozen_deposits_slashed_per_double_baking | Double_attesting -> constants.percentage_of_frozen_deposits_slashed_per_double_attestation in let get_total_supply acc_map = String.Map.fold (fun _name { pkh = _; contract = _; delegate = _; parameters = _; liquid; bonds; frozen_deposits; unstaked_frozen; unstaked_finalizable; staking_delegator_numerator = _; staking_delegate_denominator = _; frozen_rights = _; slashed_cycles = _; } tot -> Tez.( liquid +! bonds +! Frozen_tez.total_current frozen_deposits +! Unstaked_frozen.sum_current unstaked_frozen +! Unstaked_finalizable.total unstaked_finalizable +! tot)) acc_map Tez.zero in let total_before_slash = get_total_supply account_map in let slash_culprit ({frozen_deposits; unstaked_frozen; frozen_rights; _} as acc) = let base_rights = CycleMap.find slashed_cycle frozen_rights |> Option.value ~default:Tez.zero in let frozen_deposits, slashed_frozen = Frozen_tez.slash base_rights slashed_pct frozen_deposits in let unstaked_frozen, slashed_unstaked = Unstaked_frozen.slash ~preserved_cycles:constants.preserved_cycles slashed_cycle (slashed_pct :> int) unstaked_frozen in ( {acc with frozen_deposits; unstaked_frozen}, slashed_frozen :: slashed_unstaked ) in let culprit_account = String.Map.find culprit_name account_map |> Option.value_f ~default:(fun () -> raise Not_found) in let slashed_culprit_account, total_slashed = slash_culprit culprit_account in let account_map = update_account ~f:(fun _ -> slashed_culprit_account) culprit_name account_map in let update_frozen_rights_with_slash ({frozen_rights; _} as acc) = let cycle_to_slash = Cycle.add current_cycle (constants.preserved_cycles + 1) in let frozen_rights = CycleMap.update cycle_to_slash (function | None -> None | Some x -> Some (Tez.mul_q x Q.((Protocol.Int_percentage.neg slashed_pct :> int) // 100) |> Tez.of_q ~round_up:false)) frozen_rights in {acc with frozen_rights} in let account_map = update_account ~f:update_frozen_rights_with_slash culprit_name account_map in let total_after_slash = get_total_supply account_map in let portion_reward = constants.adaptive_issuance.global_limit_of_staking_over_baking + 2 in (* For each container slashed, the snitch gets a reward transferred. It gets rounded down each time *) let reward_to_snitch = List.map (fun x -> Tez.mul_q x Q.(1 // portion_reward) |> Tez.of_q ~round_up:false) total_slashed |> List.fold_left Tez.( +! ) Tez.zero in let account_map = add_liquid_rewards reward_to_snitch rewarded_name account_map in let actual_total_burnt_amount = Tez.(total_before_slash -! total_after_slash -! reward_to_snitch) in (account_map, actual_total_burnt_amount) (* Given cycle is the cycle for which the rights are computed, usually current + preserved cycles *) let update_frozen_rights_cycle cycle account_map = String.Map.map (fun ({frozen_deposits; frozen_rights; _} as acc) -> let total_frozen = Frozen_tez.total_current frozen_deposits in let frozen_rights = CycleMap.add cycle total_frozen frozen_rights in {acc with frozen_rights}) account_map let get_balance_from_context ctxt contract = let open Lwt_result_syntax in let* liquid_b = Context.Contract.balance ctxt contract in let* bonds_b = Context.Contract.frozen_bonds ctxt contract in let* staked_b = Context.Contract.staked_balance ctxt contract in let staked_b = Option.value ~default:Tez.zero staked_b |> Partial_tez.of_tez in let* unstaked_frozen_b = Context.Contract.unstaked_frozen_balance ctxt contract in let unstaked_frozen_b = Option.value ~default:Tez.zero unstaked_frozen_b in let* unstaked_finalizable_b = Context.Contract.unstaked_finalizable_balance ctxt contract in let unstaked_finalizable_b = Option.value ~default:Tez.zero unstaked_finalizable_b in let* total_balance = Context.Contract.full_balance ctxt contract in let* staking_delegator_numerator_b = Context.Contract.staking_numerator ctxt contract in let*! staking_delegate_denominator_b = match (contract : Protocol.Alpha_context.Contract.t) with | Implicit pkh -> let*! result = Context.Delegate.staking_denominator ctxt pkh in Lwt.return (match result with | Ok v -> v | Error _ -> (* Not a delegate *) Z.zero) | Originated _ -> Lwt.return Z.zero in let bd = { liquid_b; bonds_b; staked_b; unstaked_frozen_b; unstaked_finalizable_b; staking_delegator_numerator_b; staking_delegate_denominator_b; } in return (bd, total_balance) let assert_balance_check ~loc ctxt account_name account_map = let open Lwt_result_syntax in match String.Map.find account_name account_map with | None -> raise Not_found | Some account -> let* balance_ctxt, total_balance_ctxt = get_balance_from_context ctxt account.contract in let balance, total_balance = balance_and_total_balance_of_account account_name account_map in let*! r1 = assert_balance_equal ~loc account_name balance_ctxt balance in let*! r2 = Assert.equal ~loc Tez.equal (Format.asprintf "%s : Total balances do not match" account_name) Tez.pp total_balance_ctxt total_balance in join_errors r1 r2 let get_launch_cycle ~loc blk = let open Lwt_result_syntax in let* launch_cycle_opt = Context.get_adaptive_issuance_launch_cycle (B blk) in Assert.get_some ~loc launch_cycle_opt (** AI operations *) let stake ctxt contract amount = Op.transaction ctxt ~entrypoint:Protocol.Alpha_context.Entrypoint.stake ~fee:Tez.zero contract contract amount let set_delegate_parameters ctxt delegate ~parameters:{limit_of_staking_over_baking; edge_of_baking_over_staking} = let entrypoint = Protocol.Alpha_context.Entrypoint.set_delegate_parameters in let limit_of_staking_over_baking_millionth = Q.mul limit_of_staking_over_baking (Q.of_int 1_000_000) |> Q.to_int in let edge_of_baking_over_staking_billionth = Q.mul edge_of_baking_over_staking (Q.of_int 1_000_000_000) |> Q.to_int in let parameters = Protocol.Alpha_context.Script.lazy_expr (Expr.from_string (Printf.sprintf "Pair %d (Pair %d Unit)" limit_of_staking_over_baking_millionth edge_of_baking_over_staking_billionth)) in Op.transaction ctxt ~entrypoint ~parameters ~fee:Tez.zero delegate delegate Tez.zero let unstake ctxt contract amount = Op.transaction ctxt ~entrypoint:Protocol.Alpha_context.Entrypoint.unstake ~fee:Tez.zero contract contract amount let finalize_unstake ctxt ?(amount = Tez.zero) contract = Op.transaction ctxt ~entrypoint:Protocol.Alpha_context.Entrypoint.finalize_unstake ~fee:Tez.zero contract contract amount let portion_of_rewards_to_liquid_for_cycle ?policy ctxt cycle pkh rewards = let open Lwt_result_syntax in let* {frozen; weighted_delegated} = Context.Delegate.stake_for_cycle ?policy ctxt cycle pkh in let portion = Tez.(ratio weighted_delegated (frozen +! weighted_delegated)) in let to_liquid = Tez.mul_q rewards portion in return (Partial_tez.to_tez ~round_up:false to_liquid)
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>