RedStone Oracles
RedStone oracles 如何与 TON 一起工作
RedStone oracles采用另一种设计,为智能合约提供预言机数据。数据提供者不在合约存储中持续保存数据,而是在终端用户需要时才将信息引入链上。在此之前,数据一直保存在去中心化缓存层中,该层由 RedStone light 缓存网关和流数据广播协议提供支持。数据由终端用户传输至合约,终端用户应在函数调用中附加已签名的数据包。信息完整性通过签名检查在链上进行验证。
要了解更多有关 RedStone 算法设计的信息,请访问 RedStone docs。
文件链接
智能合约
price_manager.fc
- 用 FunC 编写的消费 RedStone Oracle数据的 Oracle 合约示例 price_manager.fc。它需要 TVM Upgrade 2023.07。
初始化数据
如上所述,传输到合约的数据包是通过签名检查来验证的。
为达到 "签名者数量阈值",签署所传递数据
的签名者应是初始数据中传递的 "签名者 "之一。此外,还需要
传递 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
的值应以序列化的 int
的 tuple
形式传递。
参见 tuple。
在下面的函数参数中,每个 feed_id
都是一个编码为 int
的字符串,这意味着,这是一个由字符串中特定字母的十六进制值组成的值
。例如:
'ETH'
为 int
,十六进制为0x455448
,十进制为4543560
,即256*256*ord('E')+256*ord('T')+ord('H')
。
您可以使用 feed_id=hexlify(toUtf8Bytes(feed_string))
转换特定值或
endpoint
feed_ids
的值应以 int
的序列化 元组
形式传递。
payload
值由代表序列化 RedStone 有效载荷的字节数组打包而成。
请参阅下文 TON RedStone payload packing 部分,以及包含所有所需 int
长度常量的文件constants.fc。
get_prices
(cell) get_prices_v2(cell data_feed_ids, cell payload) method_id;
该函数在链上处理作为参数传递的 payload
,并返回作为标识符在 feed_ids
中传递的每个 feed 的汇总值的 cell
。
由于 TON API v4 中 HTTP GET 方法的长度限制,该函数是为 TON API v2 编写的。
这只是一个 method_id
函数--它们不会修改合约的存储空间,也不会消耗 TON。
OP_REDSTONE_WRITE_PRICES
无论采用哪种即时处理方式,也存在一种在链上处理 payload
的方法,但
会将汇总值保存/写入合约的存储空间。这些值会保存在合约的存储空间中,然后可以使用 read_prices
函数读取。使用 read_timestamp
函数可以读取上次保存/写入合约的数据的时间戳。
该方法必须以 TON 内部报文的形式调用。信息参数如下
- 一个
int
表示 RedStone_Write_Prices 名称,由 keccak256 散列,在 constants.ts 中定义为 。 - 一个
cell
- ref,将data_feed_ids
表示为序列化的int
s.tuple`. - 一个
cell
- 表示打包的 RedStone 有效载荷的 ref
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();
// ...
}
这是一条内部信息--它消耗 GAS 并修改合约的存储空间,因此必须以 TON 为单位支付。
请访问: https://ton-showroom.redstone.finance/ 了解其工作原理
read_prices
(tuple) read_prices(tuple data_feed_ids) method_id;
该函数读取合约存储中的持久化值,并返回与
传递的 feed_ids
相对应的元组。
该函数不会修改存储空间,只能读取
使用 write_prices
函数保存的 "feed_ids "的汇总值。
这只是一个 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();
要定义价格进给合约的初始(存储)数据,请使用预定义的 类 PriceFeedInitData.ts。
OP_REDSTONE_FETCH_DATA
无论从网络外部读取持久化在合约中的值,
,都有可能直接在链上获取存储在合约中的 feed_id
值。
必须调用内部消息 OP_REDSTONE_FETCH_DATA
。消息参数如下
- 一个
int
表示RedStone_Fetch_Data
名称,由 keccak256 散列,在 constants.ts 中定义 。 - 代表
feed_id
值的int
。 - 代表信息的 "初始发送方 "的 "slice",以便他们在返回交易时携带剩余的交易余额 。
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();
// ...
}
返回信息 OP_REDSTONE_DATA_FETCHED
会发送给发送方,其中包含 "值 "和
已保存值的 "时间戳"。然后,可在发送方中获取并处理该信息,或将其保存在
发送方的存储器中。
初始 payload 的 ref
(initial_payload
)会被添加为 ref,例如包含第一条信息的发件人,
,以便他们携带剩余的交易余额。
begin_cell()
.store_uint(value, MAX_VALUE_SIZE_BITS)
.store_uint(timestamp, TIMESTAMP_BITS)
.store_ref(initial_payload)
.end_cell()
这是一条内部信息--它消耗 GAS 并修改合约的存储空间,因此必须以 TON 为单位支付。
get_price_and_timestamp
(int, int) get_price_and_timestamp() method_id;
通过发送 OP_REDSTONE_FETCH_DATA
消息并获取 OP_REDSTONE_DATA_FETCHED
消息的返回值,返回上次保存/写入适配器存储的数据值和时间戳。
这只是一个 method_id
函数 - 它不会修改合约的存储空间,也不会消耗 TON。
single_feed_man.fc
初始数据
与 prices
和 price_feed
初始数据类似。由于 TON 合约的架构设计,初始数据需要与合约的存储结构相匹配,其构造如下:
beginCell()
.storeUint(BigInt(hexlify(toUtf8Bytes(this.feedId))), consts.DATA_FEED_ID_BS * 8)
.storeUint(this.signerCountThreshold, SIGNER_COUNT_THRESHOLD_BITS)
.storeUint(0, consts.DEFAULT_NUM_VALUE_BS * 8)
.storeUint(0, consts.TIMESTAMP_BS * 8)
.storeRef(serializeTuple(createTupleItems(this.signers)))
.endCell();