diff --git a/README.md b/README.md index 2170507..88bfb2a 100644 --- a/README.md +++ b/README.md @@ -1,167 +1,179 @@ -# Zcash Sustainaiblity Fund simulator +# Network Sustainability Mechanism simulator -This short Rust program simulates Zcash Block Rewards (aka Block Subsidies) assuming Zcash Sustainability Fund backed smoothing out of the issuance curve will be implemented (https://github.com/zcash/zips/pull/706). +This short Rust program simulates Zcash Block Subsidies, assuming that smoothing of the +issuance curve in line with the Network Sustainability Mechanism will be implemented as +specified in [ZIP 234](https://zips.z.cash/zip-0234). ## Running -### Devenv -You can use [devenv.sh](https://devenv.sh/) to setup a development environment. Otherwise follow the instructions below. - ### Manual setup -To run the program, you need to have Rust installed. You'll also need some extra libraries to enable plotting, on Ubuntu do: +To run the program, you need to have Rust installed. You'll also need some extra libraries +to enable plotting, on Ubuntu do: ``` sudo apt install pkg-config libfreetype6-dev libfontconfig1-dev ``` - Then, clone this repository and run: +Then, clone this repository and run: ``` cargo run ``` +## Disclaimer + +This fork of Eiger's "[ZSF simulator](https://github.com/eigerco/zsf-simulator)" has been +significantly modified by me, Daira-Emma Hopwood. Any errors in the interpretation of +ZIP 234 are my own. + ## Output -The program will output the final block that subsidies will be paid out from: +The program will output the final block that subsidies would be paid out from +according to ZIP 234: ``` -Last block is 47917869 in ~113.88 years -Final block subsidy: 1 (~0 ZEC) -Final ZSF balance: 0 (~0 ZEC) +DEPLOYMENT_BLOCK_HEIGHT = 3662980 (2.23 years after 2nd halving) +Last block with non-zero subsidy is at height 50788958 in ~114.23 years after the 2nd halving. +Final block subsidy: 1 (~0.00000001 ZEC) +Final NSM balance: 0 (~0 ZEC) ``` -As well as a summary of subsidies paid out during 4 year periods in between halvings in comparison with the current -issuance scheme: +It also gives a summary of subsidies that would be paid out during 4-year periods +after NSM deployment in comparison with the current issuance scheme. (Note that +these periods do not line up with halving periods under the current scheme.) ``` -Halvings: -Halving 1 at block 1680000: - ZSF subsidies: 262523884819889 (~ 2625238.848 ZEC, 1.563 ZEC per block) - legacy subsidies: 262500000000000 (~ 2625000.000 ZEC, 1.562 ZEC per block) - difference: 23884819889 (~ 238 ZEC), ZSF/legacy: 1.0001 -Halving 2 at block 3360000: - ZSF subsidies: 131259082555273 (~ 1312590.826 ZEC, 0.781 ZEC per block) - legacy subsidies: 131250000000000 (~ 1312500.000 ZEC, 0.781 ZEC per block) - difference: 9082555273 (~ 90 ZEC), ZSF/legacy: 1.0001 -Halving 3 at block 5040000: - ZSF subsidies: 65628165536456 (~ 656281.655 ZEC, 0.391 ZEC per block) - legacy subsidies: 65625000000000 (~ 656250.000 ZEC, 0.391 ZEC per block) - difference: 3165536456 (~ 31 ZEC), ZSF/legacy: 1.0000 -Halving 4 at block 6720000: - ZSF subsidies: 32813394913130 (~ 328133.949 ZEC, 0.195 ZEC per block) - legacy subsidies: 32812500000000 (~ 328125.000 ZEC, 0.195 ZEC per block) - difference: 894913130 (~ 8 ZEC), ZSF/legacy: 1.0000 -Halving 5 at block 8400000: - ZSF subsidies: 16406353535358 (~ 164063.535 ZEC, 0.098 ZEC per block) - legacy subsidies: 16406250000000 (~ 164062.500 ZEC, 0.098 ZEC per block) - difference: 103535358 (~ 1 ZEC), ZSF/legacy: 1.0000 -Halving 6 at block 10080000: - ZSF subsidies: 8203004810267 (~ 82030.048 ZEC, 0.049 ZEC per block) - legacy subsidies: 8203124160000 (~ 82031.242 ZEC, 0.049 ZEC per block) - difference: -119349733 (~ -1 ZEC), ZSF/legacy: 1.0000 -Halving 7 at block 11760000: - ZSF subsidies: 4101416429306 (~ 41014.164 ZEC, 0.024 ZEC per block) - legacy subsidies: 4101562080000 (~ 41015.621 ZEC, 0.024 ZEC per block) - difference: -145650694 (~ -1 ZEC), ZSF/legacy: 1.0000 -Halving 8 at block 13440000: - ZSF subsidies: 2050665226986 (~ 20506.652 ZEC, 0.012 ZEC per block) - legacy subsidies: 2050781040000 (~ 20507.810 ZEC, 0.012 ZEC per block) - difference: -115813014 (~ -1 ZEC), ZSF/legacy: 0.9999 -Halving 9 at block 15120000: - ZSF subsidies: 1025311119937 (~ 10253.111 ZEC, 0.006 ZEC per block) - legacy subsidies: 1025389680000 (~ 10253.897 ZEC, 0.006 ZEC per block) - difference: -78560063 (~ 0 ZEC), ZSF/legacy: 0.9999 -Halving 10 at block 16800000: - ZSF subsidies: 512644813724 (~ 5126.448 ZEC, 0.003 ZEC per block) - legacy subsidies: 512694000000 (~ 5126.940 ZEC, 0.003 ZEC per block) - difference: -49186276 (~ 0 ZEC), ZSF/legacy: 0.9999 -Halving 11 at block 18480000: - ZSF subsidies: 256317033772 (~ 2563.170 ZEC, 0.002 ZEC per block) - legacy subsidies: 256346160000 (~ 2563.462 ZEC, 0.002 ZEC per block) - difference: -29126228 (~ 0 ZEC), ZSF/legacy: 0.9999 -Halving 12 at block 20160000: - ZSF subsidies: 128155830300 (~ 1281.558 ZEC, 0.001 ZEC per block) - legacy subsidies: 128172240000 (~ 1281.722 ZEC, 0.001 ZEC per block) - difference: -16409700 (~ 0 ZEC), ZSF/legacy: 0.9999 -Halving 13 at block 21840000: - ZSF subsidies: 64076572065 (~ 640.766 ZEC, 0.000 ZEC per block) - legacy subsidies: 64085280000 (~ 640.853 ZEC, 0.000 ZEC per block) - difference: -8707935 (~ 0 ZEC), ZSF/legacy: 0.9999 -Halving 14 at block 23520000: - ZSF subsidies: 32037614417 (~ 320.376 ZEC, 0.000 ZEC per block) - legacy subsidies: 32042640000 (~ 320.426 ZEC, 0.000 ZEC per block) - difference: -5025583 (~ 0 ZEC), ZSF/legacy: 0.9998 -Halving 15 at block 25200000: - ZSF subsidies: 16018471375 (~ 160.185 ZEC, 0.000 ZEC per block) - legacy subsidies: 16020480000 (~ 160.205 ZEC, 0.000 ZEC per block) - difference: -2008625 (~ 0 ZEC), ZSF/legacy: 0.9999 -Halving 16 at block 26880000: - ZSF subsidies: 8009067846 (~ 80.091 ZEC, 0.000 ZEC per block) - legacy subsidies: 8010240000 (~ 80.102 ZEC, 0.000 ZEC per block) - difference: -1172154 (~ 0 ZEC), ZSF/legacy: 0.9999 -Halving 17 at block 28560000: - ZSF subsidies: 4004449844 (~ 40.044 ZEC, 0.000 ZEC per block) - legacy subsidies: 4005120000 (~ 40.051 ZEC, 0.000 ZEC per block) - difference: -670156 (~ 0 ZEC), ZSF/legacy: 0.9998 -Halving 18 at block 30240000: - ZSF subsidies: 2002182886 (~ 20.022 ZEC, 0.000 ZEC per block) - legacy subsidies: 2002560000 (~ 20.026 ZEC, 0.000 ZEC per block) - difference: -377114 (~ 0 ZEC), ZSF/legacy: 0.9998 -Halving 19 at block 31920000: - ZSF subsidies: 1001070275 (~ 10.011 ZEC, 0.000 ZEC per block) - legacy subsidies: 1001280000 (~ 10.013 ZEC, 0.000 ZEC per block) - difference: -209725 (~ 0 ZEC), ZSF/legacy: 0.9998 -Halving 20 at block 33600000: - ZSF subsidies: 500525701 (~ 5.005 ZEC, 0.000 ZEC per block) - legacy subsidies: 500640000 (~ 5.006 ZEC, 0.000 ZEC per block) - difference: -114299 (~ 0 ZEC), ZSF/legacy: 0.9998 -Halving 21 at block 35280000: - ZSF subsidies: 250255480 (~ 2.503 ZEC, 0.000 ZEC per block) - legacy subsidies: 250320000 (~ 2.503 ZEC, 0.000 ZEC per block) - difference: -64520 (~ 0 ZEC), ZSF/legacy: 0.9997 -Halving 22 at block 36960000: - ZSF subsidies: 125126575 (~ 1.251 ZEC, 0.000 ZEC per block) - legacy subsidies: 124320000 (~ 1.243 ZEC, 0.000 ZEC per block) - difference: 806575 (~ 0 ZEC), ZSF/legacy: 1.0065 -Halving 23 at block 38640000: - ZSF subsidies: 62556140 (~ 0.626 ZEC, 0.000 ZEC per block) - legacy subsidies: 62160000 (~ 0.622 ZEC, 0.000 ZEC per block) - difference: 396140 (~ 0 ZEC), ZSF/legacy: 1.0064 -Halving 24 at block 40320000: - ZSF subsidies: 31271589 (~ 0.313 ZEC, 0.000 ZEC per block) - legacy subsidies: 30240000 (~ 0.302 ZEC, 0.000 ZEC per block) - difference: 1031589 (~ 0 ZEC), ZSF/legacy: 1.0341 -Halving 25 at block 42000000: - ZSF subsidies: 15660304 (~ 0.157 ZEC, 0.000 ZEC per block) - legacy subsidies: 15120000 (~ 0.151 ZEC, 0.000 ZEC per block) - difference: 540304 (~ 0 ZEC), ZSF/legacy: 1.0357 -Halving 26 at block 43680000: - ZSF subsidies: 7766952 (~ 0.078 ZEC, 0.000 ZEC per block) - legacy subsidies: 6720000 (~ 0.067 ZEC, 0.000 ZEC per block) - difference: 1046952 (~ 0 ZEC), ZSF/legacy: 1.1558 -Halving 27 at block 45360000: - ZSF subsidies: 3962388 (~ 0.040 ZEC, 0.000 ZEC per block) - legacy subsidies: 3360000 (~ 0.034 ZEC, 0.000 ZEC per block) - difference: 602388 (~ 0 ZEC), ZSF/legacy: 1.1793 -Halving 28 at block 47040000: - ZSF subsidies: 1814216 (~ 0.018 ZEC, 0.000 ZEC per block) - legacy subsidies: 1680000 (~ 0.017 ZEC, 0.000 ZEC per block) - difference: 134216 (~ 0 ZEC), ZSF/legacy: 1.0799 +Four-year periods: +Years 0.. 4 at heights 3662980.. 5342980: + NSM subsidies: 189352008318309 (~ 1893520.083 ZEC, 1.12709529 ZEC per block) + no-NSM subsidies: 189329765625000 (~ 1893297.656 ZEC, 1.12696289 ZEC per block) + difference: 22242693309 (~ 222.427 ZEC), NSM/no-NSM: 1.0001 +Years 4.. 8 at heights 5342980.. 7022980: + NSM subsidies: 94673941416154 (~ 946739.414 ZEC, 0.56353537 ZEC per block) + no-NSM subsidies: 94664804687500 (~ 946648.047 ZEC, 0.56348098 ZEC per block) + difference: 9136728654 (~ 91.367 ZEC), NSM/no-NSM: 1.0001 +Years 8.. 12 at heights 7022980.. 8702980: + NSM subsidies: 47335978420161 (~ 473359.784 ZEC, 0.28176178 ZEC per block) + no-NSM subsidies: 47332402343750 (~ 473324.023 ZEC, 0.28174049 ZEC per block) + difference: 3576076411 (~ 35.761 ZEC), NSM/no-NSM: 1.0001 +Years 12.. 16 at heights 8702980..10382980: + NSM subsidies: 23667493076937 (~ 236674.931 ZEC, 0.14087793 ZEC per block) + no-NSM subsidies: 23666201171875 (~ 236662.012 ZEC, 0.14087025 ZEC per block) + difference: 1291905062 (~ 12.919 ZEC), NSM/no-NSM: 1.0001 +Years 16.. 20 at heights 10382980..12062980: + NSM subsidies: 11833498476344 (~ 118334.985 ZEC, 0.07043749 ZEC per block) + no-NSM subsidies: 11833100117647 (~ 118331.001 ZEC, 0.07043512 ZEC per block) + difference: 398358697 (~ 3.984 ZEC), NSM/no-NSM: 1.0000 +Years 20.. 24 at heights 12062980..13742980: + NSM subsidies: 5916625209792 (~ 59166.252 ZEC, 0.03521801 ZEC per block) + no-NSM subsidies: 5916549687114 (~ 59165.497 ZEC, 0.03521756 ZEC per block) + difference: 75522678 (~ 0.755 ZEC), NSM/no-NSM: 1.0000 +Years 24.. 28 at heights 13742980..15422980: + NSM subsidies: 2958250592564 (~ 29582.506 ZEC, 0.01760863 ZEC per block) + no-NSM subsidies: 2958274843557 (~ 29582.748 ZEC, 0.01760878 ZEC per block) + difference: -24250993 (~ -0.243 ZEC), NSM/no-NSM: 1.0000 +Years 28.. 32 at heights 15422980..17102980: + NSM subsidies: 1479094290332 (~ 14790.943 ZEC, 0.00880413 ZEC per block) + no-NSM subsidies: 1479136953488 (~ 14791.370 ZEC, 0.00880439 ZEC per block) + difference: -42663156 (~ -0.427 ZEC), NSM/no-NSM: 1.0000 +Years 32.. 36 at heights 17102980..18782980: + NSM subsidies: 739531642726 (~ 7395.316 ZEC, 0.00440197 ZEC per block) + no-NSM subsidies: 739567636744 (~ 7395.676 ZEC, 0.00440219 ZEC per block) + difference: -35994018 (~ -0.360 ZEC), NSM/no-NSM: 1.0000 +Years 36.. 40 at heights 18782980..20462980: + NSM subsidies: 369758070192 (~ 3697.581 ZEC, 0.00220094 ZEC per block) + no-NSM subsidies: 369782978372 (~ 3697.830 ZEC, 0.00220109 ZEC per block) + difference: -24908180 (~ -0.249 ZEC), NSM/no-NSM: 0.9999 +Years 40.. 44 at heights 20462980..22142980: + NSM subsidies: 184875159489 (~ 1848.752 ZEC, 0.00110045 ZEC per block) + no-NSM subsidies: 184890649186 (~ 1848.906 ZEC, 0.00110054 ZEC per block) + difference: -15489697 (~ -0.155 ZEC), NSM/no-NSM: 0.9999 +Years 44.. 48 at heights 22142980..23822980: + NSM subsidies: 92435642203 (~ 924.356 ZEC, 0.00055021 ZEC per block) + no-NSM subsidies: 92444484593 (~ 924.445 ZEC, 0.00055026 ZEC per block) + difference: -8842390 (~ -0.088 ZEC), NSM/no-NSM: 0.9999 +Years 48.. 52 at heights 23822980..25502980: + NSM subsidies: 46216852296 (~ 462.169 ZEC, 0.00027510 ZEC per block) + no-NSM subsidies: 46221870587 (~ 462.219 ZEC, 0.00027513 ZEC per block) + difference: -5018291 (~ -0.050 ZEC), NSM/no-NSM: 0.9999 +Years 52.. 56 at heights 25502980..27182980: + NSM subsidies: 23107941672 (~ 231.079 ZEC, 0.00013755 ZEC per block) + no-NSM subsidies: 23110467003 (~ 231.105 ZEC, 0.00013756 ZEC per block) + difference: -2525331 (~ -0.025 ZEC), NSM/no-NSM: 0.9999 +Years 56.. 60 at heights 27182980..28862980: + NSM subsidies: 11553728655 (~ 115.537 ZEC, 0.00006877 ZEC per block) + no-NSM subsidies: 11554861792 (~ 115.549 ZEC, 0.00006878 ZEC per block) + difference: -1133137 (~ -0.011 ZEC), NSM/no-NSM: 0.9999 +Years 60.. 64 at heights 28862980..30542980: + NSM subsidies: 5776743256 (~ 57.767 ZEC, 0.00003439 ZEC per block) + no-NSM subsidies: 5777430896 (~ 57.774 ZEC, 0.00003439 ZEC per block) + difference: -687640 (~ -0.007 ZEC), NSM/no-NSM: 0.9999 +Years 64.. 68 at heights 30542980..32222980: + NSM subsidies: 2888311019 (~ 28.883 ZEC, 0.00001719 ZEC per block) + no-NSM subsidies: 2888715448 (~ 28.887 ZEC, 0.00001719 ZEC per block) + difference: -404429 (~ -0.004 ZEC), NSM/no-NSM: 0.9999 +Years 68.. 72 at heights 32222980..33902980: + NSM subsidies: 1444125052 (~ 14.441 ZEC, 0.00000860 ZEC per block) + no-NSM subsidies: 1444357724 (~ 14.444 ZEC, 0.00000860 ZEC per block) + difference: -232672 (~ -0.002 ZEC), NSM/no-NSM: 0.9998 +Years 72.. 76 at heights 33902980..35582980: + NSM subsidies: 722047128 (~ 7.220 ZEC, 0.00000430 ZEC per block) + no-NSM subsidies: 722178862 (~ 7.222 ZEC, 0.00000430 ZEC per block) + difference: -131734 (~ -0.001 ZEC), NSM/no-NSM: 0.9998 +Years 76.. 80 at heights 35582980..37262980: + NSM subsidies: 361015488 (~ 3.610 ZEC, 0.00000215 ZEC per block) + no-NSM subsidies: 361089431 (~ 3.611 ZEC, 0.00000215 ZEC per block) + difference: -73943 (~ -0.001 ZEC), NSM/no-NSM: 0.9998 +Years 80.. 84 at heights 37262980..38942980: + NSM subsidies: 180506560 (~ 1.805 ZEC, 0.00000107 ZEC per block) + no-NSM subsidies: 180076425 (~ 1.801 ZEC, 0.00000107 ZEC per block) + difference: 430135 (~ 0.004 ZEC), NSM/no-NSM: 1.0024 +Years 84.. 88 at heights 38942980..40622980: + NSM subsidies: 90245698 (~ 0.902 ZEC, 0.00000054 ZEC per block) + no-NSM subsidies: 89666503 (~ 0.897 ZEC, 0.00000053 ZEC per block) + difference: 579195 (~ 0.006 ZEC), NSM/no-NSM: 1.0065 +Years 88.. 92 at heights 40622980..42302980: + NSM subsidies: 45126662 (~ 0.451 ZEC, 0.00000027 ZEC per block) + no-NSM subsidies: 44364961 (~ 0.444 ZEC, 0.00000026 ZEC per block) + difference: 761701 (~ 0.008 ZEC), NSM/no-NSM: 1.0172 +Years 92.. 96 at heights 42302980..43982980: + NSM subsidies: 22552905 (~ 0.226 ZEC, 0.00000013 ZEC per block) + no-NSM subsidies: 21810771 (~ 0.218 ZEC, 0.00000013 ZEC per block) + difference: 742134 (~ 0.007 ZEC), NSM/no-NSM: 1.0340 +Years 96..100 at heights 43982980..45662980: + NSM subsidies: 11280615 (~ 0.113 ZEC, 0.00000007 ZEC per block) + no-NSM subsidies: 10437095 (~ 0.104 ZEC, 0.00000006 ZEC per block) + difference: 843520 (~ 0.008 ZEC), NSM/no-NSM: 1.0808 +Years 100..104 at heights 45662980..47342980: + NSM subsidies: 5609808 (~ 0.056 ZEC, 0.00000003 ZEC per block) + no-NSM subsidies: 4846838 (~ 0.048 ZEC, 0.00000003 ZEC per block) + difference: 762970 (~ 0.008 ZEC), NSM/no-NSM: 1.1574 +Years 104..108 at heights 47342980..49022980: + NSM subsidies: 2702325 (~ 0.027 ZEC, 0.00000002 ZEC per block) + no-NSM subsidies: 2423419 (~ 0.024 ZEC, 0.00000001 ZEC per block) + difference: 278906 (~ 0.003 ZEC), NSM/no-NSM: 1.1151 +Years 108..112 at heights 49022980..50702980: + NSM subsidies: 1680000 (~ 0.017 ZEC, 0.00000001 ZEC per block) + no-NSM subsidies: 743419 (~ 0.007 ZEC, 0.00000000 ZEC per block) + difference: 936581 (~ 0.009 ZEC), NSM/no-NSM: 2.2598 ``` The program will also output PNG files with plots of the issuance curve in `plots/`: -![ZSF balance simulation](plots/zsf_balance.png) -![ZSF block subsidy simulation](plots/zsf_block_subsidy.png) +![NSM balance simulation](plots/nsm_balance.png) +![NSM block subsidy simulation](plots/nsm_block_subsidy.png) ## About this repo -This simulator is part of our larger efforts to implement the Zcash Sustainability Fund, watch the presentation [here](https://www.youtube.com/watch?v=_QSYgvDV33k) +This simulator is part of our larger efforts to implement the Network Sustainability Mechanism. +They are described in a ZconV presentation by Eiger and Shielded Labs (from when it was called the +Zcash Sustainability Fund) that is available [here](https://www.youtube.com/watch?v=_QSYgvDV33k). ## About [Eiger](https://www.eiger.co) -We are engineers. We contribute to various ecosystems by building low level implementations and core components. We believe in Zcash because privacy is critical to a well functioning society. +We are engineers. We contribute to various ecosystems by building low level implementations and +core components. We believe in Zcash because privacy is critical to a well functioning society. Contact us at hello@eiger.co Follow us on [X/Twitter](https://x.com/eiger_co) diff --git a/plots/nsm_balance.png b/plots/nsm_balance.png new file mode 100644 index 0000000..1f6f281 Binary files /dev/null and b/plots/nsm_balance.png differ diff --git a/plots/nsm_block_subsidy.png b/plots/nsm_block_subsidy.png new file mode 100644 index 0000000..3248bfa Binary files /dev/null and b/plots/nsm_block_subsidy.png differ diff --git a/plots/zsf_balance.png b/plots/zsf_balance.png deleted file mode 100644 index 9f0e8e5..0000000 Binary files a/plots/zsf_balance.png and /dev/null differ diff --git a/plots/zsf_block_subsidy.png b/plots/zsf_block_subsidy.png deleted file mode 100644 index 435d8fc..0000000 Binary files a/plots/zsf_block_subsidy.png and /dev/null differ diff --git a/src/main.rs b/src/main.rs index e81c687..1d6cb6b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,134 +1,185 @@ use crate::plots::{generate_plots, PLOT_SAMPLING}; -pub const MAX_MONEY: i64 = 21_000_000; -const BLOCKS_PER_YEAR: u32 = 420_768; -const ZATOSHIS_PER_ZEC: i64 = 100_000_000; -// predicted ZEC supply at the next halving (block 2726400) -const INITIAL_SUPPLY: i64 = 1_574_963_454_129_680; -const INITIAL_SUBSIDIES: i64 = MAX_MONEY * ZATOSHIS_PER_ZEC - INITIAL_SUPPLY; +mod plots; -const BLOCK_SUBSIDY_NUMERATOR: i64 = 4126; -const BLOCK_SUBSIDY_DENOMINATOR: i64 = 10_000_000_000; +type Height = i64; // or height interval +type Zats = i64; -const POST_BLOSSOM_HALVING_INTERVAL: u32 = 1_680_000; -// block subsidy starting at the next halving (block 2726400) -const INITIAL_LEGACY_BLOCK_SUBSIDY: i64 = 50 * ZATOSHIS_PER_ZEC / 32; +pub const MAX_MONEY: Zats = 21_000_000; +const BLOCKS_PER_YEAR: i64 = 420_768; +const ZATOSHIS_PER_ZEC: Zats = 1_0000_0000; +const INITIAL_HALVING: usize = 2; +const INITIAL_HALVING_HEIGHT: Height = 2726400; +// Predicted ZEC supply at INITIAL_HALVING. TODO: check this. +const INITIAL_SUPPLY: Zats = 1_574_963_454_129_680; +const INITIAL_SUBSIDIES: Zats = MAX_MONEY * ZATOSHIS_PER_ZEC - INITIAL_SUPPLY; -mod plots; +const NSM_BLOCK_SUBSIDY_NUMERATOR: i64 = 4126; +const NSM_BLOCK_SUBSIDY_DENOMINATOR: i64 = 10_000_000_000; -fn main() { - let (block, balance_points, zsf_reward_points, legacy_reward_points, halvings) = simulate(); +const POST_BLOSSOM_HALVING_INTERVAL: Height = 1_680_000; +// Block subsidy at INITIAL_HALVING_HEIGHT. +const INITIAL_NO_NSM_BLOCK_SUBSIDY: Zats = 12_5000_0000 / 8; + +fn to_zec(zatoshis: Zats) -> f64 { + zatoshis as f64 / ZATOSHIS_PER_ZEC as f64 +} + +fn years_after_initial(height: Height) -> f64 { + (height - INITIAL_HALVING_HEIGHT) as f64 / BLOCKS_PER_YEAR as f64 +} + +fn no_nsm_block_subsidy_for_height(height: Height) -> Zats { + assert!(height >= INITIAL_HALVING_HEIGHT); + let halving_after_initial = (height - INITIAL_HALVING_HEIGHT) / POST_BLOSSOM_HALVING_INTERVAL; + INITIAL_NO_NSM_BLOCK_SUBSIDY >> std::cmp::min(halving_after_initial, 63) +} + +struct Stats { + pub index: i64, + pub end_height: i64, + pub nsm_subsidies: i64, + pub no_nsm_subsidies: i64, +} + +impl Stats { + fn summary(self: &Stats) -> String { + let difference = self.nsm_subsidies - self.no_nsm_subsidies; + let ratio = self.nsm_subsidies as f64 / self.no_nsm_subsidies as f64; + let end_year = self.index * 4; + format!( + "Years {:>3}..{:>3} at heights {:>8}..{:>8}:\n \ + NSM subsidies: {:>15} (~{:>12.3} ZEC, {:>12.8} ZEC per block)\n \ + no-NSM subsidies: {:>15} (~{:>12.3} ZEC, {:>12.8} ZEC per block)\n \ + difference: {:>15} (~{:>12.3} ZEC), NSM/no-NSM: {:.4}", + end_year - 4, + end_year, + self.end_height - POST_BLOSSOM_HALVING_INTERVAL, + self.end_height, + self.nsm_subsidies, + to_zec(self.nsm_subsidies), + self.nsm_subsidies as f64 + / POST_BLOSSOM_HALVING_INTERVAL as f64 + / ZATOSHIS_PER_ZEC as f64, + self.no_nsm_subsidies, + to_zec(self.no_nsm_subsidies), + self.no_nsm_subsidies as f64 + / POST_BLOSSOM_HALVING_INTERVAL as f64 + / ZATOSHIS_PER_ZEC as f64, + difference, + to_zec(difference), + ratio + ) + } +} - print_halving(halvings); +fn main() { + let (end_height, balance_points, nsm_reward_points, no_nsm_reward_points, four_year_stats) = + simulate(); - let years = block as f64 / BLOCKS_PER_YEAR as f64; + print_four_year_stats(&four_year_stats); generate_plots( balance_points, - zsf_reward_points, - legacy_reward_points, - years, + nsm_reward_points, + no_nsm_reward_points, + years_after_initial(end_height), ); } +#[allow(clippy::type_complexity)] fn simulate() -> ( - u32, + Height, Vec<(f64, f64)>, Vec<(f64, f64)>, Vec<(f64, f64)>, - Vec, + Vec, ) { - let mut available_subsidies: i64 = INITIAL_SUBSIDIES; - let mut block: u32 = 0; + let mut available_subsidies: Zats = INITIAL_SUBSIDIES; + let mut height: Height = INITIAL_HALVING_HEIGHT; let mut balance_points: Vec<(f64, f64)> = Vec::new(); - let mut zsf_reward_points: Vec<(f64, f64)> = Vec::new(); - let mut legacy_reward_points: Vec<(f64, f64)> = Vec::new(); - let mut halvings: Vec = Vec::new(); - let mut halving_subsidies: i64 = 0; + let mut nsm_reward_points: Vec<(f64, f64)> = Vec::new(); + let mut no_nsm_reward_points: Vec<(f64, f64)> = Vec::new(); + let mut four_year_stats: Vec = Vec::new(); + let mut four_year_nsm_subsidies: Zats = 0; + let mut four_year_no_nsm_subsidies: Zats = 0; + let mut nsm_deployment_height: Option = None; while available_subsidies > 0 { - let block_subsidy = (available_subsidies * BLOCK_SUBSIDY_NUMERATOR - + (BLOCK_SUBSIDY_DENOMINATOR - 1)) - / BLOCK_SUBSIDY_DENOMINATOR; + let years = years_after_initial(height); + let nsm_block_subsidy = (available_subsidies * NSM_BLOCK_SUBSIDY_NUMERATOR + + (NSM_BLOCK_SUBSIDY_DENOMINATOR - 1)) + / NSM_BLOCK_SUBSIDY_DENOMINATOR; + + let no_nsm_block_subsidy = no_nsm_block_subsidy_for_height(height); + + if nsm_deployment_height.is_none() && nsm_block_subsidy < no_nsm_block_subsidy { + nsm_deployment_height = Some(height); + println!("DEPLOYMENT_BLOCK_HEIGHT = {height} ({years:.2} years after {INITIAL_HALVING}nd halving)"); + } + + let block_subsidy = if nsm_deployment_height.is_some() { + four_year_nsm_subsidies += nsm_block_subsidy; + four_year_no_nsm_subsidies += no_nsm_block_subsidy; + nsm_block_subsidy + } else { + no_nsm_block_subsidy + }; available_subsidies -= block_subsidy; - halving_subsidies += block_subsidy; if available_subsidies == 0 { println!( - "Last block is {} in ~{:.2} years\nFinal block subsidy: {} (~{} ZEC)\nFinal ZSF balance: {} (~{} ZEC)", - block, // current block - block as f64 / BLOCKS_PER_YEAR as f64, // ~ current year - block_subsidy, // block subsidy in zatoshis - block_subsidy / ZATOSHIS_PER_ZEC, // block subsidy in ZEC - available_subsidies, // available subsidies in zatoshis - available_subsidies / ZATOSHIS_PER_ZEC // available subsidies in ZEC + "Last block with non-zero subsidy is at height {} in ~{:.2} years after the {}nd halving.\n\ + Final block subsidy: {} (~{} ZEC)\n\ + Final NSM balance: {} (~{} ZEC)", + height, // current height + years, // ~ current year after initial halving + INITIAL_HALVING, // initial halving ordinal + block_subsidy, // block subsidy in zatoshis + to_zec(block_subsidy), // block subsidy in ZEC + available_subsidies, // available subsidies in zatoshis + to_zec(available_subsidies) // available subsidies in ZEC ); } - if block % PLOT_SAMPLING == 0 { - let block_float = block as f64 / BLOCKS_PER_YEAR as f64; - balance_points.push(( - block_float, - available_subsidies as f64 / ZATOSHIS_PER_ZEC as f64, - )); - - zsf_reward_points.push((block_float, block_subsidy as f64 / ZATOSHIS_PER_ZEC as f64)); - - legacy_reward_points.push(( - block_float, - ((INITIAL_LEGACY_BLOCK_SUBSIDY / 2_i64.pow(block / POST_BLOSSOM_HALVING_INTERVAL)) - as f64) - / ZATOSHIS_PER_ZEC as f64, - )); + if ((height - INITIAL_HALVING_HEIGHT) as u32) % PLOT_SAMPLING == 0 { + balance_points.push((years, to_zec(available_subsidies))); + nsm_reward_points.push((years, to_zec(block_subsidy))); + no_nsm_reward_points.push((years, to_zec(no_nsm_block_subsidy))); } - if block > 0 && block % POST_BLOSSOM_HALVING_INTERVAL == 0 { - halvings.push(halving_subsidies); - halving_subsidies = 0; + if let Some(h) = nsm_deployment_height { + if height > h && (height - h) % POST_BLOSSOM_HALVING_INTERVAL == 0 { + four_year_stats.push(Stats { + index: (height - h) / POST_BLOSSOM_HALVING_INTERVAL, + end_height: height, + nsm_subsidies: four_year_nsm_subsidies, + no_nsm_subsidies: four_year_no_nsm_subsidies, + }); + four_year_nsm_subsidies = 0; + four_year_no_nsm_subsidies = 0; + } } - block += 1; + height += 1; } ( - block, + height, balance_points, - zsf_reward_points, - legacy_reward_points, - halvings, + nsm_reward_points, + no_nsm_reward_points, + four_year_stats, ) } -fn print_halving(halvings: Vec) { +fn print_four_year_stats(four_year_stats: &Vec) { println!("#############################################"); - println!("Halvings:"); - - for (i, zsf_halving_subsidies) in halvings.iter().enumerate() { - let legacy_subsidy_per_block = INITIAL_LEGACY_BLOCK_SUBSIDY / 2_i64.pow(i as u32); - let legacy_halving_subsidies = legacy_subsidies_for_halving(i as u32); - let difference = zsf_halving_subsidies - legacy_halving_subsidies; - let ratio = *zsf_halving_subsidies as f64 / legacy_halving_subsidies as f64; - - println!( - "Halving {:>2} at block {:>8}:\n ZSF subsidies: {:>15} (~{:>12.3} ZEC, {:>12.3} ZEC per block)\n legacy subsidies: {:>15} (~{:>12.3} ZEC, {:>12.3} ZEC per block)\n difference: {:>15} (~{:>12.3} ZEC), ZSF/legacy: {:.4}", - i + 1, - (i + 1) as u32 * POST_BLOSSOM_HALVING_INTERVAL, - zsf_halving_subsidies, - *zsf_halving_subsidies as f64 / ZATOSHIS_PER_ZEC as f64, - *zsf_halving_subsidies as f64 / POST_BLOSSOM_HALVING_INTERVAL as f64 / ZATOSHIS_PER_ZEC as f64, - legacy_halving_subsidies, - legacy_halving_subsidies as f64 / ZATOSHIS_PER_ZEC as f64, - legacy_subsidy_per_block as f64 / ZATOSHIS_PER_ZEC as f64, - difference, - difference / ZATOSHIS_PER_ZEC, - ratio - ); + println!("Four-year periods:"); + + for stats in four_year_stats { + println!("{}", stats.summary()); } println!("#############################################"); } - -fn legacy_subsidies_for_halving(halving: u32) -> i64 { - let subsidy_for_halving = INITIAL_LEGACY_BLOCK_SUBSIDY / 2_i64.pow(halving); - POST_BLOSSOM_HALVING_INTERVAL as i64 * subsidy_for_halving -} diff --git a/src/plots.rs b/src/plots.rs index de08381..2d2c33a 100644 --- a/src/plots.rs +++ b/src/plots.rs @@ -4,17 +4,17 @@ use plotters::{ style::*, }; -use crate::MAX_MONEY; +use crate::INITIAL_HALVING; -const BALANCE_PLOT_PATH: &str = "plots/zsf_balance.png"; -const BLOCK_SUBSIDY_PLOT_PATH: &str = "plots/zsf_block_subsidy.png"; +const BALANCE_PLOT_PATH: &str = "plots/nsm_balance.png"; +const BLOCK_SUBSIDY_PLOT_PATH: &str = "plots/nsm_block_subsidy.png"; const PLOT_ZOOM: usize = 5; pub const PLOT_SAMPLING: u32 = 1000; pub fn generate_plots( balance_points: Vec<(f64, f64)>, - zsf_reward_points: Vec<(f64, f64)>, - legacy_reward_points: Vec<(f64, f64)>, + nsm_reward_points: Vec<(f64, f64)>, + no_nsm_reward_points: Vec<(f64, f64)>, years: f64, ) { balance_plot( @@ -23,8 +23,8 @@ pub fn generate_plots( ); block_subsidy_plot( - &zsf_reward_points[0..(balance_points.len() / PLOT_ZOOM)], - &legacy_reward_points[0..(balance_points.len() / PLOT_ZOOM)], + &nsm_reward_points[0..(balance_points.len() / PLOT_ZOOM)], + &no_nsm_reward_points[0..(balance_points.len() / PLOT_ZOOM)], years / PLOT_ZOOM as f64, ); } @@ -35,18 +35,18 @@ fn balance_plot(points: &[(f64, f64)], years: f64) { root.fill(&WHITE).unwrap(); let mut chart = ChartBuilder::on(&root) - .caption("ZSF balance", ("sans-serif", 50).into_font()) + .caption("NSM balance", ("sans-serif", 50).into_font()) .margin(50) .x_label_area_size(50) .y_label_area_size(50) - .build_cartesian_2d(0f64..years, 0f64..(MAX_MONEY as f64)) + .build_cartesian_2d(0f64..years, 0f64..6_000_000_f64) .unwrap(); chart .configure_mesh() .bold_line_style(WHITE.mix(0.3)) - .y_desc("ZSF balance in ZEC") - .x_desc("Years from activation") + .y_desc("NSM balance in ZEC") + .x_desc(format!("Years from {INITIAL_HALVING}nd halving")) .axis_desc_style(("sans-serif", 15)) .y_label_formatter(&|x| format!("{}M", x / 1_000_000_f64)) .draw() @@ -62,8 +62,8 @@ fn balance_plot(points: &[(f64, f64)], years: f64) { } fn block_subsidy_plot( - zsf_reward_points: &[(f64, f64)], - legacy_reward_points: &[(f64, f64)], + nsm_reward_points: &[(f64, f64)], + no_nsm_reward_points: &[(f64, f64)], years: f64, ) { let root = BitMapBackend::new(BLOCK_SUBSIDY_PLOT_PATH, (1024, 768)).into_drawing_area(); @@ -72,35 +72,35 @@ fn block_subsidy_plot( let mut chart = ChartBuilder::on(&root) .caption( - "Block subsidies - ZSF vs current", + "Block subsidies - NSM vs current", ("sans-serif", 50).into_font(), ) .margin(50) .x_label_area_size(50) .y_label_area_size(50) - .build_cartesian_2d(0f64..years, 0f64..5f64) + .build_cartesian_2d(0f64..years, 0f64..2f64) .unwrap(); chart .configure_mesh() .bold_line_style(WHITE.mix(0.3)) .y_desc("Block subsidy in ZEC") - .x_desc("Years from activation") + .x_desc(format!("Years from {INITIAL_HALVING}nd halving")) .axis_desc_style(("sans-serif", 15)) .draw() .unwrap(); - let zsf_series = LineSeries::new(zsf_reward_points.to_owned(), &RED); - let legacy_series = LineSeries::new(legacy_reward_points.to_owned(), &BLUE); + let nsm_series = LineSeries::new(nsm_reward_points.to_owned(), &RED); + let no_nsm_series = LineSeries::new(no_nsm_reward_points.to_owned(), &BLUE); chart - .draw_series(zsf_series) + .draw_series(nsm_series) .unwrap() - .label("ZSF Smooth Issuance") + .label("NSM Smooth Issuance") .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], RED)); chart - .draw_series(legacy_series) + .draw_series(no_nsm_series) .unwrap() .label("Current issuance") .legend(|(x, y)| PathElement::new(vec![(x, y), (x + 20, y)], BLUE));