# FunС ### Общая инфа На текущий момент нет официальной документации по языку. FunC позволяет на более высоком уровне чем Fift создать смарт-контракт, используя такие примитивы как функции ### Пример из доки ``` ;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges) ;; accepts orders for up to 254 internal messages (transfers) in one external message () recv_internal(slice in_msg) impure { ;; do nothing for internal messages } () recv_external(slice in_msg) impure { var signature = in_msg~load_bits(512); var cs = in_msg; var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); throw_if(35, valid_until <= now()); var ds = get_data().begin_parse(); var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); ds.end_parse(); throw_unless(33, msg_seqno == stored_seqno); throw_unless(34, subwallet_id == stored_subwallet); throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); var dict = cs~load_dict(); cs.end_parse(); accept_message(); int i = -1; do { (i, var cs, var f) = dict.idict_get_next?(16, i); if (f) { var mode = cs~load_uint(8); send_raw_message(cs~load_ref(), mode); } } until (~ f); set_data(begin_cell() .store_uint(stored_seqno + 1, 32) .store_uint(stored_subwallet, 32) .store_uint(public_key, 256) .end_cell()); } ;; Get methods int seqno() method_id { return get_data().begin_parse().preload_uint(32); } ``` ## Построчно разберем код Первая функция: ``` () recv_internal(slice in_msg) impure { ;; do nothing for internal messages } ``` Она принимает внутреннее (internal) сообщение от контракта в сети TON. Нужна для коммункации между контрактами. ``` Указывает на то, что функция ничего не возвращает void | | Принимает slice объект от другого контракта | | | | Декоратор, видимо говорящий о том, что функция не явлеятся getter, деталей пока нет V V V () recv_internal(slice in_msg) impure { } ``` Фактически функция игнорирует любой запрос со стороны контракта, а не пользователя. Пользователь делает external сообщения в сеть. Оба типа сообщений даже у нод по-разному хранятся. Важно понимать, что slice тут, по факту просто куча байтов произвольной длины ____ ``` () recv_external(slice in_msg) impure { ``` Имеет такую же логику как и internal, отличается лишь неймингом, который очень ВАЖЕН! ``` var signature = in_msg~load_bits(512); ``` Обозначем переменную signature, которая грузится из первых 512 бит slice переменной in_msg, которую нам передали По guidelines от создателей они решили разбить на участки slice in_msg Первый участок - подпись 512 бит ``` a~load_bits(b); ``` load_bits возвращает первые (мб и нет) b бит из slice переменной a ``` var cs = in_msg; var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); ``` Тут сначала непонятно зачем создается cs переменная из in_msg, это может быть связано с тем, что in_msg и cs находятся в разных областях памяти и cs переменная на чтение будет требовать меньше или 0 газа в отличии от in_msg (а может и нет, просто по фану сделали) ``` var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); ``` Массовое создание и присвоение значений var (a, b, c ... , d) = (z, y, x, ... , f) Тут опять загружается 32 бита из slice переменной Видимо после заргрузки N бит, переменная slice уменьшается на эти N бит Собственно создаются переменные: subwallet_id - некий id кошелька valid_until - момент, до какого числа сообщение external будет действительным. Иначе злоумышленник может похитить ваше сообщение из пула всех сообщений и выполнить его в любое время msg_seqno - так называемый nonce. Некое число, которое отображает количество уже подписанных и отправленных тобой транзакций. Если его не будет, то злоумышленник может украсть твою подпись, сумму, автора и повторять заносить эти данные как новые транзакции ``` throw_if(35, valid_until <= now()); ``` throw_if выбрасывает ошибку с кодом 35, если условие valid_until <= now() не выполняется now() - позволяет получить текущее время Проверка нужна на то, что не просрачилась транзакция ``` var ds = get_data().begin_parse(); var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); ds.end_parse(); ``` get_data() - метод для того, чтобы начать считывать данные из клеток (я так понимаю, что за контрактом закреплен некий storage, то, как из него читать и писать примерно известно) get_data() - открывает некий поток, а ля io.open / close get_data().begin_parse() - позволяет из него читать ``` var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); ``` Тут мы получем уже сохранненные данные из клетки в memory переменные stored_seqno, stored_subwallet, public_key - очевидно, что это такое ``` throw_unless(33, msg_seqno == stored_seqno); throw_unless(34, subwallet_id == stored_subwallet); throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); ``` проверяем, что nonce сходится, иначе кидаем ошибку с кодом 33 проверяем, что subwallet_id сходится, иначе кидаем ошибку с кодом 34 проверяем, что подпись сходится с публичным ключом владельца, иначе кидаем ошибку с кодом 35 ``` check_signature(slice_hash(in_msg), signature, public_key) ``` Функция проверки подписи, где нужно передать подпись, публичный ключ и hash от сообщения отправителя ``` var dict = cs~load_dict(); cs.end_parse(); ``` Идет загрузка dictionary объекта (hashmap видимо). У него есть отдельный метод для загрузки. Видимо в памяти он аллоцирован иначе чем slice данные и читается оттуда как из потока