Berichtsmanifest
| Artikel | Beschreibung |
|---|---|
| Kunde | LiNEAR Protocol |
| Ziel | LiNEAR |
Versionshistorie
| Version | Datum | Beschreibung |
|---|---|---|
| 1.0 | 1. Apr. 2022 | Erste Veröffentlichung |
1. Einleitung
1.1 Über Zielverträge
| Information | Beschreibung |
|---|---|
| Typ | Smart Contract |
| Sprache | Rust |
| Ansatz | Semi-automatische und manuelle Verifizierung |
Das Repository, das geprüft wurde, beinhaltet LiNEAR ^1.
Der Prüfungsprozess ist iterativ. Insbesondere prüfen wir die Commits, die gefundene Probleme beheben. Wenn neue Probleme auftreten, setzen wir diesen Prozess fort. Die Commit-SHA-Werte während der Prüfung sind wie folgt aufgeführt. Unser Prüfbericht ist für die ursprüngliche Version (d. h. Version 1) sowie für neue Codes (in den folgenden Versionen) zur Behebung von Problemen im Prüfbericht verantwortlich.

1.2 Sicherheitsmodell
Zur Risikobewertung folgen wir den Standards oder Vorschlägen, die sowohl in der Industrie als auch in der akademischen Welt weit verbreitet sind, einschließlich der OWASP Risk Rating Methodology ^2 und der Common Weakness Enumeration ^3. Die allgemeine Schwere des Risikos wird durch Wahrscheinlichkeit und Auswirkung bestimmt. Insbesondere wird die Wahrscheinlichkeit verwendet, um abzuschätzen, wie wahrscheinlich eine bestimmte Schwachstelle von einem Angreifer entdeckt und ausgenutzt werden kann, während die Auswirkung verwendet wird, um die Folgen einer erfolgreichen Ausnutzung zu messen.
In diesem Bericht sind sowohl Wahrscheinlichkeit als auch Auswirkung in zwei Kategorien eingeteilt, d. h. hoch und niedrig bzw. ihre Kombinationen sind in Tabelle 1.1 dargestellt.

Dementsprechend sind die in diesem Bericht gemessenen Schweregrade in drei Kategorien eingeteilt: Hoch, Mittel, Niedrig. Der Vollständigkeit halber wird auch Unbestimmt verwendet, um Umstände abzudecken, bei denen das Risiko nicht gut bestimmt werden kann.
Darüber hinaus fällt der Status eines gefundenen Problems in eine der folgenden vier Kategorien:
-
Unbestimmt Noch keine Reaktion.
-
Bestätigt Das Problem wurde vom Kunden erhalten, aber noch nicht bestätigt.
-
Bestätigt Das Problem wurde vom Kunden anerkannt, aber noch nicht behoben.
-
Behoben Das Problem wurde vom Kunden bestätigt und behoben.
2. Ergebnisse
Insgesamt finden wir 4 potenzielle Probleme im Smart Contract. Wir haben auch 4 Empfehlungen, wie folgt:
-
Hohes Risiko: 0
-
Mittleres Risiko: 2
-
Niedriges Risiko: 2
-
Empfehlungen: 4
| ID | Schweregrad | Beschreibung | Kategorie | Status |
|---|---|---|---|---|
| 1 | Mittel | Präzisionsverlust | Softwaresicherheit | Behoben |
| 2 | Niedrig | Verfügbares Guthaben des Benutzers kann vorübergehend gesperrt sein | DeFi-Sicherheit | Bestätigt |
| 3 | Mittel | Unbegrenzte Belohnungsverteilung an Begünstigte | DeFi-Sicherheit | Behoben |
| 4 | Niedrig | Entnahmeanforderungen der Benutzer können nicht rechtzeitig erfüllt werden | DeFi-Sicherheit | Behoben |
| 7 | - | Funktion epoch_update_rewards funktioniert aufgrund unbegrenzter Begünstigter möglicherweise nicht | Empfehlung | Behoben |
| 8 | - | Redundanter Code | Empfehlung | Bestätigt |
| 6 | - | Fehlende Prüfung des vorausbezahlten Gas in der Funktion ft_transfer_call | Empfehlung | Behoben |
| 9 | - | Risiko eines zentralisierten Designs | Empfehlung | Bestätigt |
Die Details sind in den folgenden Abschnitten aufgeführt.
2.1 Softwaresicherheit
2.1.1 Potenzielle Problem 1: Präzisionsverlust
| Information | Beschreibung |
|---|---|
| Status | In Version 2 behoben |
| Eingeführt von | Version 1 |
Beschreibung In Zeile 125 der Funktion internal_calculate_distribution wird bei der Berechnung der Variablen reward_per_session vor der Multiplikation eine Division durchgeführt.
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;
Auflistung 2.1: contracts/linear/src/farm.rs
Auswirkung Die Division wird in der Sprache Rust bei ganzen Zahlen abgeschnitten. In diesem Fall kann die Division vor der Multiplikation bei ganzen Zahlen zu einem Präzisionsverlust führen.
Vorschlag I Ändern Sie diese Berechnung so, dass die Multiplikation vor der Division durchgeführt wird.
Vorschlag II Berechnen Sie den Wert von reward_per_session im Voraus, wenn ein Farm hinzugefügt wird. Dies liegt daran, dass farm.amount, farm.end_date und farm.start_date während des Farmprozesses nicht geändert werden, es sei denn, der Besitzer stoppt ihn.
2.2 DeFi-Sicherheit
2.2.1 Potenzielle Problem 2: Verfügbares Guthaben des Benutzers kann vorübergehend gesperrt sein
| Information | Beschreibung |
|---|---|
| Status | Bestätigt |
| Eingeführt von | Version 1 |
Beschreibung Die eingezahlten NEARs des Benutzers werden direkt zum nicht eingesetzten Guthaben des Benutzers addiert. Wenn der Benutzer die Funktionen unstake/unstake_all aufgerufen hat, wird dieser Betrag an verfügbaren NEARs daher in den nächsten 0 bis 8 Epochen gesperrt.
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();
}
Auflistung 2.2: contracts/linear/src/internal.rs
Auswirkung Wenn ein Benutzer sich des Arbeitsablaufs dieses Vertrags nicht bewusst ist und direkt mit diesem Vertrag interagiert, kann das verfügbare Guthaben des Benutzers vorübergehend gesperrt werden.
Vorschlag I Fügen Sie der Struktur Account ein weiteres Attribut (z. B. available_amount) hinzu, um die verfügbaren NEARs zu verwalten.
Feedback vom Projekt Dies ist beabsichtigt und folgt im Wesentlichen der ursprünglichen Schnittstelle und dem Design des Staking-Pools. Um das potenzielle Problem zu lösen, können wir Verbesserungen vornehmen, um ein weiteres Feld 'unstaking' hinzuzufügen, um es von 'unstaked' zu unterscheiden, und 'unstaking' vor dem Start des nächsten Entnahmevorgangs für dieses Konto in 'unstaked' zu verschieben. Vorerst ziehen wir es jedoch vor, die Änderung im Moment nicht vorzunehmen, um den Arbeitsablauf konsistent mit dem Staking-Pool zu halten. Als Workaround wird die Benutzeroberfläche beim Entnehmen aus dem Frontend die Benutzer daran erinnern, zuerst abzuheben, wenn sie einen 'nicht eingesetzten' Betrag in ihrem Konto haben.
2.2.2 Potenzielle Problem 3: Unbegrenzte Belohnungsverteilung an Begünstigte
| Information | Beschreibung |
|---|---|
| Status | In Version 2 behoben |
| Eingeführt von | Version 1 |
Beschreibung Dieser Vertrag prüft nicht das Gesamtgewicht aller Begünstigten in der Funktion assert_valid, wenn ein neuer Begünstigter eingerichtet wird.
pub fn set_beneficiary(&mut self, account_id: AccountId, fraction: Fraction) {
self.assert_owner();
fraction.assert_valid();
self.beneficiaries.insert(&account_id, &fraction);
}
Auflistung 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
);
}
Auflistung 2.4: contracts/linear/src/utils.rs
Auswirkung Sobald das Gesamtgewicht der Begünstigten 100 % überschreitet, können die für Begünstigte geprägten LiNEARs den Preis von LiNEAR nach der Ausführung der Aktion epoch_update_rewards verringern.
Vorschlag I Führen Sie einen angemessenen Schwellenwert ein, um die gesamte an Begünstigte verteilte Belohnung zu begrenzen.
2.2.3 Potenzielle Problem 4: Entnahmeanforderungen der Benutzer können nicht rechtzeitig erfüllt werden
| Information | Beschreibung |
|---|---|
| Status | In Version 2 behoben |
| Eingeführt von | Version 1 |
Beschreibung Beschreibung Die von der Funktion get_num_epoch_to_unstake zurückgegebene Anzahl von Epochen sollte in einigen Eckfällen verdoppelt werden. Wenn beispielsweise die gesamten eingesetzten NEARs in Validator-Staking-Pools, die sich nicht im Wartezustand befinden, nicht ausreichen, können die Benutzer nicht alle nach 4 Epochen angeforderten nicht eingesetzten NEARs abheben.
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
}
Auflistung 2.5: contracts/linear/src/validator_pool.rs
Auswirkung Die Entnahmeanforderungen der Benutzer können möglicherweise nicht immer rechtzeitig erfüllt werden.
Vorschlag I Implementieren Sie eine Strategie, um die Wartezeit für die Entnahme von Benutzern basierend auf dem Status von Validator-Staking-Pools vorherzusagen.
2.3 Zusätzliche Empfehlung
2.3.1 Funktion epoch_update_rewards funktioniert aufgrund unbegrenzter Begünstigter möglicherweise nicht
| Information | Beschreibung |
|---|---|
| Status | In Version 2 behoben |
| Eingeführt von | Version 1 |
Beschreibung Die Anzahl der Begünstigten ist unbegrenzt. In diesem Fall kann die Funktion epoch_update_rewards, die die Funktion internal_distribute_staking_rewards aufruft, das Gas aufbrauchen.
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,
));
}
Auflistung 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);
}
}
Auflistung 2.7: contract/src/internal.rs
Auswirkung Die Funktion epoch_update_rewards funktioniert aufgrund von begrenztem Gas möglicherweise nicht, wenn es zu viele Begünstigte gibt.
Vorschlag I Es wird empfohlen, einen angemessenen Schwellenwert hinzuzufügen, um die Anzahl der Begünstigten zu begrenzen.
2.3.2 Redundanter Code
| Information | Beschreibung |
|---|---|
| Status | Bestätigt |
| Eingeführt von | Version 1 |
Beschreibung Der Parameter registration_only ist redundant, da die Funktion storage_deposit keine Logik für diesen Parameter implementiert.
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()
}
Auflistung 2.8: contracts/linear/src/fungible_token/storage.rs
Vorschlag I Es wird empfohlen, diesen ungenutzten Parameter in der Funktion storage_deposit zu entfernen.
Feedback vom Projekt Das stimmt. Dasselbe gilt für die Standard-FT-Implementierung in der near-contract-standards Bibliothek. Wir behalten den ungenutzten Parameter registration_only bei, um mit der Standard-Schnittstelle storage_deposit(account_id, registration_only) konsistent zu bleiben.
2.3.3 Fehlende Prüfung des vorausbezahlten Gas in der Funktion ft_transfer_call
| Information | Beschreibung |
|---|---|
| Status | In Version 2 behoben |
| Eingeführt von | Version 1 |
Beschreibung Das vorausbezahlte Gas sollte geprüft werden, um sicherzustellen, dass es für die Ziel Funktionen, einschließlich ft_on_transfer und ft_resolve_transfer, ausreichend ist.
#[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()
}
Auflistung 2.9: contracts/linear/src/fungible_token/core.rs
Vorschlag I Prüfen Sie das vorausbezahlte Gas.
2.3.4 Das Risiko eines zentralisierten Designs
| Information | Beschreibung |
|---|---|
| Status | Bestätigt |
| Eingeführt von | Version 1 |
Beschreibung Dieses Projekt hat potenzielle Zentralisierungsprobleme.
Vorschlag I Es wird empfohlen, ein dezentralisiertes Design im Vertrag einzuführen, z. B. Multisignatur oder DAO.
Feedback vom Projekt I Ja. Dies ist wie in github.com/linear-protocol/LiNEAR/issues/60 erwähnt geplant.
Vorschlag II Der Projektinhaber muss die Sicherheit des privaten Schlüssels der OWNER_ROLE/MANAGERS_ROLE gewährleisten und ein Multisignatur-Schema verwenden, um das Risiko von Single-Point-of-Failure zu reduzieren.
Feedback vom Projekt II Ja. Wir arbeiten an Sicherheitsrichtlinien, um die Risiken von Single-Point-of-Failure zu reduzieren.
3. Hinweise und Bemerkungen
3.1 Haftungsausschluss
Dieser Prüfbericht stellt keine Anlageberatung oder persönliche Empfehlung dar. Er berücksichtigt nicht und sollte nicht als Berücksichtigung oder Einflussnahme auf die potenzielle Ökonomie eines Tokens, eines Token-Verkaufs oder eines anderen Produkts, einer Dienstleistung oder eines anderen Vermögenswerts interpretiert werden. Keine juristische oder natürliche Person sollte sich in irgendeiner Weise auf diesen Bericht verlassen, auch nicht zur Entscheidungsfindung beim Kauf oder Verkauf von Tokens, Produkten, Dienstleistungen oder anderen Vermögenswerten.
Dieser Prüfbericht ist keine Befürwortung eines bestimmten Projekts oder Teams, und der Bericht garantiert nicht die Sicherheit eines bestimmten Projekts. Diese Prüfung gibt keine Gewährleistung für die Entdeckung aller Sicherheitsprobleme von Smart Contracts, d. h. das Bewertungsergebnis garantiert nicht die Nichtexistenz weiterer Funde von Sicherheitsproblemen. Da eine Prüfung nicht als umfassend betrachtet werden kann, empfehlen wir immer unabhängige Prüfungen und ein öffentliches Bug-Bounty-Programm, um die Sicherheit von Smart Contracts zu gewährleisten.
Der Umfang dieser Prüfung beschränkt sich auf den in Abschnitt 1.1 genannten Code. Sofern nicht ausdrücklich angegeben, sind die Sicherheit der Sprache selbst (z. B. der Solidity-Sprache), der zugrunde liegenden Kompilierungswerkzeuge und der Computing-Infrastruktur nicht Teil des Umfangs.
3.2 Prüfverfahren
Wir führen die Prüfung nach folgendem Verfahren durch.
-
Schwachstellenerkennung Wir scannen zuerst Smart Contracts mit automatischen Codeanalysatoren und verifizieren dann manuell (ablehnen oder bestätigen) die von ihnen gemeldeten Probleme.
-
Semantische Analyse Wir studieren die Geschäftslogik von Smart Contracts und führen weitere Untersuchungen zu möglichen Schwachstellen mit einem automatischen Fuzzing-Tool (entwickelt von unserem Forschungsteam) durch. Wir analysieren auch mögliche Angriffsszenarien manuell mit unabhängigen Prüfern, um das Ergebnis zu überprüfen.
-
Empfehlung Wir geben Entwicklern einige nützliche Ratschläge aus der Perspektive guter Programmierpraxis, einschließlich Gasoptimierung, Code-Stil usw.
Wir zeigen die wichtigsten konkreten Prüfpunkte im Folgenden.
3.2.1 Softwaresicherheit
- Reentrancy
- DoS
- Zugriffskontrolle
- Datenverarbeitung und Datenfluss
- Ausnahmebehandlung
- Nicht vertrauenswürdiger externer Aufruf und Kontrollfluss
- Konsistenz der Initialisierung
- Ereignisoperationen
- Fehleranfällige Zufälligkeit
- Unsachgemäße Verwendung des Proxy-Systems
3.2.2 DeFi-Sicherheit
- Semantische Konsistenz
- Funktionale Konsistenz
- Zugriffskontrolle
- Geschäftslogik
- Token-Operation
- Notfallmechanismus
- Oracle-Sicherheit
- Whitelist und Blacklist
- Wirtschaftliche Auswirkung
- Batch-Übertragung
3.2.3 NFT-Sicherheit
- Duplizierter Artikel
- Verifizierung des Token-Empfängers
- Sicherheit von Off-Chain-Metadaten
3.2.4 Zusätzliche Empfehlung
- Gasoptimierung
- Codequalität und Stil
::: Hinweis Die vorherigen Prüfpunkte sind die wichtigsten. Wir können je nach Funktionalität des Projekts weitere Prüfpunkte während des Prüfprozesses verwenden. :::



