You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Replace ambiguous raw-u64 time values at the authoring boundary with explicit CKB temporal domain types.
CellScript currently returns u64 for the HeaderDep epoch number, epoch start block number, epoch length, input since, and absolute/relative epoch-since constructors. The runtime checks are bounded and fail-closed, but the type system cannot prevent mixing values with different units or comparing an encoded since bitfield to a plain epoch number.
Argent's temporal-value work demonstrates the value of preserving temporal identity in artifacts even when the runtime representation remains integer-based. CellScript should define a CKB-specific, wire-compatible version rather than a generic temporal wrapper.
Problem examples
All of the following values are currently represented as u64:
HeaderDep epoch number
HeaderDep epoch start block number
HeaderDep epoch length
encoded input since
absolute epoch since
relative epoch since
application epoch deadline
application epoch duration
This makes invalid combinations type-correct:
let epoch = env::current_timepoint()
let encoded_since = ckb::input_since(source::input(0))
// Numerically valid u64 operations, semantically invalid domains:
require encoded_since >= epoch
let deadline = encoded_since + epoch
The current implementation also names an epoch-number read current_timepoint, which is easy to misread as a Unix timestamp.
Proposed type families
The exact surface requires an RFC, but the semantic domains should include:
Type
Meaning
Candidate runtime representation
EpochNumber
whole CKB epoch number
checked u64
BlockNumber
absolute block number
checked u64
Timestamp
CKB timestamp metric
checked u64
EpochLength
blocks in one epoch
nonzero checked u64
EpochFraction
CKB epoch number/index/length tuple
canonical fixed fields or encoded u64
Since<Absolute, BlockNumber>
absolute block-number constraint
canonical encoded u64
Since<Absolute, EpochFraction>
absolute epoch-fraction constraint
canonical encoded u64
Since<Absolute, Timestamp>
absolute timestamp constraint
canonical encoded u64
Since<Relative, BlockNumber>
relative block-number constraint
canonical encoded u64
Since<Relative, EpochFraction>
relative epoch-fraction constraint
canonical encoded u64
Since<Relative, Timestamp>
relative timestamp constraint
canonical encoded u64
DecodedSince
tagged union returned by decoding raw input since
one of the six validated variants
EpochDuration
application duration in whole epochs
checked u64
The exact generic spelling is an RFC decision. The invariant is not: both the relative/absolute mode and the block/epoch/timestamp metric must remain in the type identity. A wrapper that distinguishes only AbsoluteSince from RelativeSince is insufficient.
The unsafe-looking direction must be explicit in source and typed-semantics metadata. No implicit widening or assignment conversion should cross these domains.
API migration sketch
A candidate additive first step:
let epoch: EpochNumber = env::current_epoch_number()
let start: BlockNumber = ckb::header_epoch_start_block_number(header)
let length: EpochLength = ckb::header_epoch_length(header)
let maturity: Since<Relative, EpochFraction> =
ckb::since_epoch_relative(number, index, length)
let observed: DecodedSince = ckb::input_since(input)
require observed.satisfies(maturity)
The existing env::current_timepoint() -> u64 should remain only under the existing edition contract, emit a targeted migration warning, and migrate mechanically to the precisely named API. Do not silently change its return type within an edition.
Artifact and interface requirements
Temporal identity must survive every compiler layer:
flowchart LR
SRC["Typed source"] --> AST["Temporal AST type"]
AST --> IR["Distinct IR type / checked ops"]
IR --> TS["typed-semantics record"]
IR --> ABI["Fixed wire representation"]
TS --> CHECK["Standalone checker"]
ABI --> VM["CKB-VM execution"]
Loading
The public package interface must record:
semantic temporal type;
exact serialized representation;
constructor/decoder version;
Since metric and relative/absolute policy;
target/profile identity;
migration behavior.
A type alias to u64 is insufficient because it would erase the safety property before IR.
Implementation phases
Phase 0 — inventory and RFC
inventory every stdlib/source/IR/metadata path that currently uses a temporal u64;
define the type domains and legal operations;
define ABI and interface compatibility;
define edition migration and diagnostics;
align with official CKB Since and epoch-fraction semantics.
Phase 1 — additive typed APIs
add typed constructors, readers, decoders, comparisons, and checked arithmetic;
preserve old APIs with warnings;
add typed-semantics and interface records;
add formatter, LSP, docs, and Playground support.
Phase 2 — migration
provide a mechanical migration command or code action;
migrate bundled examples and package fixtures;
keep old-edition dependencies interoperable;
reject implicit cross-domain arithmetic in the new edition.
Phase 3 — stabilization
mutation-test Since flag/metric validation;
add exact CKB-VM vectors for valid and malformed encodings;
document compatibility and wire identity;
stabilize only after real timelock, DAO, vesting, and atomic-swap fixtures migrate.
Acceptance matrix
Case
Expected
compare two EpochNumber values
pass
add EpochDuration to EpochNumber
pass with overflow guard
compare EpochNumber and BlockNumber
type error
compare encoded Since and epoch
type error
construct fraction with index >= length
stable error
construct fraction with zero/overflowing length
stable error
decode unknown Since flags
stable runtime error
mix absolute and relative Since
type error unless explicitly normalized by an accepted operation
round-trip valid CKB Since vectors
exact match
compile old-edition current_timepoint source
preserved behavior plus migration policy
cross-package interface changes raw u64 to typed domain
correctly classified as compatible or breaking according to the accepted edition/ABI policy
Completion criteria
An accepted RFC defines every temporal domain and operation.
Official CKB Since and epoch semantics are cited and pinned with vectors.
Temporal identity survives AST, type checking, IR, typed semantics, interfaces, ABI, lowering, and checker.
The wire representation remains deterministic and versioned.
New-edition code cannot implicitly mix temporal domains with u64.
Existing-edition behavior is preserved with a mechanical migration path.
Timelock, DAO, vesting, NFT expiry, governance, and atomic-swap examples use the typed API.
Malformed flags, fractions, and arithmetic boundaries have CKB-VM negative tests.
LSP, formatter, VS Code, Playground, docs, and generated builders agree.
dev, ci, and backend gates pass.
Non-goals
inventing wall-clock semantics that CKB does not expose;
treating a HeaderDep epoch number as a Unix timestamp;
implicit conversion from arbitrary integers;
changing existing edition semantics in place;
generic dimensional-analysis or units-of-measure support;
hiding CKB Since flags behind unchecked arithmetic.
Summary
Replace ambiguous raw-
u64time values at the authoring boundary with explicit CKB temporal domain types.CellScript currently returns
u64for the HeaderDep epoch number, epoch start block number, epoch length, inputsince, and absolute/relative epoch-sinceconstructors. The runtime checks are bounded and fail-closed, but the type system cannot prevent mixing values with different units or comparing an encodedsincebitfield to a plain epoch number.Argent's temporal-value work demonstrates the value of preserving temporal identity in artifacts even when the runtime representation remains integer-based. CellScript should define a CKB-specific, wire-compatible version rather than a generic
temporalwrapper.Problem examples
All of the following values are currently represented as
u64:This makes invalid combinations type-correct:
The current implementation also names an epoch-number read
current_timepoint, which is easy to misread as a Unix timestamp.Proposed type families
The exact surface requires an RFC, but the semantic domains should include:
EpochNumberu64BlockNumberu64Timestampu64EpochLengthu64EpochFractionu64Since<Absolute, BlockNumber>u64Since<Absolute, EpochFraction>u64Since<Absolute, Timestamp>u64Since<Relative, BlockNumber>u64Since<Relative, EpochFraction>u64Since<Relative, Timestamp>u64DecodedSincesinceEpochDurationu64The exact generic spelling is an RFC decision. The invariant is not: both the relative/absolute mode and the block/epoch/timestamp metric must remain in the type identity. A wrapper that distinguishes only
AbsoluteSincefromRelativeSinceis insufficient.flowchart TB H["HeaderDep fields"] --> EN["EpochNumber"] H --> BN["BlockNumber"] H --> EL["EpochLength"] EN --> EWF["EpochNumberWithFraction"] EL --> EWF EWF --> SAE["Since<Absolute, EpochFraction>"] EWF --> SRE["Since<Relative, EpochFraction>"] BN --> SAB["Since<Absolute, BlockNumber>"] BN --> SRB["Since<Relative, BlockNumber>"] TS["Timestamp"] --> SAT["Since<Absolute, Timestamp>"] TS --> SRT["Since<Relative, Timestamp>"] RAW["Raw u64"] -. "explicit checked conversion only" .-> EN RAW -. "decode flags + metric + payload" .-> DS["DecodedSince"]Required type rules
Allowed by default
EpochNumberWithFractionfrom validated number/index/length;Since;sinceinto a tagged validated representation;Rejected without explicit conversion
EpochNumberversusBlockNumber;Since;Since;Since;u64;Explicit escape hatch
Low-level protocol work may need raw bits. Provide named operations such as:
The unsafe-looking direction must be explicit in source and typed-semantics metadata. No implicit widening or assignment conversion should cross these domains.
API migration sketch
A candidate additive first step:
The existing
env::current_timepoint() -> u64should remain only under the existing edition contract, emit a targeted migration warning, and migrate mechanically to the precisely named API. Do not silently change its return type within an edition.Artifact and interface requirements
Temporal identity must survive every compiler layer:
flowchart LR SRC["Typed source"] --> AST["Temporal AST type"] AST --> IR["Distinct IR type / checked ops"] IR --> TS["typed-semantics record"] IR --> ABI["Fixed wire representation"] TS --> CHECK["Standalone checker"] ABI --> VM["CKB-VM execution"]The public package interface must record:
Sincemetric and relative/absolute policy;A type alias to
u64is insufficient because it would erase the safety property before IR.Implementation phases
Phase 0 — inventory and RFC
u64;Sinceand epoch-fraction semantics.Phase 1 — additive typed APIs
Phase 2 — migration
Phase 3 — stabilization
Sinceflag/metric validation;Acceptance matrix
EpochNumbervaluesEpochDurationtoEpochNumberEpochNumberandBlockNumberSinceand epochindex >= lengthSinceflagsSinceSincevectorscurrent_timepointsourceu64to typed domainCompletion criteria
Sinceand epoch semantics are cited and pinned with vectors.u64.dev,ci, andbackendgates pass.Non-goals
Sinceflags behind unchecked arithmetic.References
sincespecification