Sometimes a CKB script (also known as smart contract) requires current time as input. Because the deterministic nature of CKB transactions, a script has no access to timestamp in block header as on Ethereum. Several alternatives have been devised to provide the equivalent function, such as since
field, header dep + ckb_load_header_by_field
syscall, or more complex schemes.
The idea here is another one and it reuses existing Internet infrastructure, Roughtime. Roughtime is a protocol that aims to achieve rough time synchronisation in a secure way that doesn’t depend on any particular time server. It is proposed by Google and Cloudflare, and already provided by many including Chainpoint and int08h. Roughtime is a interactive authenticated protocol in which a client can submit a request with nonce to the server and the server must sign the nonce together with current timestamp as the response.
Now imagine a design pattern like this, for any operation which can only be performed after timestamp T
on CKB:
- create an operation cell Cell_{op}, with timestamp
T
embedded indata
orlock
args; - the lock of Cell_{op} requires a witness W = W_{op} + W_{t} such that: a) contains the operator’s signature W_{op}, b) an timestamp witness W_{t} from any one or multiple Roughtime servers, and c) $W_{t} must include a timestamp larger or equal than
T
; - W_t is a Roughtime response to a request which uses the outpoint of Cell_{op} as nonce.
This way the Cell_{op} can be unlocked only after time T
.
Roughtime uses EdDSA which can be easily supported on CKB (thanks to the cryptography abstraction). The accuracy of Roughtime is good enough for layer 1 applications. The scheme is permissionless and decentralized in that a script can choose any one or multiple Roughtime servers as oracle. The timestamp witness are auditable. The reputation stake of some Roughtime servers are higher than many decentralized oracles we see today.
ps.
It’s more trickier to implement operations that can only performe before T
. A simple idea which does NOT work is like this:
- unlock Cell_{op} and create an intermediate state Cell'_{op};
- use Cell'_{op} as a fresh nonce to get a W'_t from Roughtime server;
- If the timestamp in W'_t is smaller than
T
, Cell'_{op} can be unlocked to perform the actual action.
The problem is the operator could retain a valid W'_t at wish and use it to unlock Cell'_{op} at any time after T
.In order to achieve “only perform before T”, we should rely on on-chain time instead of a time oracle. Here’s a pattern that works:
- unlock Cell_{op} and create an intermediate state Cell'_{op} in
tx1
; - create a following transaction
tx2
with Cell'_{op} as an input and its including block header as header dep, and operation performed state Cell''_{op} as output. -
tx2
is valid only if the timestamp in header dep is smaller thanT
.