Skip to main content

访问链上时间

在需要访问基于网络的时间以进行交易时,你有多种选择。如果你需要准实时测量(在几秒内),请使用 Move 中的 Clock 模块提供的不可变时间参考。此模块的参考值会随每个网络检查点更新。如果你不需要当前时间片,可以使用 epoch_timestamp_ms 函数捕获当前时代开始的精确时刻。

sui::clock::Clock 模块

要访问提示时间戳,你必须将 sui::clock::Clock 的只读引用作为入口函数参数传递给你的交易。Clock 的实例提供在地址 0x6,不允许创建新实例。

Clock 实例中使用以毫秒为单位的 Unix 时间戳

module sui::clock {
public fun timestamp_ms(clock: &Clock): u64;
}

以下示例演示了一个入口函数,该函数触发一个包含来自 Clock 的时间戳的事件:

module example::clock {
use sui::clock::{Self, Clock};
use sui::event;

struct TimeEvent has copy, drop, store {
timestamp_ms: u64
}

entry fun access(clock: &Clock) {
event::emit(TimeEvent {
timestamp_ms: clock::timestamp_ms(clock),
});
}
}

对前述入口函数的调用采用以下形式,将 0x6 作为 Clock 参数的地址传递:

sui client call --package <EXAMPLE> --module 'clock' --function 'access' --args '0x6' --gas-budget <GAS-AMOUNT>

预计 Clock 时间戳将以每 2 到 3 秒的速度更改,这是网络提交检查点的速率。

在同一交易中对 sui::clock::timestamp_ms 的连续调用始终产生相同的结果(交易被认为立即生效),但来自 Clock 的时间戳在接触相同共享对象的跨交易中是单调递增的:连续的交易看到比它们的前任时间戳大或相等。

任何需要访问 Clock 的交易都必须经过共识,因为唯一可用的实例是一个共享对象。因此,这种技术不适用于必须使用单一所有者快速路径的交易(请参阅 Epoch 时间戳,了解时间戳的单一所有者兼容源)。

使用时钟的交易必须将其视为不可变引用(而不是可变引用或值)。这可以防止争用,因为访问 Clock 的交易只能读取它,因此不需要相对于彼此进行排序。验证器拒绝签署不符合此要求的交易,包括接受 Clock&mut Clock 的入口函数的软件包将无法发布。

以下函数通过手动创建 Clock 对象并操纵其时间戳来测试与 Clock 相关的代码。这仅在测试代码中才可能:

module sui::clock {
#[test_only]
public fun create_for_testing(ctx: &mut TxContext);

#[test_only]
public fun share_for_testing(clock: Clock);

#[test_only]
public fun increment_for_testing(clock: &mut Clock, tick: u64);

#[test_only]
public fun set_for_testing(clock: &mut Clock, timestamp_ms: u64);

#[test_only]
public fun destroy_for_testing(clock: Clock);
}

下一个示例呈现了一个简单的测试,创建一个 Clock,增加它,然后检查它的值:

module example::clock_tests {
use sui::clock::{Self, Clock};
use sui::tx_context;

#[test]
fun creating_a_clock_and_incrementing_it() {
let ctx = tx_context::dummy();
let clock = clock::create_for_testing(&mut ctx);

clock::increment_for_testing(&mut clock, 42);
assert!(clock::timestamp_ms(&clock) == 42, 1);

clock::set_for_testing(&mut clock, 50);
assert!(clock::timestamp_ms(&clock) == 50, 1);

clock::destroy_for_testing(clock);
}
}

时代时间戳

你可以使用以下函数访问所有交易(包括不经过共识的交易)的当前时代开始的时间戳:

module sui::tx_context {
public fun epoch_timestamp_ms(ctx: &TxContext): u64;
}

前述函数返回当前时代开始的时间点,以毫秒粒度的 Unix 时间戳形式为 u64。此值大约每 24 小时更改一次,即时代更改时。

基于 sui::test_scenario 的测试可以使用 later_epoch(以下代码)来测试使用 epoch_timestamp_ms(前述代码)的依赖于时间的代码:

module sui::test_scenario {
public fun later_epoch(
scenario: &mut Scenario,
delta_ms: u64,
sender: address,
): TransactionEffects;
}

later_epoch 的行为类似于 sui::test_scenario::next_epoch(完成测试方案中的当前事务和时代),但还将时间戳增加 delta_ms 毫秒,以模拟时间的流逝。