SIP-77: Perbaikan bug StakingRewards dan stake() yang dapat dijeda
Ringkasan Singkat
stake() perlu dapat dijeda untuk insentif yang telah selesai dan dua perbaikan bug.
Abstrak
Peningkatan meliputi:
- Mewarisi kontrak
Pausabledan menambahkan pengubahnotPausedkestake()untuk mencegah staking ke pool yang sudah tidak digunakan. - Memperbaiki potensi bug overflow pada fungsi notifikasi reward.
- Perbaikan pada
setRewardsDurationuntuk memungkinkanrewardsDurationdiperbarui setelah pengaturan awal.
Motivasi
Menjeda stake saat reward selesai
Ketika kampanye StakingRewards telah selesai, kontrak perlu mencegah siapa pun melakukan staking. Staker tidak akan mendapatkan reward dan dapat menyebabkan masalah pemblokiran dengan inverse synthetic assets yang perlu dibersihkan agar bisa diluncurkan kembali. Menambahkan Pausable dan pengubah notPaused ke stake() akan memungkinkan admin mengatur paused menjadi true untuk mencegah staking. SelfDestructible belum diimplementasikan dan mengingat jumlah nilai dalam kontrak ini, sebaiknya tidak diimplementasikan.
Perbaikan potensi bug overflow
Ringkasan
Terdapat overflow perkalian yang dapat terjadi di dalam fungsi rewardPerToken, pada baris tertentu:
lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(_totalSupply)
Overflow terjadi setiap kali rewardRate >= 2^256 / (10^18 * (lastTimeRewardApplicable() - lastUpdateTime)).
Ini dapat terjadi ketika pengubah updateReward dipanggil, yang akan menyebabkan fungsi-fungsi berikut gagal (revert):
earnedstakewithdrawgetRewardexitnotifyRewardAmount
Tingkat reward diatur di dalam notifyRewardAmount, jika nilai yang terlalu besar diberikan ke fungsi tersebut. Perlu dicatat bahwa notifyRewardAmount sendiri terpengaruh oleh masalah ini, yang berarti jika reward yang diberikan salah, maka masalah tidak dapat dipulihkan.
Solusi
Transaksi notifyRewardAmount harus dibatalkan (revert) jika nilai yang diberikan lebih besar dari 2^256 / 10^18. Sebagai mekanisme keamanan tambahan, nilai ini akan diharuskan tidak lebih besar dari sisa saldo token reward dalam kontrak. Ini akan mencegah overflow dan juga memberikan pemeriksaan tambahan bahwa tingkat reward diatur ke nilai dalam rentang yang sesuai (misalnya, tidak ada angka nol tambahan atau hilang).
Detail
Secara spesifik, masalah ini terjadi ketika rewardRate terlalu tinggi; diatur di dalam fungsi notifyRewardAmount pada baris tertentu:
rewardRate = floor(reward / rewardsDuration) = (reward - k) / rewardsDuration
untuk beberapa 0 <= k < rewardsDuration.
Agar bug terjadi, kita memerlukan:
(reward - k) / rewardsDuration >= 2^256 / (10^18 * (lastTimeRewardApplicable - lastUpdateTime))reward >= rewardsDuration * 2^256 / (10^18 * (lastTimeRewardApplicable - lastUpdateTime)) + k
Oleh karena itu, kita dapat memastikan bug tidak terjadi jika kita memaksa:
reward < rewardsDuration * 2^256 / (10^18 * (lastTimeRewardApplicable - lastUpdateTime))
Jadi kita harus membatasi reward agar lebih kecil dari nilai minimum RHS.
Nilai terkecil yang mungkin dari lastUpdateTime adalah timestamp blok ketika notifyRewardAmount terakhir dipanggil. Nilai terbesar yang mungkin dari lastTimeRewardApplicable adalah periodFinish, dan periodFinish = notificationBlockamp + rewardsDuration (baris tertentu). Menggabungkan ini kita memiliki:
(lastTimeRewardApplicable - lastUpdateTime) <= rewardsDuration
Ergo, kita memerlukan:
reward < rewardsDuration * 2^256 / (10^18 * rewardsDuration) = 2^256 / 10^18
Jadi masalah tidak akan muncul setiap kali kita memerlukan
reward < uint(-1) / UNIT
Perbaikan pada setRewardsDuration untuk memungkinkan pembaruan setelah pengaturan awal
setRewardsDuration dimaksudkan untuk memungkinkan rewardsDuration diatur setelah durasi selesai. Namun, cacat pada require memungkinkan perubahan setelah pengaturan awal.
Kode saat ini:
require(periodFinish == 0 || blockamp > periodFinish);
Perubahan yang diusulkan:
require(blockamp > periodFinish);
Spesifikasi Teknis
- Mewarisi kontrak
Pausabledan menambahkan pengubahnotPausedkestake(). - Batalkan transaksi
notifyRewardAmountjika tingkat reward yang dihitung akan membayar lebih dari saldo kontrak selama periode reward. - Ubah
requiredisetRewardsDurationagar hanya memeriksa apakah periode telah selesai.
Kasus Uji
Pausable
- Harus revert ketika stake dipanggil saat paused true.
- Harus mengizinkan panggilan stake berhasil saat paused false.
Perbaikan bug overflow
- Harus revert
notifyRewardAmountjika reward lebih besar dari saldo kontrak. - Harus revert
notifyRewardAmountjika reward ditambah sisa dari periode sebelumnya lebih besar dari saldo kontrak. - Harus tidak revert
notifyRewardAmountjika reward sama dengan saldo kontrak.
Perbaikan bug setRewardsDuration
- Harus revert ketika mengatur
setRewardsDurationsebelum periode selesai. - Harus memperbarui
rewardsDurationketika memanggilsetRewardsDurationsetelah periode reward selesai.
Nilai yang Dapat Dikonfigurasi (Melalui SCCP)
Harap daftarkan semua nilai yang dapat dikonfigurasi melalui SCCP dalam implementasi ini.
Platform Lainnya
Berita Piala Dunia
Jika Anda memiliki pertanyaan, silakan kirim email ke [email protected]