Rapor Manifestosu
| Öğe | Açıklama |
|---|---|
| Müşteri | LiNEAR Protocol |
| Hedef | LiNEAR |
Sürüm Geçmişi
| Sürüm | Tarih | Açıklama |
|---|---|---|
| 1.0 | 1 Nisan 2022 | İlk Yayın |
1. Giriş
1.1 Hedef Sözleşmeler Hakkında
| Bilgi | Açıklama |
|---|---|
| Tür | Akıllı Sözleşme |
| Dil | Rust |
| Yaklaşım | Yarı otomatik ve manuel doğrulama |
Denetlenen depo LiNEAR'ı ^1 kapsamaktadır.
Denetleme süreci yinelemeli bir yapıdadır. Özellikle, tespit edilen sorunları düzelten commit'leri denetleyeceğiz. Yeni sorunlar ortaya çıkarsa bu süreci devam ettireceğiz. Denetim sırasındaki commit SHA değerleri aşağıda gösterilmektedir. Denetim raporumuz, başlangıç sürümünden (yani Sürüm 1) ve denetim raporundaki sorunları düzeltmek için (aşağıdaki sürümlerde yer alan) yeni kodlardan sorumludur.

1.2 Güvenlik Modeli
Riski değerlendirmek için, OWASP Risk Derecelendirme Metodolojisi ^2 ve Yaygın Zayıflık Sayımı ^3 dahil olmak üzere hem sektör hem de akademi tarafından yaygın olarak benimsenen standartları veya önerileri takip ediyoruz. Riskin genel ciddiyeti, olasılık ve etki tarafından belirlenir. Özellikle, olasılık belirli bir güvenlik açığının bir saldırgan tarafından ne kadar keşfedilip istismar edilebileceğini tahmin etmek için kullanılırken, etki başarılı bir istismarın sonuçlarını ölçmek için kullanılır.
Bu raporda, hem olasılık hem de etki sırasıyla yüksek ve düşük olmak üzere iki derecelendirmeye ayrılmıştır ve bunların kombinasyonları Tablo 1.1'de gösterilmiştir.

Buna göre, bu raporda ölçülen ciddiyet üç kategoriye ayrılmıştır: Yüksek, Orta, Düşük. Tamlık açısından, riskin yeterince belirlenemediği durumları kapsayacak şekilde Belirsiz de kullanılmaktadır.
Ayrıca, tespit edilen bir sorunun durumu aşağıdaki dört kategoriden birine girecektir:
-
Belirsiz Henüz yanıt alınmadı.
-
Onaylandı Sorun müşteri tarafından alındı, ancak henüz doğrulanmadı.
-
Doğrulandı Sorun müşteri tarafından tanındı, ancak henüz düzeltilmedi.
-
Düzeltildi Sorun müşteri tarafından onaylandı ve düzeltildi.
2. Bulgular
Toplamda, akıllı sözleşmede 4 potansiyel sorun tespit ettik. Ayrıca aşağıdaki şekilde 4 önerimiz bulunmaktadır:
-
Yüksek Risk: 0
-
Orta Risk: 2
-
Düşük Risk: 2
-
Öneriler: 4
| ID | Ciddiyet | Açıklama | Kategori | Durum |
|---|---|---|---|---|
| 1 | Orta | Hassasiyet kaybı | Yazılım Güvenliği | Düzeltildi |
| 2 | Düşük | Kullanıcının kullanılabilir bakiyesi geçici olarak kilitlenebilir | DeFi Güvenliği | Doğrulandı |
| 3 | Orta | Yararlanıcılara sınırsız ödül dağıtımı | DeFi Güvenliği | Düzeltildi |
| 4 | Düşük | Kullanıcıların unstaking talepleri zamanında karşılanmayabilir | DeFi Güvenliği | Düzeltildi |
| 7 | - | epoch_update_rewards fonksiyonu sınırsız yararlanıcılar nedeniyle çalışmayabilir | Öneri | Düzeltildi |
| 8 | - | Gereksiz kod | Öneri | Doğrulandı |
| 6 | - | ft_transfer_call fonksiyonunda prepaid_gas kontrolünün eksikliği | Öneri | Düzeltildi |
| 9 | - | Merkezi tasarımın riski | Öneri | Doğrulandı |
Ayrıntılar aşağıdaki bölümlerde sunulmaktadır.
2.1 Yazılım Güvenliği
2.1.1 Potansiyel Sorun 1: Hassasiyet kaybı
| Bilgi | Açıklama |
|---|---|
| Durum | Sürüm 2'de düzeltildi |
| Hangi sürümde ortaya çıktı | sürüm 1 |
Açıklama internal_calculate_distribution fonksiyonunun 125. satırında, reward_per_session değişkeni hesaplanırken çarpmadan önce bölme işlemi gerçekleştirilmektedir.
fn internal_calculate_distribution(
&self,
farm: &Farm,
total_staked: Balance,
) -> Option<RewardDistribution> {
if farm.start_date > env::block_timestamp() {
// Farm hasn't started.
return None;
}
let mut distribution = farm.last_distribution.clone();
if distribution.undistributed == 0 {
// Farm has ended.
return Some(distribution);
}
distribution.reward_round = (env::block_timestamp() - farm.start_date) / SESSION_INTERVAL;
let reward_per_session =
farm.amount / (farm.end_date - farm.start_date) as u128 * SESSION_INTERVAL as u128;
Liste 2.1: contracts/linear/src/farm.rs
Etki Rust dilinde tamsayılarda bölme işlemi kesmeye neden olur. Bu durumda, tamsayılarda çarpmadan önce bölme işlemi hassasiyet kaybına yol açabilir.
Öneri I Bu hesaplamayı, bölmeden önce çarpma işlemi yapacak şekilde değiştirin.
Öneri II Bir farm eklendiğinde reward_per_session değerini önceden hesaplayın. Bunun nedeni, farm.amount, farm.end_date ve farm.start_date değerlerinin, sahibi durdurmadıkça farming süreci boyunca değişmemesidir.
2.2 DeFi Güvenliği
2.2.1 Potansiyel Sorun 2: Kullanıcının kullanılabilir bakiyesi geçici olarak kilitlenebilir
| Bilgi | Açıklama |
|---|---|
| Durum | Doğrulandı |
| Hangi sürümde ortaya çıktı | sürüm 1 |
Açıklama Kullanıcının yatırdığı NEAR'lar doğrudan kullanıcının unstaked bakiyesine eklenecektir. Bu nedenle, kullanıcı unstake/unstake_all fonksiyonlarını çağırırsa, bu miktardaki kullanılabilir NEAR'lar sonraki 0 ila 8 epoch boyunca kilitlenecektir.
pub(crate) fn internal_deposit(&mut self, amount: Balance) {
let account_id = env::predecessor_account_id();
let mut account = self.internal_get_account(&account_id);
account.unstaked += amount;
self.internal_save_account(&account_id, &account);
Event::Deposit {
account_id: &account_id,
amount: &U128(amount),
new_unstaked_balance: &U128(account.unstaked),
}
.emit();
}
Liste 2.2: contracts/linear/src/internal.rs
Etki Bir kullanıcı bu sözleşme iş akışından haberdar değilse ve doğrudan bu sözleşmeyle etkileşime girerse, kullanıcının kullanılabilir bakiyesi geçici olarak kilitlenebilir.
Öneri I Kullanılabilir NEAR'ları korumak için Account yapısına başka bir özellik (örn. available_amount) ekleyin.
Projeden Geri Bildirim Bu tasarım gereğidir, temelde staking havuzunun orijinal arayüzünü ve tasarımını takip etmektedir. Potansiyel sorunu çözmek için, 'unstaked'den ayırt etmek amacıyla başka bir 'unstaking' alanı ekleyebilir ve bu hesap için bir sonraki unstaking sürecini başlatmadan önce 'unstaking'i 'unstaked'e taşıyabiliriz. Ancak şimdilik, iş akışını staking havuzuyla tutarlı tutmak adına değişiklik yapmamayı tercih ediyoruz. Geçici bir çözüm olarak, kullanıcılar ön yüzden unstaking yaparken, hesaplarında 'unstaked' miktarı varsa kullanıcılar önce para çekmeleri için uyarılacaktır.
2.2.2 Potansiyel Sorun 3: Yararlanıcılara sınırsız ödül dağıtımı
| Bilgi | Açıklama |
|---|---|
| Durum | Sürüm 2'de düzeltildi |
| Hangi sürümde ortaya çıktı | sürüm 1 |
Açıklama Bu sözleşme, yeni bir yararlanıcı ayarlanırken assert_valid fonksiyonunda tüm yararlanıcıların toplam ağırlığını kontrol etmemektedir.
pub fn set_beneficiary(&mut self, account_id: AccountId, fraction: Fraction) {
self.assert_owner();
fraction.assert_valid();
self.beneficiaries.insert(&account_id, &fraction);
}
Liste 2.3: contracts/linear/src/owner.rs
pub fn assert_valid(&self) {
require!(self.denominator != 0, ERR_FRACTION_BAD_DENOMINATOR);
require!(
self.numerator <= self.denominator,
ERR_FRACTION_BAD_NUMERATOR
);
}
Liste 2.4: contracts/linear/src/utils.rs
Etki Yararlanıcıların toplam ağırlığı %100'ü aştığında, yararlanıcılar için basılan LiNEAR'lar epoch_update_rewards eyleminin yürütülmesinin ardından LiNEAR fiyatını düşürebilir.
Öneri I Yararlanıcılara dağıtılan toplam ödülü sınırlamak için makul bir eşik değeri belirleyin.
2.2.3 Potansiyel Sorun 4: Kullanıcıların unstaking talepleri zamanında karşılanmayabilir
| Bilgi | Açıklama |
|---|---|
| Durum | Sürüm 2'de düzeltildi |
| Hangi sürümde ortaya çıktı | sürüm 1 |
Açıklama get_num_epoch_to_unstake fonksiyonundan döndürülen epoch sayısı bazı köşe durumlarda iki katına çıkarılmalıdır. Örneğin, beklemede durumunda olmayan doğrulayıcı staking havuzlarında stake edilen toplam NEAR miktarı yeterli değilse, kullanıcılar 4 epoch sonrasında talep edilen tüm unstaked NEAR'ları çekemeyebilir.
pub fn get_num_epoch_to_unstake(&self, _amount: u128) -> EpochHeight {
// the num of epoches can be doubled or trippled if not enough stake is available
NUM_EPOCHS_TO_UNLOCK
}
Liste 2.5: contracts/linear/src/validator_pool.rs
Etki Kullanıcının unstaking talepleri her zaman zamanında karşılanmayabilir.
Öneri I Doğrulayıcı staking havuzlarının durumuna göre kullanıcının unstaking bekleme süresini tahmin eden bir strateji uygulayın.
2.3 Ek Öneriler
2.3.1 epoch_update_rewards fonksiyonu sınırsız yararlanıcılar nedeniyle çalışmayabilir
| Bilgi | Açıklama |
|---|---|
| Durum | Sürüm 2'de düzeltildi |
| Hangi sürümde ortaya çıktı | sürüm 1 |
Açıklama Yararlanıcı sayısı sınırsızdır. Bu durumda, internal_distribute_staking_rewards fonksiyonunu çağıran epoch_update_rewards fonksiyonu gaz tükenebilir.
pub fn epoch_update_rewards(&mut self, validator_id: AccountId) {
let min_gas = GAS_EPOCH_UPDATE_REWARDS + GAS_EXT_GET_BALANCE + GAS_CB_VALIDATOR_GET_BALANCE;
require!(
env::prepaid_gas() >= min_gas,
format!("{}. require at least {:?}", ERR_NO_ENOUGH_GAS, min_gas)
);
let validator = self
.validator_pool
.get_validator(&validator_id)
.expect(ERR_VALIDATOR_NOT_EXIST);
if validator.staked_amount == 0 && validator.unstaked_amount == 0 {
return;
}
validator
.refresh_total_balance()
.then(ext_self_action_cb::validator_get_balance_callback(
validator.account_id,
env::current_account_id(),
NO_DEPOSIT,
GAS_CB_VALIDATOR_GET_BALANCE,
));
}
Liste 2.6: contracts/linear/src/epoch_actions.rs
/// When there are rewards, a part of them will be
/// given to executor, manager or treasury by minting new LiNEAR tokens.
pub(crate) fn internal_distribute_staking_rewards(&mut self, rewards: Balance) {
let hashmap: HashMap<AccountId, Fraction> = self.internal_get_beneficiaries();
for (account_id, fraction) in hashmap.iter() {
let reward_near_amount: Balance = fraction.multiply(rewards);
// mint extra LiNEAR for him
self.internal_mint_beneficiary_rewards(&account_id, reward_near_amount);
}
}
Liste 2.7: contract/src/internal.rs
Etki Çok fazla yararlanıcı olduğunda sınırlı gaz nedeniyle epoch_update_rewards fonksiyonu çalışmayabilir.
Öneri I Yararlanıcı sayısını sınırlamak için makul bir eşik değeri eklenmesi önerilir.
2.3.2 Gereksiz kod
| Bilgi | Açıklama |
|---|---|
| Durum | Doğrulandı |
| Hangi sürümde ortaya çıktı | sürüm 1 |
Açıklama storage_deposit fonksiyonu bu parametre için herhangi bir mantık uygulamadığından registration_only parametresi gereksizdir.
fn storage_deposit(
&mut self,
account_id: Option<AccountId>,
registration_only: Option<bool>,
) -> StorageBalance {
let amount: Balance = env::attached_deposit();
let account_id = account_id.unwrap_or_else(env::predecessor_account_id);
if let Some(account) = self.accounts.get(&account_id) {
log!("The account is already registered, refunding the deposit");
if amount > 0 {
Promise::new(env::predecessor_account_id()).transfer(amount);
}
} else {
let min_balance = self.storage_balance_bounds().min.0;
if amount < min_balance {
env::panic_str("The attached deposit is less than the minimum storage balance");
}
self.internal_register_account(&account_id);
let refund = amount - min_balance;
if refund > 0 {
Promise::new(env::predecessor_account_id()).transfer(refund);
}
}
self.internal_storage_balance_of(&account_id).unwrap()
}
Liste 2.8: contracts/linear/src/fungible_token/storage.rs
Öneri I storage_deposit fonksiyonundaki bu kullanılmayan parametrenin kaldırılması önerilir.
Projeden Geri Bildirim Doğru. Aynı durum nearcontract-standards crate'indeki standart FT uygulaması için de geçerlidir. Standart storage_deposit(account_id, registration_only) arayüzüyle tutarlılığı korumak adına kullanılmayan registration_only parametresini koruyacağız.
2.3.3 ft_transfer_call fonksiyonunda prepaid_gas kontrolünün eksikliği
| Bilgi | Açıklama |
|---|---|
| Durum | Sürüm 2'de düzeltildi |
| Hangi sürümde ortaya çıktı | sürüm 1 |
Açıklama ft_on_transfer ve ft_resolve_transfer dahil hedef fonksiyonlar için yeterli olduğundan emin olmak amacıyla prepaid_gas kontrol edilmelidir.
#[payable]
fn ft_transfer_call(
&mut self,
receiver_id: AccountId,
amount: U128,
memo: Option<String>,
msg: String,
) -> PromiseOrValue<U128> {
assert_one_yocto();
let sender_id = env::predecessor_account_id();
let amount = amount.into();
self.internal_ft_transfer(&sender_id, &receiver_id, amount, memo);
// Initiating receiver's call and the callback
ext_fungible_token_receiver::ft_on_transfer(
sender_id.clone(),
amount.into(),
msg,
receiver_id.clone(),
NO_DEPOSIT,
env::prepaid_gas() - GAS_FOR_FT_TRANSFER_CALL,
)
.then(ext_ft_self::ft_resolve_transfer(
sender_id,
receiver_id,
amount.into(),
env::current_account_id(),
NO_DEPOSIT,
GAS_FOR_RESOLVE_TRANSFER,
))
.into()
}
Liste 2.9: contracts/linear/src/fungible_token/core.rs
Öneri I prepaid_gas değerini kontrol edin.
2.3.4 Merkezi tasarımın riski
| Bilgi | Açıklama |
|---|---|
| Durum | Doğrulandı |
| Hangi sürümde ortaya çıktı | sürüm 1 |
Açıklama Bu projenin potansiyel merkezileşme sorunları bulunmaktadır.
Öneri I Sözleşmeye çok imzalı veya DAO gibi merkeziyetsiz bir tasarım getirilmesi önerilir.
Projeden Geri Bildirim I Evet. Bu, github.com/linear-protocol/LiNEAR/issues/60 adresinde belirtildiği gibi plandadır.
Öneri II Proje sahibinin OWNER_ROLE/MANAGERS_ROLE özel anahtarının güvenliğini sağlaması ve tek nokta arızası riskini azaltmak için çok imzalı bir şema kullanması gerekmektedir.
Projeden Geri Bildirim II Evet. Tek nokta arızası risklerini azaltmak için güvenlik politikaları üzerinde çalışıyoruz.
3. Uyarılar ve Açıklamalar
3.1 Sorumluluk Reddi
Bu denetim raporu yatırım tavsiyesi veya kişisel bir öneri niteliği taşımamaktadır. Bir tokenın, token satışının veya herhangi bir ürün, hizmet ya da diğer varlığın potansiyel ekonomisini dikkate almaz ve bu şekilde yorumlanamaz. Hiçbir kuruluş, herhangi bir token, ürün, hizmet veya diğer varlıkları satın alma ya da satma kararları vermek dahil olmak üzere hiçbir şekilde bu rapora dayanmamalıdır.
Bu denetim raporu, herhangi bir projenin veya ekibin onayı niteliğinde değildir ve rapor, herhangi bir projenin güvenliğini garanti etmemektedir. Bu denetim, akıllı sözleşmelerin tüm güvenlik sorunlarının tespit edileceğine dair herhangi bir garanti vermemektedir; yani değerlendirme sonucu, başka güvenlik sorunlarının bulunmadığını garanti etmemektedir. Tek bir denetim kapsamlı kabul edilemeyeceğinden, akıllı sözleşmelerin güvenliğini sağlamak için bağımsız denetimler ve halka açık bir hata ödül programı yürütülmesini her zaman öneririz.
Bu denetimin kapsamı, Bölüm 1.1'de belirtilen kodla sınırlıdır. Açıkça belirtilmedikçe, dilin kendisinin güvenliği (örn. solidity dili), temel derleme araç zinciri ve bilişim altyapısı kapsam dışındadır.
3.2 Denetim Prosedürü
Denetimi aşağıdaki prosedüre göre gerçekleştiriyoruz.
-
Güvenlik Açığı Tespiti Önce akıllı sözleşmeleri otomatik kod analizörleriyle tarıyoruz, ardından bu araçların raporladığı sorunları manuel olarak doğruluyoruz (reddediyoruz veya onaylıyoruz).
-
Semantik Analiz Akıllı sözleşmelerin iş mantığını inceliyoruz ve araştırma ekibimiz tarafından geliştirilen otomatik bir fuzzing aracı kullanarak olası güvenlik açıkları üzerinde daha ileri araştırmalar yürütüyoruz. Ayrıca sonuçları çapraz kontrol etmek amacıyla bağımsız denetçilerle olası saldırı senaryolarını manuel olarak analiz ediyoruz.
-
Öneri Geliştiricilere gaz optimizasyonu, kod stili ve benzeri konular dahil olmak üzere iyi programlama pratiği perspektifinden yararlı tavsiyeler sunuyoruz.
Aşağıda temel somut kontrol noktalarını gösteriyoruz.
3.2.1 Yazılım Güvenliği
-
Yeniden giriş (Reentrancy)
-
Hizmet Reddi (DoS)
-
Erişim kontrolü
-
Veri işleme ve veri akışı
-
İstisna yönetimi
-
Güvenilmeyen harici çağrı ve kontrol akışı
-
Başlatma tutarlılığı
-
Olay işlemleri
-
Hata eğilimli rastgelelik
-
Proxy sisteminin yanlış kullanımı
3.2.2 DeFi Güvenliği
-
Semantik tutarlılık
-
İşlevsellik tutarlılığı
-
Erişim kontrolü
-
İş mantığı
-
Token işlemleri
-
Acil durum mekanizması
-
Oracle güvenliği
-
Beyaz liste ve kara liste
-
Ekonomik etki
-
Toplu transfer
3.2.3 NFT Güvenliği
-
Yinelenen öğe
-
Token alıcısının doğrulanması
-
Zincir dışı metadata güvenliği
3.2.4 Ek Öneriler
-
Gaz optimizasyonu
-
Kod kalitesi ve stili
::: Note Önceki kontrol noktaları temel olanlardır. Denetim sürecinde projenin işlevselliğine göre daha fazla kontrol noktası kullanabiliriz. :::



