Time - Docs and

Just found the Time entry in the docs for 0.6.26 at Time :: Internet Computer.

The text in question:

Current system time given as nanoseconds since 1970-01-01. The system guarantees that:
the time, as observed by the canister, is monotonically increasing, even across canister upgrades.
within an invocation of one entry point, the time is constant.
The system times of different canisters are unrelated, and calls from one canister to another may appear to travel “backwards in time”

Note: While an implementation will likely try to keep the system time close to the real time, this is not formally guaranteed.

This is annoying at the moment. Is there a function that does give us some time based assurance that won’t go back in time? Like block number or block number + message in block?

The interface that the IC provides to canisters deliberately abstracts over implementation details like “blocks” – a canister developer shouldn’t be an expert in blockchains!

But even then the blocktime doesn’t help. The problematic case is a message from a canister on one subnet to a canister on another subnet. Since each subnet has their own chain of blocks, the block number is not a global concept, and not helpful here.

In practice, the problem of messages going back in time is very unlikely (it would require two subnets to have different idea of what “now” is, with a skew bigger than the time it takes for a message to be delivered across subnets). So maybe you can just ignore it.

If you really can’t, you should include the current time at the sending canister in the message, and handle the case in the application logic on the receiving side.

5 Likes

I’ll push back a bit here. I know that not all devs will want to know the ins and outs of crypto and blockchain, but that doesn’t mean that developers that are dependent on those concepts shouldn’t have access to them. If I want to gate transactions via time there should be some deterministic way of doing so. With DFINITY’s reliance on determinism, I’m surprised to see the time function, not adhering.

Ethereum solves this by having time functions rely on the system time of the block producer. And will be finalized across the chain. There should be some systematic way to tell that another message came before this one. I understand that I can declare a var in my canister and do it myself, but to be developer-friendly it would make sense to have access to some of those base numbers like timestamp, coinbase, etc. that are used in incentivization and message ordering systems. Even if I can declare messageCount that tracks the number of messages that have ever come into my canister in my code and look at it later. it would be better for the community to have a common way of doing so. It makes learning and sharing code easier.

Having access to most of the block and transaction features would be really nice(Units and Globally Available Variables — Solidity 0.4.24 documentation). If you have them now, update the docs!

I don’t follow why you think determinism is at risk? The time the canister sees is of course deterministic, else the whole consensus protocol falls apart.

Note that Ethereum is a single blockchain, and then it’s quite easy to find a notion of “time” across all services. But on the Internet Computer, you have many subnets, each of them having their own block chain and their own time.

That said, I do agree that for the programming model it would be preferable if messages never travel back in time, at the expense of possibly delaying the communication from a subnet that is a few ms ahead to one that is a few ms behind. Who knows, maybe that guarantee can be built in in a later version. For now, FWIW, it is not there.

I haven’t done a complete tear down of how the IC handles messages yet, so please correct me if I’m making poor assumptions.

In eth all the nodes have to agree that a block is valid. There for the block has to provides some assumptions about what it used to calculated the message order and set of state changes. One of these things is the system time (now) that can be used in code and all messages in that block share a reference that value. It has to be greater than the time provided by the last block. This keeps “now” from going backwards. If message 1 updates a var to “now” and other functions gate on now<var only one message call will be allowed in that block.

My assumptions in the IC is that there is a subset of elected nodes that are going to be responsible for authorizing the equivalence of a block on the IC for that canister. If n>1 of elected nodes they will eventually have to agree on state changes and if they have different system times that determine now they will tell each other to piss off. They could still agree on message ordering, but state won’t ever match. That system time value needs to be share as part of the state change work checking or it won’t be deterministic. Maybe this time changes as async come back? Perhaps calls can cross “blocks” in this way?

I’ll labor to understand better, but I’ll still contend that a systematic way to reference the block.message of the executing canister and the beacon chain would be useful in a number of systems.