Почему генератор псевдослучайных чисел в Tact возвращает одинаковые значения?

    При разработке смарт-контрактов на языке Tact в экосистеме TON многие новички сталкиваются с неожиданным поведением генератора псевдослучайных чисел (PRNG). В тестах на TypeScript или в эмуляторе вы можете заметить, что при многократном вызове функции рандома числа повторяются и всегда одинаковы. Это не баг - это особенность блокчейна.

    Детерминизм блокчейна TON

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

    Как работает PRNG в Tact

    Генератор псевдослучайных чисел в Tact использует встроенную функцию randomInt или random, которая берёт seed (начальное значение) из глобального состояния блокчейна - в частности, из поля randomSeed виртуальной машины TVM. Однако в тестовой среде seed часто фиксирован или инициализируется нулевым значением, если вы не задали его явно. Именно поэтому при каждом запуске вы видите одну и ту же последовательность чисел.

    Как изменить поведение и получать разные числа?

    Чтобы PRNG выдавал разные значения при каждом вызове, необходимо менять seed. В реальном смарт-контракте seed автоматически обновляется при каждой транзакции, так как он зависит от хеша последнего блока, времени и других непостоянных данных. В тестах же вы можете вручную задать seed с помощью методов, предоставляемых фреймворком (например, в sandbox или test-utils для Tact).

    Вот простой пример на Tact:

    fun generateRandom(): Int {    return randomInt(1, 100);}

    Для тестирования используйте:

    import { randomSeed } from '@tact-lang/emulator';// Установите seed перед каждым тестомrandomSeed(Date.now());

    Практические рекомендации

    • Используйте внешние оракулы - для критически важной случайности (например, розыгрыши) применяйте Chainlink VRF или аналогичные сервисы, которые передают seed извне.
    • Комбинируйте источники - смешивайте seed с хешем сообщения, адресом отправителя и временем блока.
    • Тестируйте с разными seed - в юнит-тестах явно задавайте seed, чтобы проверить разные сценарии.

    Заключение

    Повторение чисел в PRNG Tact при тестировании - нормальное поведение детерминированного блокчейна TON. Для получения уникальных значений в продакшене используйте изменяемый seed от транзакций, а в тестах задавайте seed вручную. Это позволит вам проверять логику контракта без сюрпризов.

    Часто задаваемые вопросы