January 31

Создание приватного DApp для лотереи на Aleo 

Лотереи являются одним из самых популярных способов розыгрыша призов, но в традиционных системах существует риск непрозрачности и мошенничества. Aleo решает эти проблемы с помощью ZK-SNARKs, обеспечивая анонимность и верифицируемую честность. В данной статье мы рассмотрим, как создать приватный DApp для лотереи на Aleo, позволяющий участникам сохранять конфиденциальность, а организаторам – гарантировать честный розыгрыш.


Задачи лотереи и роль Aleo

  1. Сбор заявок – участники вносят средства и получают билеты, не раскрывая личных данных.
  2. Случайный выбор победителя – технология ZK-SNARKs обеспечивает доказательство честной генерации случайности.
  3. Выплата выигрыша – приз достается победителю без утечек информации о других участниках.

Aleo позволяет хранить данные о билетах и транзакциях в зашифрованном виде, исключая возможность подделки или просмотра информации посторонними.


Разработка смарт-контракта

Установка окружения

Установите Aleo CLI и создайте новый проект:

leo 
# Установка Aleo CLI
curl --proto '=https' --tlsv1.2 -sSf https://install.aleo.org | sh

# Создание нового проекта
leo new private_lottery
cd private_lottery

Структуры данных

leo private_lottery.aleo;

// Структура участника лотереи
struct Participant {
    address: address,
    ticket_hash: field    // Уникальный идентификатор билета, зашифрованный
}

// Структура лотереи
struct Lottery {
    participants: [Participant; 100], // Максимум 100 участников
    ticket_price: u64,                // Стоимость билета
    pot: u64,                         // Общий призовой фонд
    seed: field                       // Случайный seed (обновляется организатором)
}
  1. participants – хранит список участников лотереи.
  2. ticket_price – фиксированная стоимость билета.
  3. pot – общий призовой фонд, формируется из взносов.
  4. seed – служит исходным значением для генерации случайного победителя.

Функция инициализации лотереи

leo function init_lottery(ticket_price: u64, seed: field) -> Lottery {
    let mut init_participants = [Participant {
        address: 0address,
        ticket_hash: 0field
    }; 100];

    let new_lottery = Lottery {
        participants: init_participants,
        ticket_price: ticket_price,
        pot: 0u64,
        seed: seed
    };
    return new_lottery;
}

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


Функция покупки билета

leo function buy_ticket(lottery: Lottery, buyer: address, ticket_hash: field, payment: u64) -> Lottery {
    if payment < lottery.ticket_price {
        abort("Недостаточно средств для покупки билета!");
    }

    let mut updated_lottery = lottery;
    let mut added = false;

    // Ищем пустое место в массиве
    for i in 0..100 {
        if updated_lottery.participants[i].address == 0address {
            updated_lottery.participants[i] = Participant {
                address: buyer,
                ticket_hash: ticket_hash
            };
            updated_lottery.pot += payment;
            added = true;
            break;
        }
    }

    if !added {
        abort("Все билеты проданы или достигнут лимит участников!");
    }

    return updated_lottery;
}

Здесь:

  • buyer – адрес покупателя.
  • ticket_hash – зашифрованный идентификатор билета (например, результат функции hash).
  • payment – сумма, уплаченная за билет (должна быть не меньше ticket_price).

Фонд лотереи (pot) увеличивается на величину уплаты.


Функция обновления seed

leo function update_seed(lottery: Lottery, new_seed: field) -> Lottery {
    let mut updated_lottery = lottery;
    updated_lottery.seed = new_seed;
    return updated_lottery;
}

Организатор может обновлять seed для более надежной генерации случайности (например, с помощью внешнего оракула или псевдослучайного генератора).


Функция определения победителя

leo function draw_winner(lottery: Lottery) -> address {
    let mut valid_tickets = 0u64;

    // Считаем количество участвующих билетов
    for i in 0..100 {
        if lottery.participants[i].address != 0address {
            valid_tickets += 1;
        }
    }

    // Если нет участников, возврат 0address
    if valid_tickets == 0u64 {
        return 0address;
    }

    // Простейший способ: берем seed, приводим к u64, берем мод по количеству билетов
    let seed_u64 = lottery.seed.to_u64();
    let winner_index = seed_u64 % valid_tickets;

    // Возвращаем адрес победителя
    let mut current_index = 0u64;

    for i in 0..100 {
        if lottery.participants[i].address != 0address {
            if current_index == winner_index {
                return lottery.participants[i].address;
            }
            current_index += 1;
        }
    }

    return 0address; // На случай ошибок
}

Данная функция выбирает победителя на основе seed. В реальной ситуации лучше использовать более сложный механизм генерации случайности (например, сочетать несколько источников или применять криптографические коммитменты).


Функция выплаты выигрыша

leo function payout_winner(lottery: Lottery, winner_address: address) -> (Lottery, u64) {
    let mut updated_lottery = lottery;
    let prize = updated_lottery.pot;
    updated_lottery.pot = 0;
    return (updated_lottery, prize);
}

После определения победителя вызывается функция, возвращающая обновленную лотерею (с обнуленным призовым фондом) и сумму выигрыша, которую можно перевести победителю.


Деплой и тестирование

Компиляция контракта

bash leo build

Развертывание в сети Aleo

bash leo deploy private_lottery.aleo

Пример использования

  1. Инициализация лотереи
    leo run init_lottery 100 0xabc123...
  2. Покупка билета
    leo run buy_ticket <lottery_state> aleo1xyz... 0xdeadbeef... 100
  3. Обновление seed
    leo run update_seed <lottery_state> 0x1234abcd...
  4. Определение победителя
    leo run draw_winner <lottery_state>
  5. Выплата выигрыша
    leo run payout_winner <lottery_state> aleo1winner...

Заключение

Приватный DApp для лотереи на Aleo объединяет:

Конфиденциальность – билеты и транзакции зашифрованы.

Честность – механизмы случайности и верифицируемые транзакции.

Децентрализацию – не требуется посредник, которому нужно доверять.

Такой подход можно адаптировать для государственных розыгрышей, благотворительных лотерей и корпоративных акций. При этом соблюдаются принципы безопасности и прозрачности, так необходимые в индустрии.