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 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 1528 1529 1530 1531 1532 1533 1534 1535 1536 1537 1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591 1592 1593 1594 1595 1596 1597 1598 1599 1600 1601 1602 1603 1604 1605 1606 1607 1608 1609 1610 1611 1612 1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633 1634 1635 1636 1637 1638 1639 1640 1641 1642 1643 1644 1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661 1662 1663 1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708 1709 1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734 1735 1736 1737 1738 1739 1740 1741 1742 1743 1744 1745 1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762 1763 1764 1765 1766 1767 1768 1769 1770 1771 1772 1773 1774 1775 1776 1777 1778 1779 1780 1781 1782 1783 1784 1785 1786 1787 1788 1789 1790 1791 1792 1793 1794 1795 1796 1797 1798 1799 1800 1801 1802 1803 1804 1805 1806 1807 1808 1809 1810 1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821 1822 1823 1824 1825 1826 1827 1828 1829 1830 1831 1832 1833 1834 1835 1836 1837 1838 1839 1840 1841 1842 1843 1844 1845 1846 1847 1848 1849 1850 1851 1852 1853 1854 1855 1856 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889 1890 1891 1892 1893 1894 1895 1896 1897 1898 1899 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 1915 1916 1917 1918 1919 1920 1921 1922 1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938 1939 1940 1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958 1959 1960 1961 1962 1963 1964 1965 1966 1967 1968 1969 1970 1971 1972 1973 1974 1975 1976 1977 1978 1979 1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996 1997 1998 1999 2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019 2020 2021 2022 2023 2024 2025 2026 2027 2028 2029 2030 2031 2032 2033 2034 2035 2036 2037 2038 2039 2040 2041 2042 2043 2044 2045 2046 2047 2048 2049 2050 2051 2052 2053 2054 2055 2056 2057 2058 2059 2060 2061 2062 2063 2064 2065 2066 2067 2068 2069 2070 2071 2072 2073 2074 2075 2076 2077 2078 2079 2080 2081 2082 2083 2084 2085 2086 2087 2088 2089 2090 2091 2092 2093 2094 2095 2096 2097 2098 2099 2100 2101 2102 2103 2104 2105 2106 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 2140 2141 2142 2143 2144 2145 2146 2147 2148 2149 2150 2151 2152 2153 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199 2200 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 2227 2228 2229 2230 2231 2232 2233 2234 2235 2236 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277 2278 2279 2280 2281 2282 2283 2284 2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302 2303 2304 2305 2306 2307 2308 2309 2310 2311 2312 2313 2314 2315 2316 2317 2318 2319 2320 2321 2322 2323 2324 2325 2326 2327 2328 2329 2330 2331 2332 2333 2334 2335 2336 2337 2338 2339 2340 2341 2342 2343 2344 2345 2346 2347 2348 2349 2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 2383 2384 2385 2386 2387 2388 2389 2390 2391 2392 2393 2394 2395 2396 2397 2398 2399 2400 2401 2402 2403 2404 2405 2406 2407 2408 2409 2410 2411 2412 2413 2414 2415 2416 2417 2418 2419 2420 2421 2422 2423 2424 2425 2426 2427 2428 2429 2430 2431 2432 2433 2434 2435 2436 2437 2438 2439 2440 2441 2442 2443 2444 2445 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 2470 2471 2472 2473 2474 2475 2476 2477 2478 2479 2480 2481 2482 2483 2484 2485 2486 2487 2488 2489 2490 2491 2492 2493 2494 2495 2496 2497 2498 2499 2500 2501 2502 2503 2504 2505 2506 2507 2508 2509 2510 2511 2512 2513 2514 2515 2516 2517 2518 2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 2559 2560 2561 2562 2563 2564 2565 2566 2567 2568 2569 2570 2571 2572 2573 2574 2575 2576 2577 2578 2579 2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606 2607 2608 2609 2610 2611 2612 2613 2614 2615 2616 2617 2618 2619 2620 2621 2622 2623 2624 2625 2626 2627 2628 2629 2630 2631 2632 2633 2634 2635 2636 2637 2638 2639 2640 2641 2642 2643 2644 2645 2646 2647 2648 2649 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676 2677 2678 2679 2680 2681 2682 2683 2684 2685 2686 2687 2688 2689 2690 2691 2692 2693 2694 2695 2696 2697 2698 2699 2700 2701 2702 2703 2704 2705 2706 2707 2708 2709 2710 2711 2712 2713 2714 2715 2716 2717 2718 2719 2720 2721 2722 2723 2724 2725 2726 2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 2748 2749 2750 2751 2752 2753 2754 2755 2756 2757 2758 2759 2760 2761 2762 2763 2764 2765 2766 2767 2768 2769 2770 2771 2772 2773 2774 2775 2776 2777 2778 2779 2780 2781 2782 2783 2784 2785 2786 2787 2788 2789 2790 2791 2792 2793 2794 2795 2796 2797 2798 2799 2800 2801 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 2817 2818 2819 2820 2821 2822 2823 2824 2825 2826 2827 2828 2829 2830 2831 2832 2833 2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864 2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892 2893 2894 2895 2896 2897 2898 2899 2900 2901 2902 2903 2904 2905 2906 2907 2908 2909 2910 2911 2912 2913 2914 2915 2916 2917 2918 2919 2920 2921 2922 2923 2924 2925 2926 2927 2928 2929 2930 2931 2932 2933 2934 2935 2936 2937 2938 2939 2940 2941 2942 2943 2944 2945 2946 2947 2948 2949 2950 2951 2952 2953 2954 2955 2956 2957 2958 2959 2960 2961 2962 2963 2964 2965 2966 2967 2968 2969 2970 2971 2972 2973 2974 2975 2976 2977 2978 2979 2980 2981 2982 2983 2984 2985 2986 2987 2988 2989 2990 2991 2992 2993 2994 2995 2996 2997 2998 2999 3000 3001 3002 3003 3004 3005 3006 3007 3008 3009 3010 3011 3012 3013 3014 3015 3016 3017 3018 3019 3020 3021 3022 3023 3024 3025 3026 3027 3028 3029 3030 3031 3032 3033 3034 3035 3036 3037 3038 3039 3040 3041 3042 3043 3044 3045 3046 3047 3048 3049 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 3090 3091 3092 3093 3094 3095 3096 3097 3098 3099 3100 3101 3102 3103 3104 3105 3106 3107 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 3123 3124 3125 3126 3127 3128 3129 3130 3131 3132 3133 3134 3135 3136 3137 3138 3139 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 3160 3161 3162 3163 3164 3165 3166 3167 3168 3169 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 3190 3191 3192 3193 3194 3195 3196 3197 3198 3199 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 3220 3221 3222 3223 3224 3225 3226 3227 3228 3229 3230 3231 3232 3233 3234 3235 3236 3237 3238 3239 3240 3241 3242 3243 3244 3245 3246 3247 3248 3249 3250 3251 3252 3253 3254 3255 3256 3257 3258 3259 3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 3280 3281 3282 3283 3284 3285 3286 3287 3288 3289 3290 3291 3292 3293 3294 3295 3296 3297 3298 3299 3300 3301 3302 3303 3304 3305 3306 3307 3308 3309 3310 3311 3312 3313 3314 3315 3316 3317 3318 3319 3320 3321 3322 3323 3324 3325 3326 3327 3328 3329 3330 3331 3332 3333 3334 3335 3336 3337 3338 3339 3340 3341 3342 3343 3344 3345 3346 3347 3348 3349 3350 3351 3352 3353 3354 3355 3356 3357 3358 3359 3360 3361 3362 3363 3364 3365 3366 3367 3368 3369 3370 3371 3372 3373 3374 3375 3376 3377 3378 3379 3380 3381 3382 3383 3384 3385 3386 3387 3388 3389 3390 3391 3392 3393 3394 3395 3396 3397 3398 3399 3400 3401 3402 3403 3404 3405 3406 3407 3408 3409 3410 3411 3412 3413 3414 3415 3416 3417 3418 3419 3420 3421 3422 3423 3424 3425 3426 3427 3428 3429 3430 3431 3432 3433 3434 3435 3436 3437 3438 3439 3440 3441 3442 3443 3444 3445 3446 3447 3448 3449 3450 3451 3452 3453 3454 3455 3456 3457 3458 3459 3460 3461 3462 3463 3464 3465 3466 3467 3468 3469 3470 3471 3472 3473 3474 3475 3476 3477 3478 3479 3480 3481 3482 3483 3484 3485 3486 3487 3488 3489 3490 3491 3492 3493 3494 3495 3496 3497 3498 3499 3500 3501 3502 3503 3504 3505 3506 3507 3508 3509 3510 3511 3512 3513 3514 3515 3516 3517 3518 3519 3520 3521 3522 3523 3524 3525 3526 3527 3528 3529 3530 3531 3532 3533 3534 3535 3536 3537 3538 3539 3540 3541 3542 3543 3544 3545 3546 3547 3548 3549 3550 3551 3552 3553 3554 3555
// This file is part of Tetcore. // Copyright (C) 2017-2021 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //! # Staking Module //! //! The Staking module is used to manage funds at stake by network maintainers. //! //! - [`staking::Config`](./trait.Config.html) //! - [`Call`](./enum.Call.html) //! - [`Module`](./struct.Module.html) //! //! ## Overview //! //! The Staking module is the means by which a set of network maintainers (known as _authorities_ in //! some contexts and _validators_ in others) are chosen based upon those who voluntarily place //! funds under deposit. Under deposit, those funds are rewarded under normal operation but are held //! at pain of _slash_ (expropriation) should the staked maintainer be found not to be discharging //! its duties properly. //! //! ### Terminology //! <!-- Original author of paragraph: @gavofyork --> //! //! - Staking: The process of locking up funds for some time, placing them at risk of slashing //! (loss) in order to become a rewarded maintainer of the network. //! - Validating: The process of running a node to actively maintain the network, either by //! producing blocks or guaranteeing finality of the chain. //! - Nominating: The process of placing staked funds behind one or more validators in order to //! share in any reward, and punishment, they take. //! - Stash account: The account holding an owner's funds used for staking. //! - Controller account: The account that controls an owner's funds for staking. //! - Era: A (whole) number of sessions, which is the period that the validator set (and each //! validator's active nominator set) is recalculated and where rewards are paid out. //! - Slash: The punishment of a staker by reducing its funds. //! //! ### Goals //! <!-- Original author of paragraph: @gavofyork --> //! //! The staking system in Tetcore NPoS is designed to make the following possible: //! //! - Stake funds that are controlled by a cold wallet. //! - Withdraw some, or deposit more, funds without interrupting the role of an entity. //! - Switch between roles (nominator, validator, idle) with minimal overhead. //! //! ### Scenarios //! //! #### Staking //! //! Almost any interaction with the Staking module requires a process of _**bonding**_ (also known //! as being a _staker_). To become *bonded*, a fund-holding account known as the _stash account_, //! which holds some or all of the funds that become frozen in place as part of the staking process, //! is paired with an active **controller** account, which issues instructions on how they shall be //! used. //! //! An account pair can become bonded using the [`bond`](./enum.Call.html#variant.bond) call. //! //! Stash accounts can change their associated controller using the //! [`set_controller`](./enum.Call.html#variant.set_controller) call. //! //! There are three possible roles that any staked account pair can be in: `Validator`, `Nominator` //! and `Idle` (defined in [`StakerStatus`](./enum.StakerStatus.html)). There are three //! corresponding instructions to change between roles, namely: //! [`validate`](./enum.Call.html#variant.validate), //! [`nominate`](./enum.Call.html#variant.nominate), and [`chill`](./enum.Call.html#variant.chill). //! //! #### Validating //! //! A **validator** takes the role of either validating blocks or ensuring their finality, //! maintaining the veracity of the network. A validator should avoid both any sort of malicious //! misbehavior and going offline. Bonded accounts that state interest in being a validator do NOT //! get immediately chosen as a validator. Instead, they are declared as a _candidate_ and they //! _might_ get elected at the _next era_ as a validator. The result of the election is determined //! by nominators and their votes. //! //! An account can become a validator candidate via the //! [`validate`](./enum.Call.html#variant.validate) call. //! //! #### Nomination //! //! A **nominator** does not take any _direct_ role in maintaining the network, instead, it votes on //! a set of validators to be elected. Once interest in nomination is stated by an account, it //! takes effect at the next election round. The funds in the nominator's stash account indicate the //! _weight_ of its vote. Both the rewards and any punishment that a validator earns are shared //! between the validator and its nominators. This rule incentivizes the nominators to NOT vote for //! the misbehaving/offline validators as much as possible, simply because the nominators will also //! lose funds if they vote poorly. //! //! An account can become a nominator via the [`nominate`](enum.Call.html#variant.nominate) call. //! //! #### Rewards and Slash //! //! The **reward and slashing** procedure is the core of the Staking module, attempting to _embrace //! valid behavior_ while _punishing any misbehavior or lack of availability_. //! //! Rewards must be claimed for each era before it gets too old by `$HISTORY_DEPTH` using the //! `payout_stakers` call. Any account can call `payout_stakers`, which pays the reward to the //! validator as well as its nominators. Only the [`Config::MaxNominatorRewardedPerValidator`] //! biggest stakers can claim their reward. This is to limit the i/o cost to mutate storage for each //! nominator's account. //! //! Slashing can occur at any point in time, once misbehavior is reported. Once slashing is //! determined, a value is deducted from the balance of the validator and all the nominators who //! voted for this validator (values are deducted from the _stash_ account of the slashed entity). //! //! Slashing logic is further described in the documentation of the `slashing` module. //! //! Similar to slashing, rewards are also shared among a validator and its associated nominators. //! Yet, the reward funds are not always transferred to the stash account and can be configured. See //! [Reward Calculation](#reward-calculation) for more details. //! //! #### Chilling //! //! Finally, any of the roles above can choose to step back temporarily and just chill for a while. //! This means that if they are a nominator, they will not be considered as voters anymore and if //! they are validators, they will no longer be a candidate for the next election. //! //! An account can step back via the [`chill`](enum.Call.html#variant.chill) call. //! //! ### Session managing //! //! The module implement the trait `SessionManager`. Which is the only API to query new validator //! set and allowing these validator set to be rewarded once their era is ended. //! //! ## Interface //! //! ### Dispatchable Functions //! //! The dispatchable functions of the Staking module enable the steps needed for entities to accept //! and change their role, alongside some helper functions to get/set the metadata of the module. //! //! ### Public Functions //! //! The Staking module contains many public storage items and (im)mutable functions. //! //! ## Usage //! //! ### Example: Rewarding a validator by id. //! //! ``` //! use fabric_support::{decl_module, dispatch}; //! use fabric_system::ensure_signed; //! use noble_staking::{self as staking}; //! //! pub trait Config: staking::Config {} //! //! decl_module! { //! pub struct Module<T: Config> for enum Call where origin: T::Origin { //! /// Reward a validator. //! #[weight = 0] //! pub fn reward_myself(origin) -> dispatch::DispatchResult { //! let reported = ensure_signed(origin)?; //! <staking::Module<T>>::reward_by_ids(vec![(reported, 10)]); //! Ok(()) //! } //! } //! } //! # fn main() { } //! ``` //! //! ## Implementation Details //! //! ### Era payout //! //! The era payout is computed using yearly inflation curve defined at //! [`T::RewardCurve`](./trait.Config.html#associatedtype.RewardCurve) as such: //! //! ```nocompile //! staker_payout = yearly_inflation(npos_token_staked / total_tokens) * total_tokens / era_per_year //! ``` //! This payout is used to reward stakers as defined in next section //! //! ```nocompile //! remaining_payout = max_yearly_inflation * total_tokens / era_per_year - staker_payout //! ``` //! The remaining reward is send to the configurable end-point //! [`T::RewardRemainder`](./trait.Config.html#associatedtype.RewardRemainder). //! //! ### Reward Calculation //! //! Validators and nominators are rewarded at the end of each era. The total reward of an era is //! calculated using the era duration and the staking rate (the total amount of tokens staked by //! nominators and validators, divided by the total token supply). It aims to incentivize toward a //! defined staking rate. The full specification can be found //! [here](https://research.web3.foundation/en/latest/polkadot/Token%20Economics.html#inflation-model). //! //! Total reward is split among validators and their nominators depending on the number of points //! they received during the era. Points are added to a validator using //! [`reward_by_ids`](./enum.Call.html#variant.reward_by_ids) or //! [`reward_by_indices`](./enum.Call.html#variant.reward_by_indices). //! //! [`Module`](./struct.Module.html) implements //! [`noble_authorship::EventHandler`](../noble_authorship/trait.EventHandler.html) to add reward //! points to block producer and block producer of referenced uncles. //! //! The validator and its nominator split their reward as following: //! //! The validator can declare an amount, named //! [`commission`](./struct.ValidatorPrefs.html#structfield.commission), that does not get shared //! with the nominators at each reward payout through its //! [`ValidatorPrefs`](./struct.ValidatorPrefs.html). This value gets deducted from the total reward //! that is paid to the validator and its nominators. The remaining portion is split among the //! validator and all of the nominators that nominated the validator, proportional to the value //! staked behind this validator (_i.e._ dividing the //! [`own`](./struct.Exposure.html#structfield.own) or //! [`others`](./struct.Exposure.html#structfield.others) by //! [`total`](./struct.Exposure.html#structfield.total) in [`Exposure`](./struct.Exposure.html)). //! //! All entities who receive a reward have the option to choose their reward destination through the //! [`Payee`](./struct.Payee.html) storage item (see //! [`set_payee`](enum.Call.html#variant.set_payee)), to be one of the following: //! //! - Controller account, (obviously) not increasing the staked value. //! - Stash account, not increasing the staked value. //! - Stash account, also increasing the staked value. //! //! ### Additional Fund Management Operations //! //! Any funds already placed into stash can be the target of the following operations: //! //! The controller account can free a portion (or all) of the funds using the //! [`unbond`](enum.Call.html#variant.unbond) call. Note that the funds are not immediately //! accessible. Instead, a duration denoted by //! [`BondingDuration`](./trait.Config.html#associatedtype.BondingDuration) (in number of eras) must //! pass until the funds can actually be removed. Once the `BondingDuration` is over, the //! [`withdraw_unbonded`](./enum.Call.html#variant.withdraw_unbonded) call can be used to actually //! withdraw the funds. //! //! Note that there is a limitation to the number of fund-chunks that can be scheduled to be //! unlocked in the future via [`unbond`](enum.Call.html#variant.unbond). In case this maximum //! (`MAX_UNLOCKING_CHUNKS`) is reached, the bonded account _must_ first wait until a successful //! call to `withdraw_unbonded` to remove some of the chunks. //! //! ### Election Algorithm //! //! The current election algorithm is implemented based on Phragmén. The reference implementation //! can be found [here](https://github.com/w3f/consensus/tree/master/NPoS). //! //! The election algorithm, aside from electing the validators with the most stake value and votes, //! tries to divide the nominator votes among candidates in an equal manner. To further assure this, //! an optional post-processing can be applied that iteratively normalizes the nominator staked //! values until the total difference among votes of a particular nominator are less than a //! threshold. //! //! ## GenesisConfig //! //! The Staking module depends on the [`GenesisConfig`](./struct.GenesisConfig.html). The //! `GenesisConfig` is optional and allow to set some initial stakers. //! //! ## Related Modules //! //! - [Balances](../noble_balances/index.html): Used to manage values at stake. //! - [Session](../noble_session/index.html): Used to manage sessions. Also, a list of new //! validators is stored in the Session module's `Validators` at the end of each era. #![recursion_limit = "128"] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] mod mock; #[cfg(test)] mod tests; #[cfg(any(feature = "runtime-benchmarks", test))] pub mod testing_utils; #[cfg(any(feature = "runtime-benchmarks", test))] pub mod benchmarking; pub mod slashing; pub mod offchain_election; pub mod inflation; pub mod weights; use tetcore_std::{ result, prelude::*, collections::btree_map::BTreeMap, convert::{TryInto, From}, mem::size_of, }; use codec::{HasCompact, Encode, Decode}; use fabric_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, weights::{Weight, constants::{WEIGHT_PER_MICROS, WEIGHT_PER_NANOS}}, storage::IterableStorageMap, dispatch::{ DispatchResult, DispatchResultWithPostInfo, DispatchErrorWithPostInfo, WithPostDispatchInfo, }, traits::{ Currency, LockIdentifier, LockableCurrency, WithdrawReasons, OnUnbalanced, Imbalance, Get, UnixTime, EstimateNextNewSession, EnsureOrigin, CurrencyToVote, IsSubType, } }; use noble_session::historical; use tp_runtime::{ Percent, Perbill, PerU16, RuntimeDebug, DispatchError, curve::PiecewiseLinear, traits::{ Convert, Zero, StaticLookup, CheckedSub, Saturating, SaturatedConversion, AtLeast32BitUnsigned, Dispatchable, }, transaction_validity::{ TransactionValidityError, TransactionValidity, ValidTransaction, InvalidTransaction, TransactionSource, TransactionPriority, }, }; use tp_staking::{ SessionIndex, offence::{OnOffenceHandler, OffenceDetails, Offence, ReportOffence, OffenceError}, }; #[cfg(feature = "std")] use tp_runtime::{Serialize, Deserialize}; use fabric_system::{ self as system, ensure_signed, ensure_root, ensure_none, offchain::SendTransactionTypes, }; use tp_npos_elections::{ ExtendedBalance, Assignment, ElectionScore, ElectionResult as PrimitiveElectionResult, to_support_map, EvaluateSupport, seq_phragmen, generate_solution_type, is_score_better, SupportMap, VoteWeight, CompactSolution, PerThing128, }; pub use weights::WeightInfo; const STAKING_ID: LockIdentifier = *b"staking "; pub const MAX_UNLOCKING_CHUNKS: usize = 32; pub const MAX_NOMINATIONS: usize = <CompactAssignments as CompactSolution>::LIMIT; pub(crate) const LOG_TARGET: &'static str = "staking"; // syntactic sugar for logging. #[macro_export] macro_rules! log { ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { fabric_support::debug::$level!( target: crate::LOG_TARGET, $patter $(, $values)* ) }; } /// Data type used to index nominators in the compact type pub type NominatorIndex = u32; /// Data type used to index validators in the compact type. pub type ValidatorIndex = u16; // Ensure the size of both ValidatorIndex and NominatorIndex. They both need to be well below usize. static_assertions::const_assert!(size_of::<ValidatorIndex>() <= size_of::<usize>()); static_assertions::const_assert!(size_of::<NominatorIndex>() <= size_of::<usize>()); static_assertions::const_assert!(size_of::<ValidatorIndex>() <= size_of::<u32>()); static_assertions::const_assert!(size_of::<NominatorIndex>() <= size_of::<u32>()); /// Maximum number of stakers that can be stored in a snapshot. pub(crate) const MAX_VALIDATORS: usize = ValidatorIndex::max_value() as usize; pub(crate) const MAX_NOMINATORS: usize = NominatorIndex::max_value() as usize; /// Counter for the number of eras that have passed. pub type EraIndex = u32; /// Counter for the number of "reward" points earned by a given validator. pub type RewardPoint = u32; // Note: Maximum nomination limit is set here -- 16. generate_solution_type!( #[compact] pub struct CompactAssignments::<NominatorIndex, ValidatorIndex, OffchainAccuracy>(16) ); /// Accuracy used for on-chain election. pub type ChainAccuracy = Perbill; /// Accuracy used for off-chain election. This better be small. pub type OffchainAccuracy = PerU16; /// The balance type of this module. pub type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as fabric_system::Config>::AccountId>>::Balance; type PositiveImbalanceOf<T> = <<T as Config>::Currency as Currency<<T as fabric_system::Config>::AccountId>>::PositiveImbalance; type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency<<T as fabric_system::Config>::AccountId>>::NegativeImbalance; /// Information regarding the active era (era in used in session). #[derive(Encode, Decode, RuntimeDebug)] pub struct ActiveEraInfo { /// Index of era. pub index: EraIndex, /// Moment of start expressed as millisecond from `$UNIX_EPOCH`. /// /// Start can be none if start hasn't been set for the era yet, /// Start is set on the first on_finalize of the era to guarantee usage of `Time`. start: Option<u64>, } /// Reward points of an era. Used to split era total payout between validators. /// /// This points will be used to reward validators and their respective nominators. #[derive(PartialEq, Encode, Decode, Default, RuntimeDebug)] pub struct EraRewardPoints<AccountId: Ord> { /// Total number of points. Equals the sum of reward points for each validator. total: RewardPoint, /// The reward points earned by a given validator. individual: BTreeMap<AccountId, RewardPoint>, } /// Indicates the initial status of the staker. #[derive(RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum StakerStatus<AccountId> { /// Chilling. Idle, /// Declared desire in validating or already participating in it. Validator, /// Nominating for a group of other stakers. Nominator(Vec<AccountId>), } /// A destination account for payment. #[derive(PartialEq, Eq, Copy, Clone, Encode, Decode, RuntimeDebug)] pub enum RewardDestination<AccountId> { /// Pay into the stash account, increasing the amount at stake accordingly. Staked, /// Pay into the stash account, not increasing the amount at stake. Stash, /// Pay into the controller account. Controller, /// Pay into a specified account. Account(AccountId), } impl<AccountId> Default for RewardDestination<AccountId> { fn default() -> Self { RewardDestination::Staked } } /// Preference of what happens regarding validation. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct ValidatorPrefs { /// Reward that validator takes up-front; only the rest is split between themselves and /// nominators. #[codec(compact)] pub commission: Perbill, /// Whether or not this validator is accepting more nominations. If `true`, then no nominator /// who is not already nominating this validator may nominate them. By default, validators /// are accepting nominations. pub blocked: bool, } impl Default for ValidatorPrefs { fn default() -> Self { ValidatorPrefs { commission: Default::default(), blocked: false, } } } /// Just a Balance/BlockNumber tuple to encode when a chunk of funds will be unlocked. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct UnlockChunk<Balance: HasCompact> { /// Amount of funds to be unlocked. #[codec(compact)] value: Balance, /// Era number at which point it'll be unlocked. #[codec(compact)] era: EraIndex, } /// The ledger of a (bonded) stash. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct StakingLedger<AccountId, Balance: HasCompact> { /// The stash account whose balance is actually locked and at stake. pub stash: AccountId, /// The total amount of the stash's balance that we are currently accounting for. /// It's just `active` plus all the `unlocking` balances. #[codec(compact)] pub total: Balance, /// The total amount of the stash's balance that will be at stake in any forthcoming /// rounds. #[codec(compact)] pub active: Balance, /// Any balance that is becoming free, which may eventually be transferred out /// of the stash (assuming it doesn't get slashed first). pub unlocking: Vec<UnlockChunk<Balance>>, /// List of eras for which the stakers behind a validator have claimed rewards. Only updated /// for validators. pub claimed_rewards: Vec<EraIndex>, } impl< AccountId, Balance: HasCompact + Copy + Saturating + AtLeast32BitUnsigned, > StakingLedger<AccountId, Balance> { /// Remove entries from `unlocking` that are sufficiently old and reduce the /// total by the sum of their balances. fn consolidate_unlocked(self, current_era: EraIndex) -> Self { let mut total = self.total; let unlocking = self.unlocking.into_iter() .filter(|chunk| if chunk.era > current_era { true } else { total = total.saturating_sub(chunk.value); false }) .collect(); Self { stash: self.stash, total, active: self.active, unlocking, claimed_rewards: self.claimed_rewards } } /// Re-bond funds that were scheduled for unlocking. fn rebond(mut self, value: Balance) -> Self { let mut unlocking_balance: Balance = Zero::zero(); while let Some(last) = self.unlocking.last_mut() { if unlocking_balance + last.value <= value { unlocking_balance += last.value; self.active += last.value; self.unlocking.pop(); } else { let diff = value - unlocking_balance; unlocking_balance += diff; self.active += diff; last.value -= diff; } if unlocking_balance >= value { break } } self } } impl<AccountId, Balance> StakingLedger<AccountId, Balance> where Balance: AtLeast32BitUnsigned + Saturating + Copy, { /// Slash the validator for a given amount of balance. This can grow the value /// of the slash in the case that the validator has less than `minimum_balance` /// active funds. Returns the amount of funds actually slashed. /// /// Slashes from `active` funds first, and then `unlocking`, starting with the /// chunks that are closest to unlocking. fn slash( &mut self, mut value: Balance, minimum_balance: Balance, ) -> Balance { let pre_total = self.total; let total = &mut self.total; let active = &mut self.active; let slash_out_of = | total_remaining: &mut Balance, target: &mut Balance, value: &mut Balance, | { let mut slash_from_target = (*value).min(*target); if !slash_from_target.is_zero() { *target -= slash_from_target; // don't leave a dust balance in the staking system. if *target <= minimum_balance { slash_from_target += *target; *value += tetcore_std::mem::replace(target, Zero::zero()); } *total_remaining = total_remaining.saturating_sub(slash_from_target); *value -= slash_from_target; } }; slash_out_of(total, active, &mut value); let i = self.unlocking.iter_mut() .map(|chunk| { slash_out_of(total, &mut chunk.value, &mut value); chunk.value }) .take_while(|value| value.is_zero()) // take all fully-consumed chunks out. .count(); // kill all drained chunks. let _ = self.unlocking.drain(..i); pre_total.saturating_sub(*total) } } /// A record of the nominations made by a specific account. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct Nominations<AccountId> { /// The targets of nomination. pub targets: Vec<AccountId>, /// The era the nominations were submitted. /// /// Except for initial nominations which are considered submitted at era 0. pub submitted_in: EraIndex, /// Whether the nominations have been suppressed. This can happen due to slashing of the /// validators, or other events that might invalidate the nomination. /// /// NOTE: this for future proofing and is thus far not used. pub suppressed: bool, } /// The amount of exposure (to slashing) than an individual nominator has. #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, RuntimeDebug)] pub struct IndividualExposure<AccountId, Balance: HasCompact> { /// The stash account of the nominator in question. pub who: AccountId, /// Amount of funds exposed. #[codec(compact)] pub value: Balance, } /// A snapshot of the stake backing a single validator in the system. #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Encode, Decode, Default, RuntimeDebug)] pub struct Exposure<AccountId, Balance: HasCompact> { /// The total balance backing this validator. #[codec(compact)] pub total: Balance, /// The validator's own stash that is exposed. #[codec(compact)] pub own: Balance, /// The portions of nominators stashes that are exposed. pub others: Vec<IndividualExposure<AccountId, Balance>>, } /// A pending slash record. The value of the slash has been computed but not applied yet, /// rather deferred for several eras. #[derive(Encode, Decode, Default, RuntimeDebug)] pub struct UnappliedSlash<AccountId, Balance: HasCompact> { /// The stash ID of the offending validator. validator: AccountId, /// The validator's own slash. own: Balance, /// All other slashed stakers and amounts. others: Vec<(AccountId, Balance)>, /// Reporters of the offence; bounty payout recipients. reporters: Vec<AccountId>, /// The amount of payout. payout: Balance, } /// Indicate how an election round was computed. #[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug)] pub enum ElectionCompute { /// Result was forcefully computed on chain at the end of the session. OnChain, /// Result was submitted and accepted to the chain via a signed transaction. Signed, /// Result was submitted and accepted to the chain via an unsigned transaction (by an /// authority). Unsigned, } /// The result of an election round. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub struct ElectionResult<AccountId, Balance: HasCompact> { /// Flat list of validators who have been elected. elected_stashes: Vec<AccountId>, /// Flat list of new exposures, to be updated in the [`Exposure`] storage. exposures: Vec<(AccountId, Exposure<AccountId, Balance>)>, /// Type of the result. This is kept on chain only to track and report the best score's /// submission type. An optimisation could remove this. compute: ElectionCompute, } /// The status of the upcoming (offchain) election. #[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)] pub enum ElectionStatus<BlockNumber> { /// Nothing has and will happen for now. submission window is not open. Closed, /// The submission window has been open since the contained block number. Open(BlockNumber), } /// Some indications about the size of the election. This must be submitted with the solution. /// /// Note that these values must reflect the __total__ number, not only those that are present in the /// solution. In short, these should be the same size as the size of the values dumped in /// `SnapshotValidators` and `SnapshotNominators`. #[derive(PartialEq, Eq, Clone, Copy, Encode, Decode, RuntimeDebug, Default)] pub struct ElectionSize { /// Number of validators in the snapshot of the current election round. #[codec(compact)] pub validators: ValidatorIndex, /// Number of nominators in the snapshot of the current election round. #[codec(compact)] pub nominators: NominatorIndex, } impl<BlockNumber: PartialEq> ElectionStatus<BlockNumber> { pub fn is_open_at(&self, n: BlockNumber) -> bool { *self == Self::Open(n) } pub fn is_closed(&self) -> bool { match self { Self::Closed => true, _ => false } } pub fn is_open(&self) -> bool { !self.is_closed() } } impl<BlockNumber> Default for ElectionStatus<BlockNumber> { fn default() -> Self { Self::Closed } } /// Means for interacting with a specialized version of the `session` trait. /// /// This is needed because `Staking` sets the `ValidatorIdOf` of the `noble_session::Config` pub trait SessionInterface<AccountId>: fabric_system::Config { /// Disable a given validator by stash ID. /// /// Returns `true` if new era should be forced at the end of this session. /// This allows preventing a situation where there is too many validators /// disabled and block production stalls. fn disable_validator(validator: &AccountId) -> Result<bool, ()>; /// Get the validators from session. fn validators() -> Vec<AccountId>; /// Prune historical session tries up to but not including the given index. fn prune_historical_up_to(up_to: SessionIndex); } impl<T: Config> SessionInterface<<T as fabric_system::Config>::AccountId> for T where T: noble_session::Config<ValidatorId = <T as fabric_system::Config>::AccountId>, T: noble_session::historical::Config< FullIdentification = Exposure<<T as fabric_system::Config>::AccountId, BalanceOf<T>>, FullIdentificationOf = ExposureOf<T>, >, T::SessionHandler: noble_session::SessionHandler<<T as fabric_system::Config>::AccountId>, T::SessionManager: noble_session::SessionManager<<T as fabric_system::Config>::AccountId>, T::ValidatorIdOf: Convert<<T as fabric_system::Config>::AccountId, Option<<T as fabric_system::Config>::AccountId>>, { fn disable_validator(validator: &<T as fabric_system::Config>::AccountId) -> Result<bool, ()> { <noble_session::Module<T>>::disable(validator) } fn validators() -> Vec<<T as fabric_system::Config>::AccountId> { <noble_session::Module<T>>::validators() } fn prune_historical_up_to(up_to: SessionIndex) { <noble_session::historical::Module<T>>::prune_up_to(up_to); } } pub trait Config: fabric_system::Config + SendTransactionTypes<Call<Self>> { /// The staking balance. type Currency: LockableCurrency<Self::AccountId, Moment=Self::BlockNumber>; /// Time used for computing era duration. /// /// It is guaranteed to start being called from the first `on_finalize`. Thus value at genesis /// is not used. type UnixTime: UnixTime; /// Convert a balance into a number used for election calculation. This must fit into a `u64` /// but is allowed to be sensibly lossy. The `u64` is used to communicate with the /// [`tp_npos_elections`] crate which accepts u64 numbers and does operations in 128. /// Consequently, the backward convert is used convert the u128s from tp-elections back to a /// [`BalanceOf`]. type CurrencyToVote: CurrencyToVote<BalanceOf<Self>>; /// Tokens have been minted and are unused for validator-reward. /// See [Era payout](./index.html#era-payout). type RewardRemainder: OnUnbalanced<NegativeImbalanceOf<Self>>; /// The overarching event type. type Event: From<Event<Self>> + Into<<Self as fabric_system::Config>::Event>; /// Handler for the unbalanced reduction when slashing a staker. type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>; /// Handler for the unbalanced increment when rewarding a staker. type Reward: OnUnbalanced<PositiveImbalanceOf<Self>>; /// Number of sessions per era. type SessionsPerEra: Get<SessionIndex>; /// Number of eras that staked funds must remain bonded for. type BondingDuration: Get<EraIndex>; /// Number of eras that slashes are deferred by, after computation. /// /// This should be less than the bonding duration. Set to 0 if slashes /// should be applied immediately, without opportunity for intervention. type SlashDeferDuration: Get<EraIndex>; /// The origin which can cancel a deferred slash. Root can always do this. type SlashCancelOrigin: EnsureOrigin<Self::Origin>; /// Interface for interacting with a session module. type SessionInterface: self::SessionInterface<Self::AccountId>; /// The NPoS reward curve used to define yearly inflation. /// See [Era payout](./index.html#era-payout). type RewardCurve: Get<&'static PiecewiseLinear<'static>>; /// Something that can estimate the next session change, accurately or as a best effort guess. type NextNewSession: EstimateNextNewSession<Self::BlockNumber>; /// The number of blocks before the end of the era from which election submissions are allowed. /// /// Setting this to zero will disable the offchain compute and only on-chain seq-phragmen will /// be used. /// /// This is bounded by being within the last session. Hence, setting it to a value more than the /// length of a session will be pointless. type ElectionLookahead: Get<Self::BlockNumber>; /// The overarching call type. type Call: Dispatchable + From<Call<Self>> + IsSubType<Call<Self>> + Clone; /// Maximum number of balancing iterations to run in the offchain submission. /// /// If set to 0, balance_solution will not be executed at all. type MaxIterations: Get<u32>; /// The threshold of improvement that should be provided for a new solution to be accepted. type MinSolutionScoreBump: Get<Perbill>; /// The maximum number of nominators rewarded for each validator. /// /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim /// their reward. This used to limit the i/o cost for the nominator payout. type MaxNominatorRewardedPerValidator: Get<u32>; /// A configuration for base priority of unsigned transactions. /// /// This is exposed so that it can be tuned for particular runtime, when /// multiple nobles send unsigned transactions. type UnsignedPriority: Get<TransactionPriority>; /// Maximum weight that the unsigned transaction can have. /// /// Chose this value with care. On one hand, it should be as high as possible, so the solution /// can contain as many nominators/validators as possible. On the other hand, it should be small /// enough to fit in the block. type OffchainSolutionWeightLimit: Get<Weight>; /// Weight information for extrinsics in this noble. type WeightInfo: WeightInfo; } /// Mode of era-forcing. #[derive(Copy, Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum Forcing { /// Not forcing anything - just let whatever happen. NotForcing, /// Force a new era, then reset to `NotForcing` as soon as it is done. ForceNew, /// Avoid a new era indefinitely. ForceNone, /// Force a new era at the end of all sessions indefinitely. ForceAlways, } impl Default for Forcing { fn default() -> Self { Forcing::NotForcing } } // A value placed in storage that represents the current version of the Staking storage. This value // is used by the `on_runtime_upgrade` logic to determine whether we run storage migration logic. // This should match directly with the semantic versions of the Rust crate. #[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug)] enum Releases { V1_0_0Ancient, V2_0_0, V3_0_0, V4_0_0, V5_0_0, } impl Default for Releases { fn default() -> Self { Releases::V5_0_0 } } decl_storage! { trait Store for Module<T: Config> as Staking { /// Number of eras to keep in history. /// /// Information is kept for eras in `[current_era - history_depth; current_era]`. /// /// Must be more than the number of eras delayed by session otherwise. I.e. active era must /// always be in history. I.e. `active_era > current_era - history_depth` must be /// guaranteed. HistoryDepth get(fn history_depth) config(): u32 = 84; /// The ideal number of staking participants. pub ValidatorCount get(fn validator_count) config(): u32; /// Minimum number of staking participants before emergency conditions are imposed. pub MinimumValidatorCount get(fn minimum_validator_count) config(): u32; /// Any validators that may never be slashed or forcibly kicked. It's a Vec since they're /// easy to initialize and the performance hit is minimal (we expect no more than four /// invulnerables) and restricted to testnets. pub Invulnerables get(fn invulnerables) config(): Vec<T::AccountId>; /// Map from all locked "stash" accounts to the controller account. pub Bonded get(fn bonded): map hasher(twox_64_concat) T::AccountId => Option<T::AccountId>; /// Map from all (unlocked) "controller" accounts to the info regarding the staking. pub Ledger get(fn ledger): map hasher(blake2_128_concat) T::AccountId => Option<StakingLedger<T::AccountId, BalanceOf<T>>>; /// Where the reward payment should be made. Keyed by stash. pub Payee get(fn payee): map hasher(twox_64_concat) T::AccountId => RewardDestination<T::AccountId>; /// The map from (wannabe) validator stash key to the preferences of that validator. pub Validators get(fn validators): map hasher(twox_64_concat) T::AccountId => ValidatorPrefs; /// The map from nominator stash key to the set of stash keys of all validators to nominate. pub Nominators get(fn nominators): map hasher(twox_64_concat) T::AccountId => Option<Nominations<T::AccountId>>; /// The current era index. /// /// This is the latest planned era, depending on how the Session noble queues the validator /// set, it might be active or not. pub CurrentEra get(fn current_era): Option<EraIndex>; /// The active era information, it holds index and start. /// /// The active era is the era being currently rewarded. Validator set of this era must be /// equal to [`SessionInterface::validators`]. pub ActiveEra get(fn active_era): Option<ActiveEraInfo>; /// The session index at which the era start for the last `HISTORY_DEPTH` eras. /// /// Note: This tracks the starting session (i.e. session index when era start being active) /// for the eras in `[CurrentEra - HISTORY_DEPTH, CurrentEra]`. pub ErasStartSessionIndex get(fn eras_start_session_index): map hasher(twox_64_concat) EraIndex => Option<SessionIndex>; /// Exposure of validator at era. /// /// This is keyed first by the era index to allow bulk deletion and then the stash account. /// /// Is it removed after `HISTORY_DEPTH` eras. /// If stakers hasn't been set or has been removed then empty exposure is returned. pub ErasStakers get(fn eras_stakers): double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId => Exposure<T::AccountId, BalanceOf<T>>; /// Clipped Exposure of validator at era. /// /// This is similar to [`ErasStakers`] but number of nominators exposed is reduced to the /// `T::MaxNominatorRewardedPerValidator` biggest stakers. /// (Note: the field `total` and `own` of the exposure remains unchanged). /// This is used to limit the i/o cost for the nominator payout. /// /// This is keyed fist by the era index to allow bulk deletion and then the stash account. /// /// Is it removed after `HISTORY_DEPTH` eras. /// If stakers hasn't been set or has been removed then empty exposure is returned. pub ErasStakersClipped get(fn eras_stakers_clipped): double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId => Exposure<T::AccountId, BalanceOf<T>>; /// Similar to `ErasStakers`, this holds the preferences of validators. /// /// This is keyed first by the era index to allow bulk deletion and then the stash account. /// /// Is it removed after `HISTORY_DEPTH` eras. // If prefs hasn't been set or has been removed then 0 commission is returned. pub ErasValidatorPrefs get(fn eras_validator_prefs): double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId => ValidatorPrefs; /// The total validator era payout for the last `HISTORY_DEPTH` eras. /// /// Eras that haven't finished yet or has been removed doesn't have reward. pub ErasValidatorReward get(fn eras_validator_reward): map hasher(twox_64_concat) EraIndex => Option<BalanceOf<T>>; /// Rewards for the last `HISTORY_DEPTH` eras. /// If reward hasn't been set or has been removed then 0 reward is returned. pub ErasRewardPoints get(fn eras_reward_points): map hasher(twox_64_concat) EraIndex => EraRewardPoints<T::AccountId>; /// The total amount staked for the last `HISTORY_DEPTH` eras. /// If total hasn't been set or has been removed then 0 stake is returned. pub ErasTotalStake get(fn eras_total_stake): map hasher(twox_64_concat) EraIndex => BalanceOf<T>; /// Mode of era forcing. pub ForceEra get(fn force_era) config(): Forcing; /// The percentage of the slash that is distributed to reporters. /// /// The rest of the slashed value is handled by the `Slash`. pub SlashRewardFraction get(fn slash_reward_fraction) config(): Perbill; /// The amount of currency given to reporters of a slash event which was /// canceled by extraordinary circumstances (e.g. governance). pub CanceledSlashPayout get(fn canceled_payout) config(): BalanceOf<T>; /// All unapplied slashes that are queued for later. pub UnappliedSlashes: map hasher(twox_64_concat) EraIndex => Vec<UnappliedSlash<T::AccountId, BalanceOf<T>>>; /// A mapping from still-bonded eras to the first session index of that era. /// /// Must contains information for eras for the range: /// `[active_era - bounding_duration; active_era]` BondedEras: Vec<(EraIndex, SessionIndex)>; /// All slashing events on validators, mapped by era to the highest slash proportion /// and slash value of the era. ValidatorSlashInEra: double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId => Option<(Perbill, BalanceOf<T>)>; /// All slashing events on nominators, mapped by era to the highest slash value of the era. NominatorSlashInEra: double_map hasher(twox_64_concat) EraIndex, hasher(twox_64_concat) T::AccountId => Option<BalanceOf<T>>; /// Slashing spans for stash accounts. SlashingSpans get(fn slashing_spans): map hasher(twox_64_concat) T::AccountId => Option<slashing::SlashingSpans>; /// Records information about the maximum slash of a stash within a slashing span, /// as well as how much reward has been paid out. SpanSlash: map hasher(twox_64_concat) (T::AccountId, slashing::SpanIndex) => slashing::SpanRecord<BalanceOf<T>>; /// The earliest era for which we have a pending, unapplied slash. EarliestUnappliedSlash: Option<EraIndex>; /// Snapshot of validators at the beginning of the current election window. This should only /// have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`. pub SnapshotValidators get(fn snapshot_validators): Option<Vec<T::AccountId>>; /// Snapshot of nominators at the beginning of the current election window. This should only /// have a value when [`EraElectionStatus`] == `ElectionStatus::Open(_)`. pub SnapshotNominators get(fn snapshot_nominators): Option<Vec<T::AccountId>>; /// The next validator set. At the end of an era, if this is available (potentially from the /// result of an offchain worker), it is immediately used. Otherwise, the on-chain election /// is executed. pub QueuedElected get(fn queued_elected): Option<ElectionResult<T::AccountId, BalanceOf<T>>>; /// The score of the current [`QueuedElected`]. pub QueuedScore get(fn queued_score): Option<ElectionScore>; /// Flag to control the execution of the offchain election. When `Open(_)`, we accept /// solutions to be submitted. pub EraElectionStatus get(fn era_election_status): ElectionStatus<T::BlockNumber>; /// True if the current **planned** session is final. Note that this does not take era /// forcing into account. pub IsCurrentSessionFinal get(fn is_current_session_final): bool = false; /// True if network has been upgraded to this version. /// Storage version of the noble. /// /// This is set to v5.0.0 for new networks. StorageVersion build(|_: &GenesisConfig<T>| Releases::V5_0_0): Releases; } add_extra_genesis { config(stakers): Vec<(T::AccountId, T::AccountId, BalanceOf<T>, StakerStatus<T::AccountId>)>; build(|config: &GenesisConfig<T>| { for &(ref stash, ref controller, balance, ref status) in &config.stakers { assert!( T::Currency::free_balance(&stash) >= balance, "Stash does not have enough balance to bond." ); let _ = <Module<T>>::bond( T::Origin::from(Some(stash.clone()).into()), T::Lookup::unlookup(controller.clone()), balance, RewardDestination::Staked, ); let _ = match status { StakerStatus::Validator => { <Module<T>>::validate( T::Origin::from(Some(controller.clone()).into()), Default::default(), ) }, StakerStatus::Nominator(votes) => { <Module<T>>::nominate( T::Origin::from(Some(controller.clone()).into()), votes.iter().map(|l| T::Lookup::unlookup(l.clone())).collect(), ) }, _ => Ok(()) }; } }); } } pub mod migrations { use super::*; #[derive(Decode)] struct OldValidatorPrefs { #[codec(compact)] pub commission: Perbill } impl OldValidatorPrefs { fn upgraded(self) -> ValidatorPrefs { ValidatorPrefs { commission: self.commission, .. Default::default() } } } pub fn migrate_to_blockable<T: Config>() -> fabric_support::weights::Weight { Validators::<T>::translate::<OldValidatorPrefs, _>(|_, p| Some(p.upgraded())); ErasValidatorPrefs::<T>::translate::<OldValidatorPrefs, _>(|_, _, p| Some(p.upgraded())); T::BlockWeights::get().max_block } } decl_event!( pub enum Event<T> where Balance = BalanceOf<T>, <T as fabric_system::Config>::AccountId { /// The era payout has been set; the first balance is the validator-payout; the second is /// the remainder from the maximum amount of reward. /// \[era_index, validator_payout, remainder\] EraPayout(EraIndex, Balance, Balance), /// The staker has been rewarded by this amount. \[stash, amount\] Reward(AccountId, Balance), /// One validator (and its nominators) has been slashed by the given amount. /// \[validator, amount\] Slash(AccountId, Balance), /// An old slashing report from a prior era was discarded because it could /// not be processed. \[session_index\] OldSlashingReportDiscarded(SessionIndex), /// A new set of stakers was elected with the given \[compute\]. StakingElection(ElectionCompute), /// A new solution for the upcoming election has been stored. \[compute\] SolutionStored(ElectionCompute), /// An account has bonded this amount. \[stash, amount\] /// /// NOTE: This event is only emitted when funds are bonded via a dispatchable. Notably, /// it will not be emitted for staking rewards when they are added to stake. Bonded(AccountId, Balance), /// An account has unbonded this amount. \[stash, amount\] Unbonded(AccountId, Balance), /// An account has called `withdraw_unbonded` and removed unbonding chunks worth `Balance` /// from the unlocking queue. \[stash, amount\] Withdrawn(AccountId, Balance), /// A nominator has been kicked from a validator. \[nominator, stash\] Kicked(AccountId, AccountId), } ); decl_error! { /// Error for the staking module. pub enum Error for Module<T: Config> { /// Not a controller account. NotController, /// Not a stash account. NotStash, /// Stash is already bonded. AlreadyBonded, /// Controller is already paired. AlreadyPaired, /// Targets cannot be empty. EmptyTargets, /// Duplicate index. DuplicateIndex, /// Slash record index out of bounds. InvalidSlashIndex, /// Can not bond with value less than minimum balance. InsufficientValue, /// Can not schedule more unlock chunks. NoMoreChunks, /// Can not rebond without unlocking chunks. NoUnlockChunk, /// Attempting to target a stash that still has funds. FundedTarget, /// Invalid era to reward. InvalidEraToReward, /// Invalid number of nominations. InvalidNumberOfNominations, /// Items are not sorted and unique. NotSortedAndUnique, /// Rewards for this era have already been claimed for this validator. AlreadyClaimed, /// The submitted result is received out of the open window. OffchainElectionEarlySubmission, /// The submitted result is not as good as the one stored on chain. OffchainElectionWeakSubmission, /// The snapshot data of the current window is missing. SnapshotUnavailable, /// Incorrect number of winners were presented. OffchainElectionBogusWinnerCount, /// One of the submitted winners is not an active candidate on chain (index is out of range /// in snapshot). OffchainElectionBogusWinner, /// Error while building the assignment type from the compact. This can happen if an index /// is invalid, or if the weights _overflow_. OffchainElectionBogusCompact, /// One of the submitted nominators is not an active nominator on chain. OffchainElectionBogusNominator, /// One of the submitted nominators has an edge to which they have not voted on chain. OffchainElectionBogusNomination, /// One of the submitted nominators has an edge which is submitted before the last non-zero /// slash of the target. OffchainElectionSlashedNomination, /// A self vote must only be originated from a validator to ONLY themselves. OffchainElectionBogusSelfVote, /// The submitted result has unknown edges that are not among the presented winners. OffchainElectionBogusEdge, /// The claimed score does not match with the one computed from the data. OffchainElectionBogusScore, /// The election size is invalid. OffchainElectionBogusElectionSize, /// The call is not allowed at the given time due to restrictions of election period. CallNotAllowed, /// Incorrect previous history depth input provided. IncorrectHistoryDepth, /// Incorrect number of slashing spans provided. IncorrectSlashingSpans, /// Internal state has become somehow corrupted and the operation cannot continue. BadState, /// Too many nomination targets supplied. TooManyTargets, /// A nomination target was supplied that was blocked or otherwise not a validator. BadTarget, } } decl_module! { pub struct Module<T: Config> for enum Call where origin: T::Origin { /// Number of sessions per era. const SessionsPerEra: SessionIndex = T::SessionsPerEra::get(); /// Number of eras that staked funds must remain bonded for. const BondingDuration: EraIndex = T::BondingDuration::get(); /// Number of eras that slashes are deferred by, after computation. /// /// This should be less than the bonding duration. /// Set to 0 if slashes should be applied immediately, without opportunity for /// intervention. const SlashDeferDuration: EraIndex = T::SlashDeferDuration::get(); /// The number of blocks before the end of the era from which election submissions are allowed. /// /// Setting this to zero will disable the offchain compute and only on-chain seq-phragmen will /// be used. /// /// This is bounded by being within the last session. Hence, setting it to a value more than the /// length of a session will be pointless. const ElectionLookahead: T::BlockNumber = T::ElectionLookahead::get(); /// Maximum number of balancing iterations to run in the offchain submission. /// /// If set to 0, balance_solution will not be executed at all. const MaxIterations: u32 = T::MaxIterations::get(); /// The threshold of improvement that should be provided for a new solution to be accepted. const MinSolutionScoreBump: Perbill = T::MinSolutionScoreBump::get(); /// The maximum number of nominators rewarded for each validator. /// /// For each validator only the `$MaxNominatorRewardedPerValidator` biggest stakers can claim /// their reward. This used to limit the i/o cost for the nominator payout. const MaxNominatorRewardedPerValidator: u32 = T::MaxNominatorRewardedPerValidator::get(); type Error = Error<T>; fn deposit_event() = default; fn on_runtime_upgrade() -> fabric_support::weights::Weight { if StorageVersion::get() == Releases::V4_0_0 { StorageVersion::put(Releases::V5_0_0); migrations::migrate_to_blockable::<T>() } else { 0 } } /// sets `ElectionStatus` to `Open(now)` where `now` is the block number at which the /// election window has opened, if we are at the last session and less blocks than /// `T::ElectionLookahead` is remaining until the next new session schedule. The offchain /// worker, if applicable, will execute at the end of the current block, and solutions may /// be submitted. fn on_initialize(now: T::BlockNumber) -> Weight { let mut consumed_weight = 0; let mut add_weight = |reads, writes, weight| { consumed_weight += T::DbWeight::get().reads_writes(reads, writes); consumed_weight += weight; }; if // if we don't have any ongoing offchain compute. Self::era_election_status().is_closed() && // either current session final based on the plan, or we're forcing. (Self::is_current_session_final() || Self::will_era_be_forced()) { if let Some(next_session_change) = T::NextNewSession::estimate_next_new_session(now) { if let Some(remaining) = next_session_change.checked_sub(&now) { if remaining <= T::ElectionLookahead::get() && !remaining.is_zero() { // create snapshot. let (did_snapshot, snapshot_weight) = Self::create_stakers_snapshot(); add_weight(0, 0, snapshot_weight); if did_snapshot { // Set the flag to make sure we don't waste any compute here in the same era // after we have triggered the offline compute. <EraElectionStatus<T>>::put( ElectionStatus::<T::BlockNumber>::Open(now) ); add_weight(0, 1, 0); log!(info, "💸 Election window is Open({:?}). Snapshot created", now); } else { log!(warn, "💸 Failed to create snapshot at {:?}.", now); } } } } else { log!(warn, "💸 Estimating next session change failed."); } add_weight(0, 0, T::NextNewSession::weight(now)) } // For `era_election_status`, `is_current_session_final`, `will_era_be_forced` add_weight(3, 0, 0); // Additional read from `on_finalize` add_weight(1, 0, 0); consumed_weight } /// Check if the current block number is the one at which the election window has been set /// to open. If so, it runs the offchain worker code. fn offchain_worker(now: T::BlockNumber) { use offchain_election::{set_check_offchain_execution_status, compute_offchain_election}; if Self::era_election_status().is_open_at(now) { let offchain_status = set_check_offchain_execution_status::<T>(now); if let Err(why) = offchain_status { log!(warn, "💸 skipping offchain worker in open election window due to [{}]", why); } else { if let Err(e) = compute_offchain_election::<T>() { log!(error, "💸 Error in election offchain worker: {:?}", e); } else { log!(debug, "💸 Executed offchain worker thread without errors."); } } } } fn on_finalize() { // Set the start of the first era. if let Some(mut active_era) = Self::active_era() { if active_era.start.is_none() { let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>(); active_era.start = Some(now_as_millis_u64); // This write only ever happens once, we don't include it in the weight in general ActiveEra::put(active_era); } } // `on_finalize` weight is tracked in `on_initialize` } fn integrity_test() { tet_io::TestExternalities::new_empty().execute_with(|| assert!( T::SlashDeferDuration::get() < T::BondingDuration::get() || T::BondingDuration::get() == 0, "As per documentation, slash defer duration ({}) should be less than bonding duration ({}).", T::SlashDeferDuration::get(), T::BondingDuration::get(), ) ); use tp_runtime::UpperOf; // see the documentation of `Assignment::try_normalize`. Now we can ensure that this // will always return `Ok`. // 1. Maximum sum of Vec<ChainAccuracy> must fit into `UpperOf<ChainAccuracy>`. assert!( <usize as TryInto<UpperOf<ChainAccuracy>>>::try_into(MAX_NOMINATIONS) .unwrap() .checked_mul(<ChainAccuracy>::one().deconstruct().try_into().unwrap()) .is_some() ); // 2. Maximum sum of Vec<OffchainAccuracy> must fit into `UpperOf<OffchainAccuracy>`. assert!( <usize as TryInto<UpperOf<OffchainAccuracy>>>::try_into(MAX_NOMINATIONS) .unwrap() .checked_mul(<OffchainAccuracy>::one().deconstruct().try_into().unwrap()) .is_some() ); } /// Take the origin account as a stash and lock up `value` of its balance. `controller` will /// be the account that controls it. /// /// `value` must be more than the `minimum_balance` specified by `T::Currency`. /// /// The dispatch origin for this call must be _Signed_ by the stash account. /// /// Emits `Bonded`. /// /// # <weight> /// - Independent of the arguments. Moderate complexity. /// - O(1). /// - Three extra DB entries. /// /// NOTE: Two of the storage writes (`Self::bonded`, `Self::payee`) are _never_ cleaned /// unless the `origin` falls below _existential deposit_ and gets removed as dust. /// ------------------ /// Weight: O(1) /// DB Weight: /// - Read: Bonded, Ledger, [Origin Account], Current Era, History Depth, Locks /// - Write: Bonded, Payee, [Origin Account], Locks, Ledger /// # </weight> #[weight = T::WeightInfo::bond()] pub fn bond(origin, controller: <T::Lookup as StaticLookup>::Source, #[compact] value: BalanceOf<T>, payee: RewardDestination<T::AccountId>, ) { let stash = ensure_signed(origin)?; if <Bonded<T>>::contains_key(&stash) { Err(Error::<T>::AlreadyBonded)? } let controller = T::Lookup::lookup(controller)?; if <Ledger<T>>::contains_key(&controller) { Err(Error::<T>::AlreadyPaired)? } // reject a bond which is considered to be _dust_. if value < T::Currency::minimum_balance() { Err(Error::<T>::InsufficientValue)? } system::Module::<T>::inc_consumers(&stash).map_err(|_| Error::<T>::BadState)?; // You're auto-bonded forever, here. We might improve this by only bonding when // you actually validate/nominate and remove once you unbond __everything__. <Bonded<T>>::insert(&stash, &controller); <Payee<T>>::insert(&stash, payee); let current_era = CurrentEra::get().unwrap_or(0); let history_depth = Self::history_depth(); let last_reward_era = current_era.saturating_sub(history_depth); let stash_balance = T::Currency::free_balance(&stash); let value = value.min(stash_balance); Self::deposit_event(RawEvent::Bonded(stash.clone(), value)); let item = StakingLedger { stash, total: value, active: value, unlocking: vec![], claimed_rewards: (last_reward_era..current_era).collect(), }; Self::update_ledger(&controller, &item); } /// Add some extra amount that have appeared in the stash `free_balance` into the balance up /// for staking. /// /// Use this if there are additional funds in your stash account that you wish to bond. /// Unlike [`bond`] or [`unbond`] this function does not impose any limitation on the amount /// that can be added. /// /// The dispatch origin for this call must be _Signed_ by the stash, not the controller and /// it can be only called when [`EraElectionStatus`] is `Closed`. /// /// Emits `Bonded`. /// /// # <weight> /// - Independent of the arguments. Insignificant complexity. /// - O(1). /// - One DB entry. /// ------------ /// DB Weight: /// - Read: Era Election Status, Bonded, Ledger, [Origin Account], Locks /// - Write: [Origin Account], Locks, Ledger /// # </weight> #[weight = T::WeightInfo::bond_extra()] fn bond_extra(origin, #[compact] max_additional: BalanceOf<T>) { ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); let stash = ensure_signed(origin)?; let controller = Self::bonded(&stash).ok_or(Error::<T>::NotStash)?; let mut ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; let stash_balance = T::Currency::free_balance(&stash); if let Some(extra) = stash_balance.checked_sub(&ledger.total) { let extra = extra.min(max_additional); ledger.total += extra; ledger.active += extra; // last check: the new active amount of ledger must be more than ED. ensure!(ledger.active >= T::Currency::minimum_balance(), Error::<T>::InsufficientValue); Self::deposit_event(RawEvent::Bonded(stash, extra)); Self::update_ledger(&controller, &ledger); } } /// Schedule a portion of the stash to be unlocked ready for transfer out after the bond /// period ends. If this leaves an amount actively bonded less than /// T::Currency::minimum_balance(), then it is increased to the full amount. /// /// Once the unlock period is done, you can call `withdraw_unbonded` to actually move /// the funds out of management ready for transfer. /// /// No more than a limited number of unlocking chunks (see `MAX_UNLOCKING_CHUNKS`) /// can co-exists at the same time. In that case, [`Call::withdraw_unbonded`] need /// to be called first to remove some of the chunks (if possible). /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// And, it can be only called when [`EraElectionStatus`] is `Closed`. /// /// Emits `Unbonded`. /// /// See also [`Call::withdraw_unbonded`]. /// /// # <weight> /// - Independent of the arguments. Limited but potentially exploitable complexity. /// - Contains a limited number of reads. /// - Each call (requires the remainder of the bonded balance to be above `minimum_balance`) /// will cause a new entry to be inserted into a vector (`Ledger.unlocking`) kept in storage. /// The only way to clean the aforementioned storage item is also user-controlled via /// `withdraw_unbonded`. /// - One DB entry. /// ---------- /// Weight: O(1) /// DB Weight: /// - Read: EraElectionStatus, Ledger, CurrentEra, Locks, BalanceOf Stash, /// - Write: Locks, Ledger, BalanceOf Stash, /// </weight> #[weight = T::WeightInfo::unbond()] fn unbond(origin, #[compact] value: BalanceOf<T>) { ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; ensure!( ledger.unlocking.len() < MAX_UNLOCKING_CHUNKS, Error::<T>::NoMoreChunks, ); let mut value = value.min(ledger.active); if !value.is_zero() { ledger.active -= value; // Avoid there being a dust balance left in the staking system. if ledger.active < T::Currency::minimum_balance() { value += ledger.active; ledger.active = Zero::zero(); } // Note: in case there is no current era it is fine to bond one era more. let era = Self::current_era().unwrap_or(0) + T::BondingDuration::get(); ledger.unlocking.push(UnlockChunk { value, era }); Self::update_ledger(&controller, &ledger); Self::deposit_event(RawEvent::Unbonded(ledger.stash, value)); } } /// Remove any unlocked chunks from the `unlocking` queue from our management. /// /// This essentially frees up that balance to be used by the stash account to do /// whatever it wants. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// And, it can be only called when [`EraElectionStatus`] is `Closed`. /// /// Emits `Withdrawn`. /// /// See also [`Call::unbond`]. /// /// # <weight> /// - Could be dependent on the `origin` argument and how much `unlocking` chunks exist. /// It implies `consolidate_unlocked` which loops over `Ledger.unlocking`, which is /// indirectly user-controlled. See [`unbond`] for more detail. /// - Contains a limited number of reads, yet the size of which could be large based on `ledger`. /// - Writes are limited to the `origin` account key. /// --------------- /// Complexity O(S) where S is the number of slashing spans to remove /// Update: /// - Reads: EraElectionStatus, Ledger, Current Era, Locks, [Origin Account] /// - Writes: [Origin Account], Locks, Ledger /// Kill: /// - Reads: EraElectionStatus, Ledger, Current Era, Bonded, Slashing Spans, [Origin /// Account], Locks, BalanceOf stash /// - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, /// [Origin Account], Locks, BalanceOf stash. /// - Writes Each: SpanSlash * S /// NOTE: Weight annotation is the kill scenario, we refund otherwise. /// # </weight> #[weight = T::WeightInfo::withdraw_unbonded_kill(*num_slashing_spans)] fn withdraw_unbonded(origin, num_slashing_spans: u32) -> DispatchResultWithPostInfo { ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); let controller = ensure_signed(origin)?; let mut ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; let (stash, old_total) = (ledger.stash.clone(), ledger.total); if let Some(current_era) = Self::current_era() { ledger = ledger.consolidate_unlocked(current_era) } let post_info_weight = if ledger.unlocking.is_empty() && ledger.active <= T::Currency::minimum_balance() { // This account must have called `unbond()` with some value that caused the active // portion to fall below existential deposit + will have no more unlocking chunks // left. We can now safely remove all staking-related information. Self::kill_stash(&stash, num_slashing_spans)?; // remove the lock. T::Currency::remove_lock(STAKING_ID, &stash); // This is worst case scenario, so we use the full weight and return None None } else { // This was the consequence of a partial unbond. just update the ledger and move on. Self::update_ledger(&controller, &ledger); // This is only an update, so we use less overall weight. Some(T::WeightInfo::withdraw_unbonded_update(num_slashing_spans)) }; // `old_total` should never be less than the new total because // `consolidate_unlocked` strictly subtracts balance. if ledger.total < old_total { // Already checked that this won't overflow by entry condition. let value = old_total - ledger.total; Self::deposit_event(RawEvent::Withdrawn(stash, value)); } Ok(post_info_weight.into()) } /// Declare the desire to validate for the origin controller. /// /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// And, it can be only called when [`EraElectionStatus`] is `Closed`. /// /// # <weight> /// - Independent of the arguments. Insignificant complexity. /// - Contains a limited number of reads. /// - Writes are limited to the `origin` account key. /// ----------- /// Weight: O(1) /// DB Weight: /// - Read: Era Election Status, Ledger /// - Write: Nominators, Validators /// # </weight> #[weight = T::WeightInfo::validate()] pub fn validate(origin, prefs: ValidatorPrefs) { ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; let stash = &ledger.stash; <Nominators<T>>::remove(stash); <Validators<T>>::insert(stash, prefs); } /// Declare the desire to nominate `targets` for the origin controller. /// /// Effects will be felt at the beginning of the next era. This can only be called when /// [`EraElectionStatus`] is `Closed`. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// And, it can be only called when [`EraElectionStatus`] is `Closed`. /// /// # <weight> /// - The transaction's complexity is proportional to the size of `targets` (N) /// which is capped at CompactAssignments::LIMIT (MAX_NOMINATIONS). /// - Both the reads and writes follow a similar pattern. /// --------- /// Weight: O(N) /// where N is the number of targets /// DB Weight: /// - Reads: Era Election Status, Ledger, Current Era /// - Writes: Validators, Nominators /// # </weight> #[weight = T::WeightInfo::nominate(targets.len() as u32)] pub fn nominate(origin, targets: Vec<<T::Lookup as StaticLookup>::Source>) { ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; let stash = &ledger.stash; ensure!(!targets.is_empty(), Error::<T>::EmptyTargets); ensure!(targets.len() <= MAX_NOMINATIONS, Error::<T>::TooManyTargets); let old = Nominators::<T>::get(stash).map_or_else(Vec::new, |x| x.targets); let targets = targets.into_iter() .map(|t| T::Lookup::lookup(t).map_err(DispatchError::from)) .map(|n| n.and_then(|n| if old.contains(&n) || !Validators::<T>::get(&n).blocked { Ok(n) } else { Err(Error::<T>::BadTarget.into()) })) .collect::<result::Result<Vec<T::AccountId>, _>>()?; let nominations = Nominations { targets, // initial nominations are considered submitted at era 0. See `Nominations` doc submitted_in: Self::current_era().unwrap_or(0), suppressed: false, }; <Validators<T>>::remove(stash); <Nominators<T>>::insert(stash, &nominations); } /// Declare no desire to either validate or nominate. /// /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// And, it can be only called when [`EraElectionStatus`] is `Closed`. /// /// # <weight> /// - Independent of the arguments. Insignificant complexity. /// - Contains one read. /// - Writes are limited to the `origin` account key. /// -------- /// Weight: O(1) /// DB Weight: /// - Read: EraElectionStatus, Ledger /// - Write: Validators, Nominators /// # </weight> #[weight = T::WeightInfo::chill()] fn chill(origin) { ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; Self::chill_stash(&ledger.stash); } /// (Re-)set the payment target for a controller. /// /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// /// # <weight> /// - Independent of the arguments. Insignificant complexity. /// - Contains a limited number of reads. /// - Writes are limited to the `origin` account key. /// --------- /// - Weight: O(1) /// - DB Weight: /// - Read: Ledger /// - Write: Payee /// # </weight> #[weight = T::WeightInfo::set_payee()] fn set_payee(origin, payee: RewardDestination<T::AccountId>) { let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; let stash = &ledger.stash; <Payee<T>>::insert(stash, payee); } /// (Re-)set the controller of a stash. /// /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the stash, not the controller. /// /// # <weight> /// - Independent of the arguments. Insignificant complexity. /// - Contains a limited number of reads. /// - Writes are limited to the `origin` account key. /// ---------- /// Weight: O(1) /// DB Weight: /// - Read: Bonded, Ledger New Controller, Ledger Old Controller /// - Write: Bonded, Ledger New Controller, Ledger Old Controller /// # </weight> #[weight = T::WeightInfo::set_controller()] fn set_controller(origin, controller: <T::Lookup as StaticLookup>::Source) { let stash = ensure_signed(origin)?; let old_controller = Self::bonded(&stash).ok_or(Error::<T>::NotStash)?; let controller = T::Lookup::lookup(controller)?; if <Ledger<T>>::contains_key(&controller) { Err(Error::<T>::AlreadyPaired)? } if controller != old_controller { <Bonded<T>>::insert(&stash, &controller); if let Some(l) = <Ledger<T>>::take(&old_controller) { <Ledger<T>>::insert(&controller, l); } } } /// Sets the ideal number of validators. /// /// The dispatch origin must be Root. /// /// # <weight> /// Weight: O(1) /// Write: Validator Count /// # </weight> #[weight = T::WeightInfo::set_validator_count()] fn set_validator_count(origin, #[compact] new: u32) { ensure_root(origin)?; ValidatorCount::put(new); } /// Increments the ideal number of validators. /// /// The dispatch origin must be Root. /// /// # <weight> /// Same as [`set_validator_count`]. /// # </weight> #[weight = T::WeightInfo::set_validator_count()] fn increase_validator_count(origin, #[compact] additional: u32) { ensure_root(origin)?; ValidatorCount::mutate(|n| *n += additional); } /// Scale up the ideal number of validators by a factor. /// /// The dispatch origin must be Root. /// /// # <weight> /// Same as [`set_validator_count`]. /// # </weight> #[weight = T::WeightInfo::set_validator_count()] fn scale_validator_count(origin, factor: Percent) { ensure_root(origin)?; ValidatorCount::mutate(|n| *n += factor * *n); } /// Force there to be no new eras indefinitely. /// /// The dispatch origin must be Root. /// /// # <weight> /// - No arguments. /// - Weight: O(1) /// - Write: ForceEra /// # </weight> #[weight = T::WeightInfo::force_no_eras()] fn force_no_eras(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceNone); } /// Force there to be a new era at the end of the next session. After this, it will be /// reset to normal (non-forced) behaviour. /// /// The dispatch origin must be Root. /// /// # <weight> /// - No arguments. /// - Weight: O(1) /// - Write ForceEra /// # </weight> #[weight = T::WeightInfo::force_new_era()] fn force_new_era(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceNew); } /// Set the validators who cannot be slashed (if any). /// /// The dispatch origin must be Root. /// /// # <weight> /// - O(V) /// - Write: Invulnerables /// # </weight> #[weight = T::WeightInfo::set_invulnerables(invulnerables.len() as u32)] fn set_invulnerables(origin, invulnerables: Vec<T::AccountId>) { ensure_root(origin)?; <Invulnerables<T>>::put(invulnerables); } /// Force a current staker to become completely unstaked, immediately. /// /// The dispatch origin must be Root. /// /// # <weight> /// O(S) where S is the number of slashing spans to be removed /// Reads: Bonded, Slashing Spans, Account, Locks /// Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Account, Locks /// Writes Each: SpanSlash * S /// # </weight> #[weight = T::WeightInfo::force_unstake(*num_slashing_spans)] fn force_unstake(origin, stash: T::AccountId, num_slashing_spans: u32) { ensure_root(origin)?; // remove all staking-related information. Self::kill_stash(&stash, num_slashing_spans)?; // remove the lock. T::Currency::remove_lock(STAKING_ID, &stash); } /// Force there to be a new era at the end of sessions indefinitely. /// /// The dispatch origin must be Root. /// /// # <weight> /// - Weight: O(1) /// - Write: ForceEra /// # </weight> #[weight = T::WeightInfo::force_new_era_always()] fn force_new_era_always(origin) { ensure_root(origin)?; ForceEra::put(Forcing::ForceAlways); } /// Cancel enactment of a deferred slash. /// /// Can be called by the `T::SlashCancelOrigin`. /// /// Parameters: era and indices of the slashes for that era to kill. /// /// # <weight> /// Complexity: O(U + S) /// with U unapplied slashes weighted with U=1000 /// and S is the number of slash indices to be canceled. /// - Read: Unapplied Slashes /// - Write: Unapplied Slashes /// # </weight> #[weight = T::WeightInfo::cancel_deferred_slash(slash_indices.len() as u32)] fn cancel_deferred_slash(origin, era: EraIndex, slash_indices: Vec<u32>) { T::SlashCancelOrigin::ensure_origin(origin)?; ensure!(!slash_indices.is_empty(), Error::<T>::EmptyTargets); ensure!(is_sorted_and_unique(&slash_indices), Error::<T>::NotSortedAndUnique); let mut unapplied = <Self as Store>::UnappliedSlashes::get(&era); let last_item = slash_indices[slash_indices.len() - 1]; ensure!((last_item as usize) < unapplied.len(), Error::<T>::InvalidSlashIndex); for (removed, index) in slash_indices.into_iter().enumerate() { let index = (index as usize) - removed; unapplied.remove(index); } <Self as Store>::UnappliedSlashes::insert(&era, &unapplied); } /// Pay out all the stakers behind a single validator for a single era. /// /// - `validator_stash` is the stash account of the validator. Their nominators, up to /// `T::MaxNominatorRewardedPerValidator`, will also receive their rewards. /// - `era` may be any era between `[current_era - history_depth; current_era]`. /// /// The origin of this call must be _Signed_. Any account can call this function, even if /// it is not one of the stakers. /// /// This can only be called when [`EraElectionStatus`] is `Closed`. /// /// # <weight> /// - Time complexity: at most O(MaxNominatorRewardedPerValidator). /// - Contains a limited number of reads and writes. /// ----------- /// N is the Number of payouts for the validator (including the validator) /// Weight: /// - Reward Destination Staked: O(N) /// - Reward Destination Controller (Creating): O(N) /// DB Weight: /// - Read: EraElectionStatus, CurrentEra, HistoryDepth, ErasValidatorReward, /// ErasStakersClipped, ErasRewardPoints, ErasValidatorPrefs (8 items) /// - Read Each: Bonded, Ledger, Payee, Locks, System Account (5 items) /// - Write Each: System Account, Locks, Ledger (3 items) /// /// NOTE: weights are assuming that payouts are made to alive stash account (Staked). /// Paying even a dead controller is cheaper weight-wise. We don't do any refunds here. /// # </weight> #[weight = T::WeightInfo::payout_stakers_alive_staked(T::MaxNominatorRewardedPerValidator::get())] fn payout_stakers(origin, validator_stash: T::AccountId, era: EraIndex) -> DispatchResult { ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); ensure_signed(origin)?; Self::do_payout_stakers(validator_stash, era) } /// Rebond a portion of the stash scheduled to be unlocked. /// /// The dispatch origin must be signed by the controller, and it can be only called when /// [`EraElectionStatus`] is `Closed`. /// /// # <weight> /// - Time complexity: O(L), where L is unlocking chunks /// - Bounded by `MAX_UNLOCKING_CHUNKS`. /// - Storage changes: Can't increase storage, only decrease it. /// --------------- /// - DB Weight: /// - Reads: EraElectionStatus, Ledger, Locks, [Origin Account] /// - Writes: [Origin Account], Locks, Ledger /// # </weight> #[weight = T::WeightInfo::rebond(MAX_UNLOCKING_CHUNKS as u32)] fn rebond(origin, #[compact] value: BalanceOf<T>) -> DispatchResultWithPostInfo { ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); let controller = ensure_signed(origin)?; let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; ensure!(!ledger.unlocking.is_empty(), Error::<T>::NoUnlockChunk); let ledger = ledger.rebond(value); // last check: the new active amount of ledger must be more than ED. ensure!(ledger.active >= T::Currency::minimum_balance(), Error::<T>::InsufficientValue); Self::update_ledger(&controller, &ledger); Ok(Some( 35 * WEIGHT_PER_MICROS + 50 * WEIGHT_PER_NANOS * (ledger.unlocking.len() as Weight) + T::DbWeight::get().reads_writes(3, 2) ).into()) } /// Set `HistoryDepth` value. This function will delete any history information /// when `HistoryDepth` is reduced. /// /// Parameters: /// - `new_history_depth`: The new history depth you would like to set. /// - `era_items_deleted`: The number of items that will be deleted by this dispatch. /// This should report all the storage items that will be deleted by clearing old /// era history. Needed to report an accurate weight for the dispatch. Trusted by /// `Root` to report an accurate number. /// /// Origin must be root. /// /// # <weight> /// - E: Number of history depths removed, i.e. 10 -> 7 = 3 /// - Weight: O(E) /// - DB Weight: /// - Reads: Current Era, History Depth /// - Writes: History Depth /// - Clear Prefix Each: Era Stakers, EraStakersClipped, ErasValidatorPrefs /// - Writes Each: ErasValidatorReward, ErasRewardPoints, ErasTotalStake, ErasStartSessionIndex /// # </weight> #[weight = T::WeightInfo::set_history_depth(*_era_items_deleted)] fn set_history_depth(origin, #[compact] new_history_depth: EraIndex, #[compact] _era_items_deleted: u32, ) { ensure_root(origin)?; if let Some(current_era) = Self::current_era() { HistoryDepth::mutate(|history_depth| { let last_kept = current_era.checked_sub(*history_depth).unwrap_or(0); let new_last_kept = current_era.checked_sub(new_history_depth).unwrap_or(0); for era_index in last_kept..new_last_kept { Self::clear_era_information(era_index); } *history_depth = new_history_depth }) } } /// Remove all data structure concerning a staker/stash once its balance is at the minimum. /// This is essentially equivalent to `withdraw_unbonded` except it can be called by anyone /// and the target `stash` must have no funds left beyond the ED. /// /// This can be called from any origin. /// /// - `stash`: The stash account to reap. Its balance must be zero. /// /// # <weight> /// Complexity: O(S) where S is the number of slashing spans on the account. /// DB Weight: /// - Reads: Stash Account, Bonded, Slashing Spans, Locks /// - Writes: Bonded, Slashing Spans (if S > 0), Ledger, Payee, Validators, Nominators, Stash Account, Locks /// - Writes Each: SpanSlash * S /// # </weight> #[weight = T::WeightInfo::reap_stash(*num_slashing_spans)] fn reap_stash(_origin, stash: T::AccountId, num_slashing_spans: u32) { let at_minimum = T::Currency::total_balance(&stash) == T::Currency::minimum_balance(); ensure!(at_minimum, Error::<T>::FundedTarget); Self::kill_stash(&stash, num_slashing_spans)?; T::Currency::remove_lock(STAKING_ID, &stash); } /// Submit an election result to the chain. If the solution: /// /// 1. is valid. /// 2. has a better score than a potentially existing solution on chain. /// /// then, it will be _put_ on chain. /// /// A solution consists of two pieces of data: /// /// 1. `winners`: a flat vector of all the winners of the round. /// 2. `assignments`: the compact version of an assignment vector that encodes the edge /// weights. /// /// Both of which may be computed using _phragmen_, or any other algorithm. /// /// Additionally, the submitter must provide: /// /// - The `score` that they claim their solution has. /// /// Both validators and nominators will be represented by indices in the solution. The /// indices should respect the corresponding types ([`ValidatorIndex`] and /// [`NominatorIndex`]). Moreover, they should be valid when used to index into /// [`SnapshotValidators`] and [`SnapshotNominators`]. Any invalid index will cause the /// solution to be rejected. These two storage items are set during the election window and /// may be used to determine the indices. /// /// A solution is valid if: /// /// 0. It is submitted when [`EraElectionStatus`] is `Open`. /// 1. Its claimed score is equal to the score computed on-chain. /// 2. Presents the correct number of winners. /// 3. All indexes must be value according to the snapshot vectors. All edge values must /// also be correct and should not overflow the granularity of the ratio type (i.e. 256 /// or billion). /// 4. For each edge, all targets are actually nominated by the voter. /// 5. Has correct self-votes. /// /// A solutions score is consisted of 3 parameters: /// /// 1. `min { support.total }` for each support of a winner. This value should be maximized. /// 2. `sum { support.total }` for each support of a winner. This value should be minimized. /// 3. `sum { support.total^2 }` for each support of a winner. This value should be /// minimized (to ensure less variance) /// /// # <weight> /// The transaction is assumed to be the longest path, a better solution. /// - Initial solution is almost the same. /// - Worse solution is retraced in pre-dispatch-checks which sets its own weight. /// # </weight> #[weight = T::WeightInfo::submit_solution_better( size.validators.into(), size.nominators.into(), compact.voter_count() as u32, winners.len() as u32, )] pub fn submit_election_solution( origin, winners: Vec<ValidatorIndex>, compact: CompactAssignments, score: ElectionScore, era: EraIndex, size: ElectionSize, ) -> DispatchResultWithPostInfo { let _who = ensure_signed(origin)?; Self::check_and_replace_solution( winners, compact, ElectionCompute::Signed, score, era, size, ) } /// Unsigned version of `submit_election_solution`. /// /// Note that this must pass the [`ValidateUnsigned`] check which only allows transactions /// from the local node to be included. In other words, only the block author can include a /// transaction in the block. /// /// # <weight> /// See [`submit_election_solution`]. /// # </weight> #[weight = T::WeightInfo::submit_solution_better( size.validators.into(), size.nominators.into(), compact.voter_count() as u32, winners.len() as u32, )] pub fn submit_election_solution_unsigned( origin, winners: Vec<ValidatorIndex>, compact: CompactAssignments, score: ElectionScore, era: EraIndex, size: ElectionSize, ) -> DispatchResultWithPostInfo { ensure_none(origin)?; let adjustments = Self::check_and_replace_solution( winners, compact, ElectionCompute::Unsigned, score, era, size, ).expect( "An unsigned solution can only be submitted by validators; A validator should \ always produce correct solutions, else this block should not be imported, thus \ effectively depriving the validators from their authoring reward. Hence, this panic is expected." ); Ok(adjustments) } /// Remove the given nominations from the calling validator. /// /// Effects will be felt at the beginning of the next era. /// /// The dispatch origin for this call must be _Signed_ by the controller, not the stash. /// And, it can be only called when [`EraElectionStatus`] is `Closed`. The controller /// account should represent a validator. /// /// - `who`: A list of nominator stash accounts who are nominating this validator which /// should no longer be nominating this validator. /// /// Note: Making this call only makes sense if you first set the validator preferences to /// block any further nominations. #[weight = T::WeightInfo::kick(who.len() as u32)] pub fn kick(origin, who: Vec<<T::Lookup as StaticLookup>::Source>) -> DispatchResult { let controller = ensure_signed(origin)?; ensure!(Self::era_election_status().is_closed(), Error::<T>::CallNotAllowed); let ledger = Self::ledger(&controller).ok_or(Error::<T>::NotController)?; let stash = &ledger.stash; for nom_stash in who.into_iter() .map(T::Lookup::lookup) .collect::<Result<Vec<T::AccountId>, _>>()? .into_iter() { Nominators::<T>::mutate(&nom_stash, |maybe_nom| if let Some(ref mut nom) = maybe_nom { if let Some(pos) = nom.targets.iter().position(|v| v == stash) { nom.targets.swap_remove(pos); Self::deposit_event(RawEvent::Kicked(nom_stash.clone(), stash.clone())); } }); } Ok(()) } } } impl<T: Config> Module<T> { /// The total balance that can be slashed from a stash account as of right now. pub fn slashable_balance_of(stash: &T::AccountId) -> BalanceOf<T> { // Weight note: consider making the stake accessible through stash. Self::bonded(stash).and_then(Self::ledger).map(|l| l.active).unwrap_or_default() } /// Internal impl of [`Self::slashable_balance_of`] that returns [`VoteWeight`]. pub fn slashable_balance_of_vote_weight(stash: &T::AccountId, issuance: BalanceOf<T>) -> VoteWeight { T::CurrencyToVote::to_vote(Self::slashable_balance_of(stash), issuance) } /// Returns a closure around `slashable_balance_of_vote_weight` that can be passed around. /// /// This prevents call sites from repeatedly requesting `total_issuance` from backend. But it is /// important to be only used while the total issuance is not changing. pub fn slashable_balance_of_fn() -> Box<dyn Fn(&T::AccountId) -> VoteWeight> { // NOTE: changing this to unboxed `impl Fn(..)` return type and the module will still // compile, while some types in mock fail to resolve. let issuance = T::Currency::total_issuance(); Box::new(move |who: &T::AccountId| -> VoteWeight { Self::slashable_balance_of_vote_weight(who, issuance) }) } /// Dump the list of validators and nominators into vectors and keep them on-chain. /// /// This data is used to efficiently evaluate election results. returns `true` if the operation /// is successful. pub fn create_stakers_snapshot() -> (bool, Weight) { let mut consumed_weight = 0; let mut add_db_reads_writes = |reads, writes| { consumed_weight += T::DbWeight::get().reads_writes(reads, writes); }; let validators = <Validators<T>>::iter().map(|(v, _)| v).collect::<Vec<_>>(); let mut nominators = <Nominators<T>>::iter().map(|(n, _)| n).collect::<Vec<_>>(); let num_validators = validators.len(); let num_nominators = nominators.len(); add_db_reads_writes((num_validators + num_nominators) as Weight, 0); if num_validators > MAX_VALIDATORS || num_nominators.saturating_add(num_validators) > MAX_NOMINATORS { log!( warn, "💸 Snapshot size too big [{} <> {}][{} <> {}].", num_validators, MAX_VALIDATORS, num_nominators, MAX_NOMINATORS, ); (false, consumed_weight) } else { // all validators nominate themselves; nominators.extend(validators.clone()); <SnapshotValidators<T>>::put(validators); <SnapshotNominators<T>>::put(nominators); add_db_reads_writes(0, 2); (true, consumed_weight) } } /// Clears both snapshots of stakers. fn kill_stakers_snapshot() { <SnapshotValidators<T>>::kill(); <SnapshotNominators<T>>::kill(); } fn do_payout_stakers( validator_stash: T::AccountId, era: EraIndex, ) -> DispatchResult { // Validate input data let current_era = CurrentEra::get().ok_or(Error::<T>::InvalidEraToReward)?; ensure!(era <= current_era, Error::<T>::InvalidEraToReward); let history_depth = Self::history_depth(); ensure!(era >= current_era.saturating_sub(history_depth), Error::<T>::InvalidEraToReward); // Note: if era has no reward to be claimed, era may be future. better not to update // `ledger.claimed_rewards` in this case. let era_payout = <ErasValidatorReward<T>>::get(&era) .ok_or_else(|| Error::<T>::InvalidEraToReward)?; let controller = Self::bonded(&validator_stash).ok_or(Error::<T>::NotStash)?; let mut ledger = <Ledger<T>>::get(&controller).ok_or_else(|| Error::<T>::NotController)?; ledger.claimed_rewards.retain(|&x| x >= current_era.saturating_sub(history_depth)); match ledger.claimed_rewards.binary_search(&era) { Ok(_) => Err(Error::<T>::AlreadyClaimed)?, Err(pos) => ledger.claimed_rewards.insert(pos, era), } let exposure = <ErasStakersClipped<T>>::get(&era, &ledger.stash); /* Input data seems good, no errors allowed after this point */ <Ledger<T>>::insert(&controller, &ledger); // Get Era reward points. It has TOTAL and INDIVIDUAL // Find the fraction of the era reward that belongs to the validator // Take that fraction of the eras rewards to split to nominator and validator // // Then look at the validator, figure out the proportion of their reward // which goes to them and each of their nominators. let era_reward_points = <ErasRewardPoints<T>>::get(&era); let total_reward_points = era_reward_points.total; let validator_reward_points = era_reward_points.individual.get(&ledger.stash) .map(|points| *points) .unwrap_or_else(|| Zero::zero()); // Nothing to do if they have no reward points. if validator_reward_points.is_zero() { return Ok(())} // This is the fraction of the total reward that the validator and the // nominators will get. let validator_total_reward_part = Perbill::from_rational_approximation( validator_reward_points, total_reward_points, ); // This is how much validator + nominators are entitled to. let validator_total_payout = validator_total_reward_part * era_payout; let validator_prefs = Self::eras_validator_prefs(&era, &validator_stash); // Validator first gets a cut off the top. let validator_commission = validator_prefs.commission; let validator_commission_payout = validator_commission * validator_total_payout; let validator_leftover_payout = validator_total_payout - validator_commission_payout; // Now let's calculate how this is split to the validator. let validator_exposure_part = Perbill::from_rational_approximation( exposure.own, exposure.total, ); let validator_staking_payout = validator_exposure_part * validator_leftover_payout; // We can now make total validator payout: if let Some(imbalance) = Self::make_payout( &ledger.stash, validator_staking_payout + validator_commission_payout ) { Self::deposit_event(RawEvent::Reward(ledger.stash, imbalance.peek())); } // Lets now calculate how this is split to the nominators. // Reward only the clipped exposures. Note this is not necessarily sorted. for nominator in exposure.others.iter() { let nominator_exposure_part = Perbill::from_rational_approximation( nominator.value, exposure.total, ); let nominator_reward: BalanceOf<T> = nominator_exposure_part * validator_leftover_payout; // We can now make nominator payout: if let Some(imbalance) = Self::make_payout(&nominator.who, nominator_reward) { Self::deposit_event(RawEvent::Reward(nominator.who.clone(), imbalance.peek())); } } Ok(()) } /// Update the ledger for a controller. /// /// This will also update the stash lock. fn update_ledger( controller: &T::AccountId, ledger: &StakingLedger<T::AccountId, BalanceOf<T>> ) { T::Currency::set_lock( STAKING_ID, &ledger.stash, ledger.total, WithdrawReasons::all(), ); <Ledger<T>>::insert(controller, ledger); } /// Chill a stash account. fn chill_stash(stash: &T::AccountId) { <Validators<T>>::remove(stash); <Nominators<T>>::remove(stash); } /// Actually make a payment to a staker. This uses the currency's reward function /// to pay the right payee for the given staker account. fn make_payout(stash: &T::AccountId, amount: BalanceOf<T>) -> Option<PositiveImbalanceOf<T>> { let dest = Self::payee(stash); match dest { RewardDestination::Controller => Self::bonded(stash) .and_then(|controller| Some(T::Currency::deposit_creating(&controller, amount)) ), RewardDestination::Stash => T::Currency::deposit_into_existing(stash, amount).ok(), RewardDestination::Staked => Self::bonded(stash) .and_then(|c| Self::ledger(&c).map(|l| (c, l))) .and_then(|(controller, mut l)| { l.active += amount; l.total += amount; let r = T::Currency::deposit_into_existing(stash, amount).ok(); Self::update_ledger(&controller, &l); r }), RewardDestination::Account(dest_account) => { Some(T::Currency::deposit_creating(&dest_account, amount)) } } } /// Plan a new session potentially trigger a new era. fn new_session(session_index: SessionIndex) -> Option<Vec<T::AccountId>> { if let Some(current_era) = Self::current_era() { // Initial era has been set. let current_era_start_session_index = Self::eras_start_session_index(current_era) .unwrap_or_else(|| { fabric_support::print("Error: start_session_index must be set for current_era"); 0 }); let era_length = session_index.checked_sub(current_era_start_session_index) .unwrap_or(0); // Must never happen. match ForceEra::get() { Forcing::ForceNew => ForceEra::kill(), Forcing::ForceAlways => (), Forcing::NotForcing if era_length >= T::SessionsPerEra::get() => (), _ => { // Either `ForceNone`, or `NotForcing && era_length < T::SessionsPerEra::get()`. if era_length + 1 == T::SessionsPerEra::get() { IsCurrentSessionFinal::put(true); } else if era_length >= T::SessionsPerEra::get() { // Should only happen when we are ready to trigger an era but we have ForceNone, // otherwise previous arm would short circuit. Self::close_election_window(); } return None }, } // new era. Self::new_era(session_index) } else { // Set initial era Self::new_era(session_index) } } /// Basic and cheap checks that we perform in validate unsigned, and in the execution. /// /// State reads: ElectionState, CurrentEr, QueuedScore. /// /// This function does weight refund in case of errors, which is based upon the fact that it is /// called at the very beginning of the call site's function. pub fn pre_dispatch_checks(score: ElectionScore, era: EraIndex) -> DispatchResultWithPostInfo { // discard solutions that are not in-time // check window open ensure!( Self::era_election_status().is_open(), Error::<T>::OffchainElectionEarlySubmission.with_weight(T::DbWeight::get().reads(1)), ); // check current era. if let Some(current_era) = Self::current_era() { ensure!( current_era == era, Error::<T>::OffchainElectionEarlySubmission.with_weight(T::DbWeight::get().reads(2)), ) } // assume the given score is valid. Is it better than what we have on-chain, if we have any? if let Some(queued_score) = Self::queued_score() { ensure!( is_score_better(score, queued_score, T::MinSolutionScoreBump::get()), Error::<T>::OffchainElectionWeakSubmission.with_weight(T::DbWeight::get().reads(3)), ) } Ok(None.into()) } /// Checks a given solution and if correct and improved, writes it on chain as the queued result /// of the next round. This may be called by both a signed and an unsigned transaction. pub fn check_and_replace_solution( winners: Vec<ValidatorIndex>, compact_assignments: CompactAssignments, compute: ElectionCompute, claimed_score: ElectionScore, era: EraIndex, election_size: ElectionSize, ) -> DispatchResultWithPostInfo { // Do the basic checks. era, claimed score and window open. let _ = Self::pre_dispatch_checks(claimed_score, era)?; // before we read any further state, we check that the unique targets in compact is same as // compact. is a all in-memory check and easy to do. Moreover, it ensures that the solution // is not full of bogus edges that can cause lots of reads to SlashingSpans. Thus, we can // assume that the storage access of this function is always O(|winners|), not // O(|compact.edge_count()|). ensure!( compact_assignments.unique_targets().len() == winners.len(), Error::<T>::OffchainElectionBogusWinnerCount, ); // Check that the number of presented winners is sane. Most often we have more candidates // than we need. Then it should be `Self::validator_count()`. Else it should be all the // candidates. let snapshot_validators_length = <SnapshotValidators<T>>::decode_len() .map(|l| l as u32) .ok_or_else(|| Error::<T>::SnapshotUnavailable)?; // size of the solution must be correct. ensure!( snapshot_validators_length == u32::from(election_size.validators), Error::<T>::OffchainElectionBogusElectionSize, ); // check the winner length only here and when we know the length of the snapshot validators // length. let desired_winners = Self::validator_count().min(snapshot_validators_length); ensure!(winners.len() as u32 == desired_winners, Error::<T>::OffchainElectionBogusWinnerCount); let snapshot_nominators_len = <SnapshotNominators<T>>::decode_len() .map(|l| l as u32) .ok_or_else(|| Error::<T>::SnapshotUnavailable)?; // rest of the size of the solution must be correct. ensure!( snapshot_nominators_len == election_size.nominators, Error::<T>::OffchainElectionBogusElectionSize, ); // decode snapshot validators. let snapshot_validators = Self::snapshot_validators() .ok_or(Error::<T>::SnapshotUnavailable)?; // check if all winners were legit; this is rather cheap. Replace with accountId. let winners = winners.into_iter().map(|widx| { // NOTE: at the moment, since staking is explicitly blocking any offence until election // is closed, we don't check here if the account id at `snapshot_validators[widx]` is // actually a validator. If this ever changes, this loop needs to also check this. snapshot_validators.get(widx as usize).cloned().ok_or(Error::<T>::OffchainElectionBogusWinner) }).collect::<Result<Vec<T::AccountId>, Error<T>>>()?; // decode the rest of the snapshot. let snapshot_nominators = Self::snapshot_nominators() .ok_or(Error::<T>::SnapshotUnavailable)?; // helpers let nominator_at = |i: NominatorIndex| -> Option<T::AccountId> { snapshot_nominators.get(i as usize).cloned() }; let validator_at = |i: ValidatorIndex| -> Option<T::AccountId> { snapshot_validators.get(i as usize).cloned() }; // un-compact. let assignments = compact_assignments.into_assignment( nominator_at, validator_at, ).map_err(|e| { // log the error since it is not propagated into the runtime error. log!(warn, "💸 un-compacting solution failed due to {:?}", e); Error::<T>::OffchainElectionBogusCompact })?; // check all nominators actually including the claimed vote. Also check correct self votes. // Note that we assume all validators and nominators in `assignments` are properly bonded, // because they are coming from the snapshot via a given index. for Assignment { who, distribution } in assignments.iter() { let is_validator = <Validators<T>>::contains_key(&who); let maybe_nomination = Self::nominators(&who); if !(maybe_nomination.is_some() ^ is_validator) { // all of the indices must map to either a validator or a nominator. If this is ever // not the case, then the locking system of staking is most likely faulty, or we // have bigger problems. log!(error, "💸 detected an error in the staking locking and snapshot."); // abort. return Err(Error::<T>::OffchainElectionBogusNominator.into()); } if !is_validator { // a normal vote let nomination = maybe_nomination.expect( "exactly one of `maybe_validator` and `maybe_nomination.is_some` is true. \ is_validator is false; maybe_nomination is some; qed" ); // NOTE: we don't really have to check here if the sum of all edges are the // nominator correct. Un-compacting assures this by definition. for (t, _) in distribution { // each target in the provided distribution must be actually nominated by the // nominator after the last non-zero slash. if nomination.targets.iter().find(|&tt| tt == t).is_none() { return Err(Error::<T>::OffchainElectionBogusNomination.into()); } if <Self as Store>::SlashingSpans::get(&t).map_or( false, |spans| nomination.submitted_in < spans.last_nonzero_slash(), ) { return Err(Error::<T>::OffchainElectionSlashedNomination.into()); } } } else { // a self vote ensure!(distribution.len() == 1, Error::<T>::OffchainElectionBogusSelfVote); ensure!(distribution[0].0 == *who, Error::<T>::OffchainElectionBogusSelfVote); // defensive only. A compact assignment of length one does NOT encode the weight and // it is always created to be 100%. ensure!( distribution[0].1 == OffchainAccuracy::one(), Error::<T>::OffchainElectionBogusSelfVote, ); } } // convert into staked assignments. let staked_assignments = tp_npos_elections::assignment_ratio_to_staked( assignments, Self::slashable_balance_of_fn(), ); // build the support map thereof in order to evaluate. let supports = to_support_map::<T::AccountId>(&winners, &staked_assignments) .map_err(|_| Error::<T>::OffchainElectionBogusEdge)?; // Check if the score is the same as the claimed one. let submitted_score = (&supports).evaluate(); ensure!(submitted_score == claimed_score, Error::<T>::OffchainElectionBogusScore); // At last, alles Ok. Exposures and store the result. let exposures = Self::collect_exposure(supports); log!( info, "💸 A better solution (with compute {:?} and score {:?}) has been validated and stored on chain.", compute, submitted_score, ); // write new results. <QueuedElected<T>>::put(ElectionResult { elected_stashes: winners, compute, exposures, }); QueuedScore::put(submitted_score); // emit event. Self::deposit_event(RawEvent::SolutionStored(compute)); Ok(None.into()) } /// Start a session potentially starting an era. fn start_session(start_session: SessionIndex) { let next_active_era = Self::active_era().map(|e| e.index + 1).unwrap_or(0); // This is only `Some` when current era has already progressed to the next era, while the // active era is one behind (i.e. in the *last session of the active era*, or *first session // of the new current era*, depending on how you look at it). if let Some(next_active_era_start_session_index) = Self::eras_start_session_index(next_active_era) { if next_active_era_start_session_index == start_session { Self::start_era(start_session); } else if next_active_era_start_session_index < start_session { // This arm should never happen, but better handle it than to stall the staking // noble. fabric_support::print("Warning: A session appears to have been skipped."); Self::start_era(start_session); } } } /// End a session potentially ending an era. fn end_session(session_index: SessionIndex) { if let Some(active_era) = Self::active_era() { if let Some(next_active_era_start_session_index) = Self::eras_start_session_index(active_era.index + 1) { if next_active_era_start_session_index == session_index + 1 { Self::end_era(active_era, session_index); } } } } /// * Increment `active_era.index`, /// * reset `active_era.start`, /// * update `BondedEras` and apply slashes. fn start_era(start_session: SessionIndex) { let active_era = ActiveEra::mutate(|active_era| { let new_index = active_era.as_ref().map(|info| info.index + 1).unwrap_or(0); *active_era = Some(ActiveEraInfo { index: new_index, // Set new active era start in next `on_finalize`. To guarantee usage of `Time` start: None, }); new_index }); let bonding_duration = T::BondingDuration::get(); BondedEras::mutate(|bonded| { bonded.push((active_era, start_session)); if active_era > bonding_duration { let first_kept = active_era - bonding_duration; // prune out everything that's from before the first-kept index. let n_to_prune = bonded.iter() .take_while(|&&(era_idx, _)| era_idx < first_kept) .count(); // kill slashing metadata. for (pruned_era, _) in bonded.drain(..n_to_prune) { slashing::clear_era_metadata::<T>(pruned_era); } if let Some(&(_, first_session)) = bonded.first() { T::SessionInterface::prune_historical_up_to(first_session); } } }); Self::apply_unapplied_slashes(active_era); } /// Compute payout for era. fn end_era(active_era: ActiveEraInfo, _session_index: SessionIndex) { // Note: active_era_start can be None if end era is called during genesis config. if let Some(active_era_start) = active_era.start { let now_as_millis_u64 = T::UnixTime::now().as_millis().saturated_into::<u64>(); let era_duration = now_as_millis_u64 - active_era_start; let (validator_payout, max_payout) = inflation::compute_total_payout( &T::RewardCurve::get(), Self::eras_total_stake(&active_era.index), T::Currency::total_issuance(), // Duration of era; more than u64::MAX is rewarded as u64::MAX. era_duration.saturated_into::<u64>(), ); let rest = max_payout.saturating_sub(validator_payout); Self::deposit_event(RawEvent::EraPayout(active_era.index, validator_payout, rest)); // Set ending era reward. <ErasValidatorReward<T>>::insert(&active_era.index, validator_payout); T::RewardRemainder::on_unbalanced(T::Currency::issue(rest)); } } /// Plan a new era. Return the potential new staking set. fn new_era(start_session_index: SessionIndex) -> Option<Vec<T::AccountId>> { // Increment or set current era. let current_era = CurrentEra::mutate(|s| { *s = Some(s.map(|s| s + 1).unwrap_or(0)); s.unwrap() }); ErasStartSessionIndex::insert(¤t_era, &start_session_index); // Clean old era information. if let Some(old_era) = current_era.checked_sub(Self::history_depth() + 1) { Self::clear_era_information(old_era); } // Set staking information for new era. let maybe_new_validators = Self::select_and_update_validators(current_era); maybe_new_validators } /// Remove all the storage items associated with the election. fn close_election_window() { // Close window. <EraElectionStatus<T>>::put(ElectionStatus::Closed); // Kill snapshots. Self::kill_stakers_snapshot(); // Don't track final session. IsCurrentSessionFinal::put(false); } /// Select the new validator set at the end of the era. /// /// Runs [`try_do_phragmen`] and updates the following storage items: /// - [`EraElectionStatus`]: with `None`. /// - [`ErasStakers`]: with the new staker set. /// - [`ErasStakersClipped`]. /// - [`ErasValidatorPrefs`]. /// - [`ErasTotalStake`]: with the new total stake. /// - [`SnapshotValidators`] and [`SnapshotNominators`] are both removed. /// /// Internally, [`QueuedElected`], snapshots and [`QueuedScore`] are also consumed. /// /// If the election has been successful, It passes the new set upwards. /// /// This should only be called at the end of an era. fn select_and_update_validators(current_era: EraIndex) -> Option<Vec<T::AccountId>> { if let Some(ElectionResult::<T::AccountId, BalanceOf<T>> { elected_stashes, exposures, compute, }) = Self::try_do_election() { // Totally close the election round and data. Self::close_election_window(); // Populate Stakers and write slot stake. let mut total_stake: BalanceOf<T> = Zero::zero(); exposures.into_iter().for_each(|(stash, exposure)| { total_stake = total_stake.saturating_add(exposure.total); <ErasStakers<T>>::insert(current_era, &stash, &exposure); let mut exposure_clipped = exposure; let clipped_max_len = T::MaxNominatorRewardedPerValidator::get() as usize; if exposure_clipped.others.len() > clipped_max_len { exposure_clipped.others.sort_by(|a, b| a.value.cmp(&b.value).reverse()); exposure_clipped.others.truncate(clipped_max_len); } <ErasStakersClipped<T>>::insert(¤t_era, &stash, exposure_clipped); }); // Insert current era staking information <ErasTotalStake<T>>::insert(¤t_era, total_stake); // collect the pref of all winners for stash in &elected_stashes { let pref = Self::validators(stash); <ErasValidatorPrefs<T>>::insert(¤t_era, stash, pref); } // emit event Self::deposit_event(RawEvent::StakingElection(compute)); log!( info, "💸 new validator set of size {:?} has been elected via {:?} for era {:?}", elected_stashes.len(), compute, current_era, ); Some(elected_stashes) } else { None } } /// Select a new validator set from the assembled stakers and their role preferences. It tries /// first to peek into [`QueuedElected`]. Otherwise, it runs a new on-chain phragmen election. /// /// If [`QueuedElected`] and [`QueuedScore`] exists, they are both removed. No further storage /// is updated. fn try_do_election() -> Option<ElectionResult<T::AccountId, BalanceOf<T>>> { // an election result from either a stored submission or locally executed one. let next_result = <QueuedElected<T>>::take().or_else(|| Self::do_on_chain_phragmen() ); // either way, kill this. We remove it here to make sure it always has the exact same // lifetime as `QueuedElected`. QueuedScore::kill(); next_result } /// Execute election and return the new results. The edge weights are processed into support /// values. /// /// This is basically a wrapper around [`Self::do_phragmen`] which translates /// `PrimitiveElectionResult` into `ElectionResult`. /// /// No storage item is updated. pub fn do_on_chain_phragmen() -> Option<ElectionResult<T::AccountId, BalanceOf<T>>> { if let Some(phragmen_result) = Self::do_phragmen::<ChainAccuracy>(0) { let elected_stashes = phragmen_result.winners.iter() .map(|(s, _)| s.clone()) .collect::<Vec<T::AccountId>>(); let assignments = phragmen_result.assignments; let staked_assignments = tp_npos_elections::assignment_ratio_to_staked( assignments, Self::slashable_balance_of_fn(), ); let supports = to_support_map::<T::AccountId>( &elected_stashes, &staked_assignments, ) .map_err(|_| log!( error, "💸 on-chain phragmen is failing due to a problem in the result. This must be a bug." ) ) .ok()?; // collect exposures let exposures = Self::collect_exposure(supports); // In order to keep the property required by `on_session_ending` that we must return the // new validator set even if it's the same as the old, as long as any underlying // economic conditions have changed, we don't attempt to do any optimization where we // compare against the prior set. Some(ElectionResult::<T::AccountId, BalanceOf<T>> { elected_stashes, exposures, compute: ElectionCompute::OnChain, }) } else { // There were not enough candidates for even our minimal level of functionality. This is // bad. We should probably disable all functionality except for block production and let // the chain keep producing blocks until we can decide on a sufficiently substantial // set. TODO: #2494 None } } /// Execute phragmen election and return the new results. No post-processing is applied and the /// raw edge weights are returned. /// /// Self votes are added and nominations before the most recent slashing span are ignored. /// /// No storage item is updated. pub fn do_phragmen<Accuracy: PerThing128>( iterations: usize, ) -> Option<PrimitiveElectionResult<T::AccountId, Accuracy>> { let weight_of = Self::slashable_balance_of_fn(); let mut all_nominators: Vec<(T::AccountId, VoteWeight, Vec<T::AccountId>)> = Vec::new(); let mut all_validators = Vec::new(); for (validator, _) in <Validators<T>>::iter() { // append self vote let self_vote = (validator.clone(), weight_of(&validator), vec![validator.clone()]); all_nominators.push(self_vote); all_validators.push(validator); } let nominator_votes = <Nominators<T>>::iter().map(|(nominator, nominations)| { let Nominations { submitted_in, mut targets, suppressed: _ } = nominations; // Filter out nomination targets which were nominated before the most recent // slashing span. targets.retain(|stash| { <Self as Store>::SlashingSpans::get(&stash).map_or( true, |spans| submitted_in >= spans.last_nonzero_slash(), ) }); (nominator, targets) }); all_nominators.extend(nominator_votes.map(|(n, ns)| { let s = weight_of(&n); (n, s, ns) })); if all_validators.len() < Self::minimum_validator_count().max(1) as usize { // If we don't have enough candidates, nothing to do. log!( warn, "💸 Chain does not have enough staking candidates to operate. Era {:?}.", Self::current_era() ); None } else { seq_phragmen::<_, Accuracy>( Self::validator_count() as usize, all_validators, all_nominators, Some((iterations, 0)), // exactly run `iterations` rounds. ) .map_err(|err| log!(error, "Call to seq-phragmen failed due to {:?}", err)) .ok() } } /// Consume a set of [`Supports`] from [`tp_npos_elections`] and collect them into a [`Exposure`] fn collect_exposure( supports: SupportMap<T::AccountId>, ) -> Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)> { let total_issuance = T::Currency::total_issuance(); let to_currency = |e: ExtendedBalance| T::CurrencyToVote::to_currency(e, total_issuance); supports.into_iter().map(|(validator, support)| { // build `struct exposure` from `support` let mut others = Vec::with_capacity(support.voters.len()); let mut own: BalanceOf<T> = Zero::zero(); let mut total: BalanceOf<T> = Zero::zero(); support.voters .into_iter() .map(|(nominator, weight)| (nominator, to_currency(weight))) .for_each(|(nominator, stake)| { if nominator == validator { own = own.saturating_add(stake); } else { others.push(IndividualExposure { who: nominator, value: stake }); } total = total.saturating_add(stake); }); let exposure = Exposure { own, others, total, }; (validator, exposure) }).collect::<Vec<(T::AccountId, Exposure<_, _>)>>() } /// Remove all associated data of a stash account from the staking system. /// /// Assumes storage is upgraded before calling. /// /// This is called: /// - after a `withdraw_unbond()` call that frees all of a stash's bonded balance. /// - through `reap_stash()` if the balance has fallen to zero (through slashing). fn kill_stash(stash: &T::AccountId, num_slashing_spans: u32) -> DispatchResult { let controller = <Bonded<T>>::get(stash).ok_or(Error::<T>::NotStash)?; slashing::clear_stash_metadata::<T>(stash, num_slashing_spans)?; <Bonded<T>>::remove(stash); <Ledger<T>>::remove(&controller); <Payee<T>>::remove(stash); <Validators<T>>::remove(stash); <Nominators<T>>::remove(stash); system::Module::<T>::dec_consumers(stash); Ok(()) } /// Clear all era information for given era. fn clear_era_information(era_index: EraIndex) { <ErasStakers<T>>::remove_prefix(era_index); <ErasStakersClipped<T>>::remove_prefix(era_index); <ErasValidatorPrefs<T>>::remove_prefix(era_index); <ErasValidatorReward<T>>::remove(era_index); <ErasRewardPoints<T>>::remove(era_index); <ErasTotalStake<T>>::remove(era_index); ErasStartSessionIndex::remove(era_index); } /// Apply previously-unapplied slashes on the beginning of a new era, after a delay. fn apply_unapplied_slashes(active_era: EraIndex) { let slash_defer_duration = T::SlashDeferDuration::get(); <Self as Store>::EarliestUnappliedSlash::mutate(|earliest| if let Some(ref mut earliest) = earliest { let keep_from = active_era.saturating_sub(slash_defer_duration); for era in (*earliest)..keep_from { let era_slashes = <Self as Store>::UnappliedSlashes::take(&era); for slash in era_slashes { slashing::apply_slash::<T>(slash); } } *earliest = (*earliest).max(keep_from) }) } /// Add reward points to validators using their stash account ID. /// /// Validators are keyed by stash account ID and must be in the current elected set. /// /// For each element in the iterator the given number of points in u32 is added to the /// validator, thus duplicates are handled. /// /// At the end of the era each the total payout will be distributed among validator /// relatively to their points. /// /// COMPLEXITY: Complexity is `number_of_validator_to_reward x current_elected_len`. /// If you need to reward lots of validator consider using `reward_by_indices`. pub fn reward_by_ids( validators_points: impl IntoIterator<Item = (T::AccountId, u32)> ) { if let Some(active_era) = Self::active_era() { <ErasRewardPoints<T>>::mutate(active_era.index, |era_rewards| { for (validator, points) in validators_points.into_iter() { *era_rewards.individual.entry(validator).or_default() += points; era_rewards.total += points; } }); } } /// Ensures that at the end of the current session there will be a new era. fn ensure_new_era() { match ForceEra::get() { Forcing::ForceAlways | Forcing::ForceNew => (), _ => ForceEra::put(Forcing::ForceNew), } } fn will_era_be_forced() -> bool { match ForceEra::get() { Forcing::ForceAlways | Forcing::ForceNew => true, Forcing::ForceNone | Forcing::NotForcing => false, } } #[cfg(feature = "runtime-benchmarks")] pub fn add_era_stakers(current_era: EraIndex, controller: T::AccountId, exposure: Exposure<T::AccountId, BalanceOf<T>>) { <ErasStakers<T>>::insert(¤t_era, &controller, &exposure); } #[cfg(feature = "runtime-benchmarks")] pub fn put_election_status(status: ElectionStatus::<T::BlockNumber>) { <EraElectionStatus<T>>::put(status); } #[cfg(feature = "runtime-benchmarks")] pub fn set_slash_reward_fraction(fraction: Perbill) { SlashRewardFraction::put(fraction); } } /// In this implementation `new_session(session)` must be called before `end_session(session-1)` /// i.e. the new session must be planned before the ending of the previous session. /// /// Once the first new_session is planned, all session must start and then end in order, though /// some session can lag in between the newest session planned and the latest session started. impl<T: Config> noble_session::SessionManager<T::AccountId> for Module<T> { fn new_session(new_index: SessionIndex) -> Option<Vec<T::AccountId>> { fabric_support::debug::native::trace!( target: LOG_TARGET, "[{}] planning new_session({})", <fabric_system::Module<T>>::block_number(), new_index ); Self::new_session(new_index) } fn start_session(start_index: SessionIndex) { fabric_support::debug::native::trace!( target: LOG_TARGET, "[{}] starting start_session({})", <fabric_system::Module<T>>::block_number(), start_index ); Self::start_session(start_index) } fn end_session(end_index: SessionIndex) { fabric_support::debug::native::trace!( target: LOG_TARGET, "[{}] ending end_session({})", <fabric_system::Module<T>>::block_number(), end_index ); Self::end_session(end_index) } } impl<T: Config> historical::SessionManager<T::AccountId, Exposure<T::AccountId, BalanceOf<T>>> for Module<T> { fn new_session(new_index: SessionIndex) -> Option<Vec<(T::AccountId, Exposure<T::AccountId, BalanceOf<T>>)>> { <Self as noble_session::SessionManager<_>>::new_session(new_index).map(|validators| { let current_era = Self::current_era() // Must be some as a new era has been created. .unwrap_or(0); validators.into_iter().map(|v| { let exposure = Self::eras_stakers(current_era, &v); (v, exposure) }).collect() }) } fn start_session(start_index: SessionIndex) { <Self as noble_session::SessionManager<_>>::start_session(start_index) } fn end_session(end_index: SessionIndex) { <Self as noble_session::SessionManager<_>>::end_session(end_index) } } /// Add reward points to block authors: /// * 20 points to the block producer for producing a (non-uncle) block in the relay chain, /// * 2 points to the block producer for each reference to a previously unreferenced uncle, and /// * 1 point to the producer of each referenced uncle block. impl<T> noble_authorship::EventHandler<T::AccountId, T::BlockNumber> for Module<T> where T: Config + noble_authorship::Config + noble_session::Config { fn note_author(author: T::AccountId) { Self::reward_by_ids(vec![(author, 20)]) } fn note_uncle(author: T::AccountId, _age: T::BlockNumber) { Self::reward_by_ids(vec![ (<noble_authorship::Module<T>>::author(), 2), (author, 1) ]) } } /// A `Convert` implementation that finds the stash of the given controller account, /// if any. pub struct StashOf<T>(tetcore_std::marker::PhantomData<T>); impl<T: Config> Convert<T::AccountId, Option<T::AccountId>> for StashOf<T> { fn convert(controller: T::AccountId) -> Option<T::AccountId> { <Module<T>>::ledger(&controller).map(|l| l.stash) } } /// A typed conversion from stash account ID to the active exposure of nominators /// on that account. /// /// Active exposure is the exposure of the validator set currently validating, i.e. in /// `active_era`. It can differ from the latest planned exposure in `current_era`. pub struct ExposureOf<T>(tetcore_std::marker::PhantomData<T>); impl<T: Config> Convert<T::AccountId, Option<Exposure<T::AccountId, BalanceOf<T>>>> for ExposureOf<T> { fn convert(validator: T::AccountId) -> Option<Exposure<T::AccountId, BalanceOf<T>>> { if let Some(active_era) = <Module<T>>::active_era() { Some(<Module<T>>::eras_stakers(active_era.index, &validator)) } else { None } } } /// This is intended to be used with `FilterHistoricalOffences`. impl <T: Config> OnOffenceHandler<T::AccountId, noble_session::historical::IdentificationTuple<T>, Weight> for Module<T> where T: noble_session::Config<ValidatorId = <T as fabric_system::Config>::AccountId>, T: noble_session::historical::Config< FullIdentification = Exposure<<T as fabric_system::Config>::AccountId, BalanceOf<T>>, FullIdentificationOf = ExposureOf<T>, >, T::SessionHandler: noble_session::SessionHandler<<T as fabric_system::Config>::AccountId>, T::SessionManager: noble_session::SessionManager<<T as fabric_system::Config>::AccountId>, T::ValidatorIdOf: Convert< <T as fabric_system::Config>::AccountId, Option<<T as fabric_system::Config>::AccountId>, >, { fn on_offence( offenders: &[OffenceDetails<T::AccountId, noble_session::historical::IdentificationTuple<T>>], slash_fraction: &[Perbill], slash_session: SessionIndex, ) -> Result<Weight, ()> { if !Self::can_report() { return Err(()) } let reward_proportion = SlashRewardFraction::get(); let mut consumed_weight: Weight = 0; let mut add_db_reads_writes = |reads, writes| { consumed_weight += T::DbWeight::get().reads_writes(reads, writes); }; let active_era = { let active_era = Self::active_era(); add_db_reads_writes(1, 0); if active_era.is_none() { // this offence need not be re-submitted. return Ok(consumed_weight) } active_era.expect("value checked not to be `None`; qed").index }; let active_era_start_session_index = Self::eras_start_session_index(active_era) .unwrap_or_else(|| { fabric_support::print("Error: start_session_index must be set for current_era"); 0 }); add_db_reads_writes(1, 0); let window_start = active_era.saturating_sub(T::BondingDuration::get()); // fast path for active-era report - most likely. // `slash_session` cannot be in a future active era. It must be in `active_era` or before. let slash_era = if slash_session >= active_era_start_session_index { active_era } else { let eras = BondedEras::get(); add_db_reads_writes(1, 0); // reverse because it's more likely to find reports from recent eras. match eras.iter().rev().filter(|&&(_, ref sesh)| sesh <= &slash_session).next() { Some(&(ref slash_era, _)) => *slash_era, // before bonding period. defensive - should be filtered out. None => return Ok(consumed_weight), } }; <Self as Store>::EarliestUnappliedSlash::mutate(|earliest| { if earliest.is_none() { *earliest = Some(active_era) } }); add_db_reads_writes(1, 1); let slash_defer_duration = T::SlashDeferDuration::get(); let invulnerables = Self::invulnerables(); add_db_reads_writes(1, 0); for (details, slash_fraction) in offenders.iter().zip(slash_fraction) { let (stash, exposure) = &details.offender; // Skip if the validator is invulnerable. if invulnerables.contains(stash) { continue } let unapplied = slashing::compute_slash::<T>(slashing::SlashParams { stash, slash: *slash_fraction, exposure, slash_era, window_start, now: active_era, reward_proportion, }); if let Some(mut unapplied) = unapplied { let nominators_len = unapplied.others.len() as u64; let reporters_len = details.reporters.len() as u64; { let upper_bound = 1 /* Validator/NominatorSlashInEra */ + 2 /* fetch_spans */; let rw = upper_bound + nominators_len * upper_bound; add_db_reads_writes(rw, rw); } unapplied.reporters = details.reporters.clone(); if slash_defer_duration == 0 { // apply right away. slashing::apply_slash::<T>(unapplied); { let slash_cost = (6, 5); let reward_cost = (2, 2); add_db_reads_writes( (1 + nominators_len) * slash_cost.0 + reward_cost.0 * reporters_len, (1 + nominators_len) * slash_cost.1 + reward_cost.1 * reporters_len ); } } else { // defer to end of some `slash_defer_duration` from now. <Self as Store>::UnappliedSlashes::mutate( active_era, move |for_later| for_later.push(unapplied), ); add_db_reads_writes(1, 1); } } else { add_db_reads_writes(4 /* fetch_spans */, 5 /* kick_out_if_recent */) } } Ok(consumed_weight) } fn can_report() -> bool { Self::era_election_status().is_closed() } } /// Filter historical offences out and only allow those from the bonding period. pub struct FilterHistoricalOffences<T, R> { _inner: tetcore_std::marker::PhantomData<(T, R)>, } impl<T, Reporter, Offender, R, O> ReportOffence<Reporter, Offender, O> for FilterHistoricalOffences<Module<T>, R> where T: Config, R: ReportOffence<Reporter, Offender, O>, O: Offence<Offender>, { fn report_offence(reporters: Vec<Reporter>, offence: O) -> Result<(), OffenceError> { // disallow any slashing from before the current bonding period. let offence_session = offence.session_index(); let bonded_eras = BondedEras::get(); if bonded_eras.first().filter(|(_, start)| offence_session >= *start).is_some() { R::report_offence(reporters, offence) } else { <Module<T>>::deposit_event( RawEvent::OldSlashingReportDiscarded(offence_session) ); Ok(()) } } fn is_known_offence(offenders: &[Offender], time_slot: &O::TimeSlot) -> bool { R::is_known_offence(offenders, time_slot) } } #[allow(deprecated)] impl<T: Config> fabric_support::unsigned::ValidateUnsigned for Module<T> { type Call = Call<T>; fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity { if let Call::submit_election_solution_unsigned( _, _, score, era, _, ) = call { use offchain_election::DEFAULT_LONGEVITY; // discard solution not coming from the local OCW. match source { TransactionSource::Local | TransactionSource::InBlock => { /* allowed */ } _ => { log!(debug, "rejecting unsigned transaction because it is not local/in-block."); return InvalidTransaction::Call.into(); } } if let Err(error_with_post_info) = Self::pre_dispatch_checks(*score, *era) { let invalid = to_invalid(error_with_post_info); log!( debug, "💸 validate unsigned pre dispatch checks failed due to error #{:?}.", invalid, ); return invalid.into(); } log!(debug, "💸 validateUnsigned succeeded for a solution at era {}.", era); ValidTransaction::with_tag_prefix("StakingOffchain") // The higher the score[0], the better a solution is. .priority(T::UnsignedPriority::get().saturating_add(score[0].saturated_into())) // Defensive only. A single solution can exist in the pool per era. Each validator // will run OCW at most once per era, hence there should never exist more than one // transaction anyhow. .and_provides(era) // Note: this can be more accurate in the future. We do something like // `era_end_block - current_block` but that is not needed now as we eagerly run // offchain workers now and the above should be same as `T::ElectionLookahead` // without the need to query more storage in the validation phase. If we randomize // offchain worker, then we might re-consider this. .longevity(TryInto::<u64>::try_into( T::ElectionLookahead::get()).unwrap_or(DEFAULT_LONGEVITY) ) // We don't propagate this. This can never the validated at a remote node. .propagate(false) .build() } else { InvalidTransaction::Call.into() } } fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> { if let Call::submit_election_solution_unsigned( _, _, score, era, _, ) = call { // IMPORTANT NOTE: These checks are performed in the dispatch call itself, yet we need // to duplicate them here to prevent a block producer from putting a previously // validated, yet no longer valid solution on chain. // OPTIMISATION NOTE: we could skip this in the `submit_election_solution_unsigned` // since we already do it here. The signed version needs it though. Yer for now we keep // this duplicate check here so both signed and unsigned can use a singular // `check_and_replace_solution`. Self::pre_dispatch_checks(*score, *era) .map(|_| ()) .map_err(to_invalid) .map_err(Into::into) } else { Err(InvalidTransaction::Call.into()) } } } /// Check that list is sorted and has no duplicates. fn is_sorted_and_unique(list: &[u32]) -> bool { list.windows(2).all(|w| w[0] < w[1]) } /// convert a DispatchErrorWithPostInfo to a custom InvalidTransaction with the inner code being the /// error number. fn to_invalid(error_with_post_info: DispatchErrorWithPostInfo) -> InvalidTransaction { let error = error_with_post_info.error; let error_number = match error { DispatchError::Module { error, ..} => error, _ => 0, }; InvalidTransaction::Custom(error_number) }