Документация SolanaРазработка программ

Разработка программ на Rust

Программы для Solana в основном разрабатываются с использованием языка программирования Rust. На этой странице рассматривается написание программ для Solana на Rust без использования фреймворка Anchor, что часто называют написанием программ на "чистом Rust".

Разработка на чистом Rust предоставляет разработчикам прямой контроль над их программами для Solana. Однако этот подход требует больше ручной настройки и шаблонного кода по сравнению с использованием фреймворка Anchor. Этот метод рекомендуется для разработчиков, которые:

  • Стремятся к детальному контролю над логикой программы и оптимизациями
  • Хотят изучить базовые концепции перед переходом к более высокоуровневым фреймворкам

Для начинающих мы рекомендуем начать с фреймворка Anchor. Подробнее см. в разделе Anchor.

Предварительные требования

Для получения подробных инструкций по установке посетите страницу установки.

Перед началом убедитесь, что у вас установлены следующие компоненты:

  • Rust: Язык программирования для создания программ для Solana.
  • Solana CLI: Инструмент командной строки для разработки на Solana.

Начало работы

Пример ниже охватывает основные шаги для создания вашей первой программы для Solana, написанной на Rust. Мы создадим минимальную программу, которая выводит "Hello, world!" в журнал программы.

Создайте новую программу

Сначала создайте новый проект на Rust, используя стандартную команду cargo init с флагом --lib.

Terminal
cargo init hello_world --lib

Перейдите в каталог проекта. Вы должны увидеть файлы по умолчанию src/lib.rs и Cargo.toml.

Terminal
cd hello_world

Добавьте зависимость solana-program

Далее добавьте зависимость solana-program. Это минимальная зависимость, необходимая для создания программы для Solana.

Terminal
cargo add solana-program@1.18.26

Затем добавьте следующий фрагмент в Cargo.toml. Если вы не включите эту конфигурацию, каталог target/deploy не будет создан при сборке программы.

Добавьте тип библиотеки

Затем добавьте следующий фрагмент в Cargo.toml.

[lib]
crate-type = ["cdylib", "lib"]

Если вы не включите эту конфигурацию, каталог target/deploy не будет создан при сборке программы.

Ваш файл Cargo.toml должен выглядеть следующим образом:

Cargo.toml
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "lib"]
[dependencies]
solana-program = "1.18.26"

Добавьте код программы

Затем замените содержимое src/lib.rs следующим кодом. Это минимальная программа Solana, которая выводит "Hello, world!" в журнал программы при её вызове.

Макрос msg! используется в программах Solana для вывода сообщения в журнал программы.

src/lib.rs
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};
entrypoint!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
msg!("Hello, world!");
Ok(())
}

Соберите программу

Затем соберите программу с помощью команды cargo build-sbf.

Terminal
cargo build-sbf

Эта команда создаёт каталог target/deploy, содержащий два важных файла:

  1. Файл .so (например, hello_world.so): Это скомпилированная программа Solana, которая будет развернута в сети как "умный контракт".
  2. Файл keypair (например, hello_world-keypair.json): Публичный ключ этой keypair используется как идентификатор программы при её развертывании.

Чтобы просмотреть идентификатор программы, выполните следующую команду в терминале. Эта команда выводит публичный ключ keypair по указанному пути к файлу:

Terminal
solana address -k ./target/deploy/hello_world-keypair.json

Пример вывода:

4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz

Добавьте зависимости для тестирования

Далее протестируйте программу, используя крейт solana-program-test. Добавьте следующие зависимости в Cargo.toml.

Terminal
cargo add solana-program-test@1.18.26 --dev
cargo add solana-sdk@1.18.26 --dev
cargo add tokio --dev

Протестируйте программу

Добавьте следующий тест в src/lib.rs, ниже кода программы. Это модуль тестирования, который вызывает программу "Hello, world".

src/lib.rs
use solana_program::{
account_info::AccountInfo, entrypoint, entrypoint::ProgramResult, msg, pubkey::Pubkey,
};
entrypoint!(process_instruction);
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
msg!("Hello, world!");
Ok(())
}
#[cfg(test)]
mod test {
use solana_program_test::*;
use solana_sdk::{
instruction::Instruction, pubkey::Pubkey, signature::Signer, transaction::Transaction,
};
#[tokio::test]
async fn test_hello_world() {
let program_id = Pubkey::new_unique();
let mut program_test = ProgramTest::default();
program_test.add_program("hello_world", program_id, None);
let (mut banks_client, payer, recent_blockhash) = program_test.start().await;
// Create instruction
let instruction = Instruction {
program_id,
accounts: vec![],
data: vec![],
};
// Create transaction with instruction
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
// Sign transaction
transaction.sign(&[&payer], recent_blockhash);
let transaction_result = banks_client.process_transaction(transaction).await;
assert!(transaction_result.is_ok());
}
}

Запустите тест с помощью команды cargo test-sbf. В журнале программы отобразится "Hello, world!".

Terminal
cargo test-sbf

Пример вывода:

Terminal
running 1 test
[2024-10-18T21:24:54.889570000Z INFO solana_program_test] "hello_world" SBF program from /hello_world/target/deploy/hello_world.so, modified 35 seconds, 828 ms, 268 µs and 398 ns ago
[2024-10-18T21:24:54.974294000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM invoke [1]
[2024-10-18T21:24:54.974814000Z DEBUG solana_runtime::message_processor::stable_log] Program log: Hello, world!
[2024-10-18T21:24:54.976848000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM consumed 140 of 200000 compute units
[2024-10-18T21:24:54.976868000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM success
test test::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.13s

Разверните программу

Теперь разверните программу. При локальной разработке мы можем использовать solana-test-validator.

Сначала настройте Solana CLI для использования локального кластера Solana.

Terminal
solana config set -ul

Пример вывода:

Config File: /.config/solana/cli/config.yml
RPC URL: http://localhost:8899
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: /.config/solana/id.json
Commitment: confirmed

Откройте новый терминал и выполните команду solana-test-validators, чтобы запустить локальный валидатор.

Terminal
solana-test-validator

Пока тестовый валидатор работает, выполните команду solana program deploy в отдельном терминале, чтобы развернуть программу на локальном валидаторе.

Terminal
solana program deploy ./target/deploy/hello_world.so

Пример вывода:

Program Id: 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
Signature:
5osMiNMiDZGM7L1e2tPHxU8wdB8gwG8fDnXLg5G7SbhwFz4dHshYgAijk4wSQL5cXiu8z1MMou5kLadAQuHp7ybH

Вы можете проверить ID программы и подпись транзакции на Solana Explorer. Обратите внимание, что кластер в Solana Explorer также должен быть localhost. Опция "Custom RPC URL" в Solana Explorer по умолчанию использует http://localhost:8899.

Создайте пример клиента

Далее мы покажем, как вызвать программу с помощью клиента на Rust.

Сначала создайте каталог examples и файл client.rs.

Terminal
mkdir -p examples
touch examples/client.rs

Добавьте следующее в Cargo.toml.

Cargo.toml
[[example]]
name = "client"
path = "examples/client.rs"

Добавьте зависимость solana-client.

Terminal
cargo add solana-client@1.18.26 --dev

Добавьте клиента

Добавьте следующий код в examples/client.rs. Это скрипт клиента на Rust, который создает новый keypair для оплаты комиссий за транзакции, а затем вызывает программу "Hello, world".

examples/client.rs
use solana_client::rpc_client::RpcClient;
use solana_sdk::{
commitment_config::CommitmentConfig,
instruction::Instruction,
pubkey::Pubkey,
signature::{Keypair, Signer},
transaction::Transaction,
};
use std::str::FromStr;
#[tokio::main]
async fn main() {
// Program ID (replace with your actual program ID)
let program_id = Pubkey::from_str("4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz").unwrap();
// Connect to the Solana devnet
let rpc_url = String::from("http://127.0.0.1:8899");
let client = RpcClient::new_with_commitment(rpc_url, CommitmentConfig::confirmed());
// Generate a new keypair for the payer
let payer = Keypair::new();
// Request airdrop
let airdrop_amount = 1_000_000_000; // 1 SOL
let signature = client
.request_airdrop(&payer.pubkey(), airdrop_amount)
.expect("Failed to request airdrop");
// Wait for airdrop confirmation
loop {
let confirmed = client.confirm_transaction(&signature).unwrap();
if confirmed {
break;
}
}
// Create the instruction
let instruction = Instruction::new_with_borsh(
program_id,
&(), // Empty instruction data
vec![], // No accounts needed
);
// Add the instruction to new transaction
let mut transaction = Transaction::new_with_payer(&[instruction], Some(&payer.pubkey()));
transaction.sign(&[&payer], client.get_latest_blockhash().unwrap());
// Send and confirm the transaction
match client.send_and_confirm_transaction(&transaction) {
Ok(signature) => println!("Transaction Signature: {}", signature),
Err(err) => eprintln!("Error sending transaction: {}", err),
}
}

Замените ID программы

Перед запуском скрипта замените ID программы в приведенном выше фрагменте кода на ID вашей программы.

Вы можете получить ID вашей программы, выполнив следующую команду.

Terminal
solana address -k ./target/deploy/hello_world-keypair.json

Вызовите программу

Запустите скрипт клиента с помощью следующей команды.

Terminal
cargo run --example client

Пример вывода:

Transaction Signature: 54TWxKi3Jsi3UTeZbhLGUFX6JQH7TspRJjRRFZ8NFnwG5BXM9udxiX77bAACjKAS9fGnVeEazrXL4SfKrW7xZFYV

Вы можете проверить подпись транзакции в Solana Explorer (локальный кластер), чтобы увидеть "Hello, world!" в журнале программы.

Обновление программы

Программы Solana можно обновлять, повторно развертывая их с тем же идентификатором программы. Обновите программу в src/lib.rs, чтобы она выводила "Hello, Solana!" вместо "Hello, world!".

lib.rs
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
-
msg!("Hello, world!");
+
msg!("Hello, Solana!");
Ok(())
}

Протестируйте обновленную программу, запустив команду cargo test-sbf.

Terminal
cargo test-sbf

Вы должны увидеть "Hello, Solana!" в журнале программы.

Terminal
running 1 test
[2024-10-23T19:28:28.842639000Z INFO solana_program_test] "hello_world" SBF program from /code/misc/delete/hello_world/target/deploy/hello_world.so, modified 4 minutes, 31 seconds, 435 ms, 566 µs and 766 ns ago
[2024-10-23T19:28:28.934854000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM invoke [1]
[2024-10-23T19:28:28.936735000Z DEBUG solana_runtime::message_processor::stable_log] Program log: Hello, Solana!
[2024-10-23T19:28:28.938774000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM consumed 140 of 200000 compute units
[2024-10-23T19:28:28.938793000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM success
test test::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s

Повторное развертывание программы

Выполните команду cargo build-sbf, чтобы сгенерировать обновленный файл .so.

Terminal
cargo build-sbf

Повторно разверните программу, используя команду solana program deploy.

Terminal
solana program deploy ./target/deploy/hello_world.so

Снова запустите клиентский код и проверьте подпись транзакции в Solana Explorer, чтобы увидеть "Hello, Solana!" в журнале программы.

Terminal
cargo run --example client

Закрытие программы

Вы можете закрыть свою программу Solana, чтобы вернуть SOL, выделенные на аккаунт. Закрытие программы необратимо, поэтому его следует выполнять с осторожностью.

Чтобы закрыть программу, используйте команду solana program close <PROGRAM_ID>. Например:

Terminal
solana program close 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
--bypass-warning

Пример вывода:

Closed Program Id 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz, 0.1350588 SOL
reclaimed

Обратите внимание, что после закрытия программы её идентификатор программы не может быть повторно использован. Попытка развернуть программу с ранее закрытым идентификатором программы приведет к ошибке.

Error: Program 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz has been closed, use
a new Program Id

Повторное развертывание закрытой программы

Если вам нужно повторно развернуть программу с тем же исходным кодом после её закрытия, вы должны сгенерировать новый идентификатор программы. Чтобы сгенерировать новую keypair для программы, выполните следующую команду:

Terminal
solana-keygen new -o ./target/deploy/hello_world-keypair.json --force

В качестве альтернативы вы можете удалить существующий файл keypair (например, ./target/deploy/hello_world-keypair.json) и снова запустить cargo build-sbf, что создаст новый файл keypair.

Создайте новую программу

Сначала создайте новый проект на Rust, используя стандартную команду cargo init с флагом --lib.

Terminal
cargo init hello_world --lib

Перейдите в каталог проекта. Вы должны увидеть файлы по умолчанию src/lib.rs и Cargo.toml.

Terminal
cd hello_world

Добавьте зависимость solana-program

Далее добавьте зависимость solana-program. Это минимальная зависимость, необходимая для создания программы для Solana.

Terminal
cargo add solana-program@1.18.26

Затем добавьте следующий фрагмент в Cargo.toml. Если вы не включите эту конфигурацию, каталог target/deploy не будет создан при сборке программы.

Добавьте тип библиотеки

Затем добавьте следующий фрагмент в Cargo.toml.

[lib]
crate-type = ["cdylib", "lib"]

Если вы не включите эту конфигурацию, каталог target/deploy не будет создан при сборке программы.

Добавьте код программы

Затем замените содержимое src/lib.rs следующим кодом. Это минимальная программа Solana, которая выводит "Hello, world!" в журнал программы при её вызове.

Макрос msg! используется в программах Solana для вывода сообщения в журнал программы.

Соберите программу

Затем соберите программу с помощью команды cargo build-sbf.

Terminal
cargo build-sbf

Эта команда создаёт каталог target/deploy, содержащий два важных файла:

  1. Файл .so (например, hello_world.so): Это скомпилированная программа Solana, которая будет развернута в сети как "умный контракт".
  2. Файл keypair (например, hello_world-keypair.json): Публичный ключ этой keypair используется как идентификатор программы при её развертывании.

Чтобы просмотреть идентификатор программы, выполните следующую команду в терминале. Эта команда выводит публичный ключ keypair по указанному пути к файлу:

Terminal
solana address -k ./target/deploy/hello_world-keypair.json

Пример вывода:

4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz

Добавьте зависимости для тестирования

Далее протестируйте программу, используя крейт solana-program-test. Добавьте следующие зависимости в Cargo.toml.

Terminal
cargo add solana-program-test@1.18.26 --dev
cargo add solana-sdk@1.18.26 --dev
cargo add tokio --dev

Протестируйте программу

Добавьте следующий тест в src/lib.rs, ниже кода программы. Это модуль тестирования, который вызывает программу "Hello, world".

Запустите тест с помощью команды cargo test-sbf. В журнале программы отобразится "Hello, world!".

Terminal
cargo test-sbf

Пример вывода:

Terminal
running 1 test
[2024-10-18T21:24:54.889570000Z INFO solana_program_test] "hello_world" SBF program from /hello_world/target/deploy/hello_world.so, modified 35 seconds, 828 ms, 268 µs and 398 ns ago
[2024-10-18T21:24:54.974294000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM invoke [1]
[2024-10-18T21:24:54.974814000Z DEBUG solana_runtime::message_processor::stable_log] Program log: Hello, world!
[2024-10-18T21:24:54.976848000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM consumed 140 of 200000 compute units
[2024-10-18T21:24:54.976868000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM success
test test::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.13s

Разверните программу

Теперь разверните программу. При локальной разработке мы можем использовать solana-test-validator.

Сначала настройте Solana CLI для использования локального кластера Solana.

Terminal
solana config set -ul

Пример вывода:

Config File: /.config/solana/cli/config.yml
RPC URL: http://localhost:8899
WebSocket URL: ws://localhost:8900/ (computed)
Keypair Path: /.config/solana/id.json
Commitment: confirmed

Откройте новый терминал и выполните команду solana-test-validators, чтобы запустить локальный валидатор.

Terminal
solana-test-validator

Пока тестовый валидатор работает, выполните команду solana program deploy в отдельном терминале, чтобы развернуть программу на локальном валидаторе.

Terminal
solana program deploy ./target/deploy/hello_world.so

Пример вывода:

Program Id: 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
Signature:
5osMiNMiDZGM7L1e2tPHxU8wdB8gwG8fDnXLg5G7SbhwFz4dHshYgAijk4wSQL5cXiu8z1MMou5kLadAQuHp7ybH

Вы можете проверить ID программы и подпись транзакции на Solana Explorer. Обратите внимание, что кластер в Solana Explorer также должен быть localhost. Опция "Custom RPC URL" в Solana Explorer по умолчанию использует http://localhost:8899.

Создайте пример клиента

Далее мы покажем, как вызвать программу с помощью клиента на Rust.

Сначала создайте каталог examples и файл client.rs.

Terminal
mkdir -p examples
touch examples/client.rs

Добавьте следующее в Cargo.toml.

Cargo.toml
[[example]]
name = "client"
path = "examples/client.rs"

Добавьте зависимость solana-client.

Terminal
cargo add solana-client@1.18.26 --dev

Добавьте клиента

Добавьте следующий код в examples/client.rs. Это скрипт клиента на Rust, который создает новый keypair для оплаты комиссий за транзакции, а затем вызывает программу "Hello, world".

Замените ID программы

Перед запуском скрипта замените ID программы в приведенном выше фрагменте кода на ID вашей программы.

Вы можете получить ID вашей программы, выполнив следующую команду.

Terminal
solana address -k ./target/deploy/hello_world-keypair.json

Вызовите программу

Запустите скрипт клиента с помощью следующей команды.

Terminal
cargo run --example client

Пример вывода:

Transaction Signature: 54TWxKi3Jsi3UTeZbhLGUFX6JQH7TspRJjRRFZ8NFnwG5BXM9udxiX77bAACjKAS9fGnVeEazrXL4SfKrW7xZFYV

Вы можете проверить подпись транзакции в Solana Explorer (локальный кластер), чтобы увидеть "Hello, world!" в журнале программы.

Обновление программы

Программы Solana можно обновлять, повторно развертывая их с тем же идентификатором программы. Обновите программу в src/lib.rs, чтобы она выводила "Hello, Solana!" вместо "Hello, world!".

lib.rs
pub fn process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
_instruction_data: &[u8],
) -> ProgramResult {
-
msg!("Hello, world!");
+
msg!("Hello, Solana!");
Ok(())
}

Протестируйте обновленную программу, запустив команду cargo test-sbf.

Terminal
cargo test-sbf

Вы должны увидеть "Hello, Solana!" в журнале программы.

Terminal
running 1 test
[2024-10-23T19:28:28.842639000Z INFO solana_program_test] "hello_world" SBF program from /code/misc/delete/hello_world/target/deploy/hello_world.so, modified 4 minutes, 31 seconds, 435 ms, 566 µs and 766 ns ago
[2024-10-23T19:28:28.934854000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM invoke [1]
[2024-10-23T19:28:28.936735000Z DEBUG solana_runtime::message_processor::stable_log] Program log: Hello, Solana!
[2024-10-23T19:28:28.938774000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM consumed 140 of 200000 compute units
[2024-10-23T19:28:28.938793000Z DEBUG solana_runtime::message_processor::stable_log] Program 1111111QLbz7JHiBTspS962RLKV8GndWFwiEaqKM success
test test::test_hello_world ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.14s

Повторное развертывание программы

Выполните команду cargo build-sbf, чтобы сгенерировать обновленный файл .so.

Terminal
cargo build-sbf

Повторно разверните программу, используя команду solana program deploy.

Terminal
solana program deploy ./target/deploy/hello_world.so

Снова запустите клиентский код и проверьте подпись транзакции в Solana Explorer, чтобы увидеть "Hello, Solana!" в журнале программы.

Terminal
cargo run --example client

Закрытие программы

Вы можете закрыть свою программу Solana, чтобы вернуть SOL, выделенные на аккаунт. Закрытие программы необратимо, поэтому его следует выполнять с осторожностью.

Чтобы закрыть программу, используйте команду solana program close <PROGRAM_ID>. Например:

Terminal
solana program close 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz
--bypass-warning

Пример вывода:

Closed Program Id 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz, 0.1350588 SOL
reclaimed

Обратите внимание, что после закрытия программы её идентификатор программы не может быть повторно использован. Попытка развернуть программу с ранее закрытым идентификатором программы приведет к ошибке.

Error: Program 4Ujf5fXfLx2PAwRqcECCLtgDxHKPznoJpa43jUBxFfMz has been closed, use
a new Program Id

Повторное развертывание закрытой программы

Если вам нужно повторно развернуть программу с тем же исходным кодом после её закрытия, вы должны сгенерировать новый идентификатор программы. Чтобы сгенерировать новую keypair для программы, выполните следующую команду:

Terminal
solana-keygen new -o ./target/deploy/hello_world-keypair.json --force

В качестве альтернативы вы можете удалить существующий файл keypair (например, ./target/deploy/hello_world-keypair.json) и снова запустить cargo build-sbf, что создаст новый файл keypair.

lib.rs
Cargo.toml
[package]
name = "hello_world"
version = "0.1.0"
edition = "2021"
[dependencies]

Is this page helpful?

Содержание

Редактировать страницу