Оракулы RedStone
Эта страница переведена сообществом на русский язык, но нуждается в улучшениях. Если вы хотите принять участие в переводе свяжитесь с @alexgton.
Как оракулы RedStone работают с TON
Оракулы RedStone используют альтернативную конструкцию предоставления данных оракула смарт-контрактам. Вместо постоянного сохранения данных в хранилище контракта (поставщиками данных) информация передается on-chain только при необходимости (конечными пользователями). До этого момента данные остаются в децентрализованном слое кэша, который поддерживается шлюзами RedStone light cache и протоколе трансляции потоковых данных. Данные передаются в контракт конечными пользователями, которые должны прикреплять подписанные пакеты данных к своим вызовам функций. Целостность информации проверяется on-chain посредством проверки подписей.
Чтобы узнать больше о дизайне оракулов RedStone, перейдите в документацию RedStone
Ссылки на документацию
Смарт-контракты
price_manager.fc
- Пример контракта оракула, который использует данные оракулов RedStone price_manager.fc, написанный на FunC. Требуется Обновление TVM 2023.07.
начальные данные
Как упоминалось выше, пакеты данных, переданные в контракт, проверяются проверкой подписи.
Чтобы быть учтенными для достижения signer_core_threshold
, подписант, подписывающий переданные данные,
должен быть одним из signers
, переданных в исходных данных. Также необходимо signer_count_threshold
, чтобы быть переданным.
Из-за архитектуры контрактов TON исходные данные должны соответствовать структуре хранения контракта, которая построена следующим образом:
begin_cell()
.store_uint(signer_count_threshold, 8) /// number as passed below
.store_uint(timestamp, TIMESTAMP_BITS) /// initially 0 representing the epoch 0
.store_ref(signers) /// serialized tuple of values passed below
.end_cell();
Значение signers
должно быть передано как сериализованный tuple
из int
.См. tuple.
В параметрах функции ниже каждый feed_id
представляет собой строку, закодированную в int
, что означает, что это значение,
состоящее из шестнадцатеричных значений определенных букв в строке. Например:
'ETH'
как int
равно 0x455448
в шестнадцатеричном или 4543560
в десятичном, как 256*256*ord('E')+256*ord('T')+ord('H')
.
Вы можете использовать: feed_id=hexlify(toUtf8Bytes(feed_string))
для преобразования определенных значений или
конечной точки
Значение feed_ids
должно быть передано как сериализованный tuple
из int
.
Значение payload
упаковывается из массива байтов, представляющих сериализованную полезную нагрузку RedStone.См. раздел Упаковка полезной нагрузки TON RedStone ниже, а также файл constants.fc, содержащий все необходимые константы длины int
.
get_prices
(cell) get_prices_v2(cell data_feed_ids, cell payload) method_id;
Функция обрабатывает on-chain payload
, переданный в качестве аргумента,
и возвращает cell
агрегированных значений каждого канала, переданного в качестве идентификатора внутри feed_ids
.
Из-за ограничения длины метода HTTP GET в TON API v4, функция написана для TON API v2.
Это просто функции method_id
— они не изменяют хранилище контракта и не потребляют TON.
OP_REDSTONE_WRITE_PRICES
Независимо от обработки на лету, также существует метод обработки payload
on-chain, но
сохраняющий/записывающий агрегированные значения в хранилище контракта. Значения сохраняются в хранилище контракта и затем могут быть прочитаны с помощью функции read_prices
. Метку времени последнего сохранения/записи данных в контракт можно прочитать с помощью функции read_timestamp
.
Метод должен быть вызван как внутреннее сообщение TON. Аргументы сообщения:
int
, представляющий имя RedStone_Write_Prices, хешированное keccak256, как определено в constants.tscell
- ссылка, представляющаяdata_feed_ids
как сериализованныйtuple
изint
cell
- ссылка, представляющая упакованную полезную нагрузку RedStone
int op = in_msg_body~load_uint(OP_NUMBER_BITS);
if (op == OP_REDSTONE_WRITE_PRICES) {
cell data_feeds_cell = in_msg_body~load_ref();
cell payload_cell = in_msg_body~load_ref();
// ...
}
Это внутреннее сообщение - оно потребляет газ и изменяет хранилище контракта, поэтому должно быть оплачено TON.
Посмотрите, как это работает на: https://ton-showroom.redstone.finance/
read_prices
(tuple) read_prices(tuple data_feed_ids) method_id;
Функция считывает значения, сохраняющиеся в хранилище контракта, и возвращает кортеж, соответствующий переданным feed_ids
.
Функция не изменяет хранилище и может считывать только агрегированные значения feed_ids
, сохраненные с помощью функции write_prices
.
Это просто функция method_id
- она не и зменяет хранилище контракта и не потребляет TON.
read_timestamp
(int) read_timestamp() method_id;
Возвращает временную метку данных, которые были в последний раз сохранены/записаны в хранилище контракта с помощью сообщения OP_REDSTONE_WRITE_PRICES
.
Это просто функция method_id
— она не изменяет хранилище контракта и не потребляет TON.
price_feed.fc
Из-за архитектуры контрактов TON исходные данные должны соответствовать структуре хранения контракта, которая построена следующим образом:
beginCell()
.storeUint(BigInt(hexlify(toUtf8Bytes(this.feedId))), consts.DATA_FEED_ID_BS * 8)
.storeAddress(Address.parse(this.managerAddress))
.storeUint(0, consts.DEFAULT_NUM_VALUE_BS * 8) /// initially 0 representing the epoch 0
.storeUint(0, consts.TIMESTAMP_BS * 8)
.endCell();
Чтобы определить исходные (хранимые) данные для контракта Price feed, используйте предопределенный класс PriceFeedInitData.ts.
OP_REDSTONE_FETCH_DATA
Независимо от считывания значений, сохраняющихся в контракте, из-за пределов сети,
существует возможность получения значения, хранящегося в контракте, для feed_id
on-chain напрямую.
Необходимо вызвать внутреннее сообщение OP_REDSTONE_FETCH_DATA
. Аргументы сообщения:
int
, представляющий имяRedStone_Fetch_Data
, хешированное keccak256, как определено в constants.tsint
, представляющий значениеfeed_id
.slice
, представляющийinitial_sender
сообщения, чтобы они могли перенести оставшийся баланс транзакции при возврате транзакции.
int op = in_msg_body~load_uint(OP_NUMBER_BITS);
if (op == OP_REDSTONE_FETCH_DATA) {
int feed_id = in_msg_body~load_uint(DATA_FEED_ID_BITS);
cell initial_payload = in_msg_body~load_ref();
// ...
}