Chromia
Start Building

01 / 01Guide

Handling timestamps on Chromia with Rell

A blockchain runs on many nodes, so there is no single, well-defined moment at which a piece of code executes. A call such as System.currentTimeMillis() would produce a different value on each node. Synchronized clocks don't fix this: the primary node runs the code first and the validating nodes follow, and network latency and hardware differences mean they never read the time at exactly the same instant.

A reliable time value must therefore come from something every node agrees on, namely the latest known time or previous block time. Every block header carries a timestamp that the validating nodes accept, which makes it a good source. You can read it by querying the system block entity.

rell
block @ {} (@max .timestamp);

While the blockchain is initializing, no blocks exist yet, so this query returns null. If you set a timestamp on an object, for example, you first have to initialize it to zero:

rell
object my_object {    mutable timestamp = 0;}

You then update the value yourself through an operation.

rell
operation set_time() {    my_object.timestamp = block @ {} (@max .timestamp);}

Inside an operation, you have access to a special context, op_context, which exposes a last_block_time property. You can use it to shorten the expression above. You might want to make it the default value for timestamp attributes on entities. However, when you add such an attribute to an entity that already holds data, you need the block query to assign a correct timestamp to the existing records:

rell
entity my_entity {    timestamp = if (op_context.exists) op_context.last_block_time else block @ {} (@max .timestamp)!!;}

The reason is that the default value is applied to all existing entities when the blockchain restarts after the update. At that point no operation is running, so op_context is not available (op_context.exists is false).

To handle every case the same way, define a function last_known_time():

rell
function last_known_time()    = if (op_context.exists) op_context.last_block_time else block @ {} (@max .timestamp) ?: 0;

You can call this function safely in any context:

rell
object my_object {    timestamp = last_known_time();}entity my_entity {    timestamp = last_known_time();}operation my_operation() {    create my_entity();}query get_last_known_time() = last_known_time();
Written for this site from Chromia’s public course. Code samples are the course’s own.