Skip to content

feat: port the value-time-lock verifier policy (#33)#39

Merged
LiranCohen merged 1 commit into
masterfrom
feat/value-lock-verifier
Jun 4, 2026
Merged

feat: port the value-time-lock verifier policy (#33)#39
LiranCohen merged 1 commit into
masterfrom
feat/value-lock-verifier

Conversation

@LiranCohen

Copy link
Copy Markdown
Contributor

Summary

Ports the Sidetree reference ValueTimeLockVerifier (decentralized-identity/sidetree v1.0.6) into sidetree-go as exported, pure, well-tested functions — the policy that decides how many operations an anchor may carry once value-locking is in use. This is the widened value-lock policy surface: it carries amountLocked, normalizedFee, owner, and the lock window. Plan: docs/plans/2026-06-04-001-feat-ion-value-locking-protocol-rules-plan.md.

API (valuelock.go)

  • ValueTimeLock{AmountLocked, Owner, LockTransactionTime, UnlockTransactionTime} — a resolved on-chain lock (mirrors ValueTimeLockModel); active for block times in [LockTransactionTime, UnlockTransactionTime).
  • CalculateMaxNumberOfOperationsAllowed(lock, normalizedFee) = floor(amountLocked / (normalizedFee × 0.001 × 60000)), floored to 100; the free quota (100) with no lock. The 10000 ceiling is not applied here — the reader's op-count gate (P0: Reader accepts >100-op anchors that canonical ION rejects (per-anchor op-limit unenforced) — consensus divergence #28) enforces it separately, matching the reference.
  • VerifyLockAmount(lock, opCount, normalizedFee, txWriter, anchorTime)≤100 ops always pass; over-quota requires a lock whose owner == txWriter, whose window contains anchorTime, and whose allowance ≥ opCount; over-quota with no lock is rejected. Mirrors the reference ordering exactly (owner/window checks skipped when lock == nil, then the allowance check rejects the over-quota anchor).

Architecture

sidetree-go owns this policy; the Bitcoin layer (ion-node) owns the data: resolving writerLockId → ValueTimeLock (#55), tracking the per-block normalized fee (#54), and identifying the transaction writer. ion-node plugs the policy in by implementing the existing ValueLocking callback as a thin adapter that resolves those inputs and calls VerifyLockAmount.

The bool ValueLocking callback is kept as the integration seam, so nothing in the reader's runtime path changes today — this PR is purely additive library API. It is latent until a writer locks BTC to exceed 100 ops/anchor; ION mainnet ships value-locking disabled. Until the resolver exists, the reader default-rejects over-quota locked anchors (#28).

New sentinels: ErrValueLockInvalidOwner, ErrValueLockTimeOutOfRange, ErrValueLockInsufficientForOps.

Testing

  • TestCalculateMaxNumberOfOperationsAllowed — no-lock free quota, sub-quota floored to 100, exactly 100, 200, floor-toward-zero, higher-fee-lowers-allowance, non-positive-fee guard.
  • TestVerifyLockAmount — free quota, no-lock over quota, sufficient lock, over-allowance, wrong owner, and window boundaries (start inclusive, unlock exclusive).
  • gofmt clean; go build, go vet, go test -race -count=1 ./... green.

Post-Deploy Monitoring & Validation

No additional operational monitoring required: purely additive library API with no change to the reader's runtime path (the verifier is not yet invoked by the processor — ion-node's ValueLocking adapter will call it in #55). Inert on ION mainnet (value-locking disabled).

🤖 Generated with Claude Code

Port the Sidetree reference ValueTimeLockVerifier (decentralized-identity/
sidetree v1.0.6) as exported, pure, well-tested functions, giving the reader the
policy that decides how many operations an anchor may carry once value-locking is
in use:

  - ValueTimeLock{AmountLocked, Owner, LockTransactionTime, UnlockTransactionTime}
    — a resolved on-chain lock (mirrors ValueTimeLockModel); active for block
    times in [LockTransactionTime, UnlockTransactionTime).
  - CalculateMaxNumberOfOperationsAllowed(lock, normalizedFee) =
    floor(amountLocked / (normalizedFee * 0.001 * 60000)), floored to 100; the
    free quota (100) when there is no lock. The 10000 ceiling is intentionally not
    applied here — the reader's op-count gate (#28) enforces it separately.
  - VerifyLockAmount(lock, opCount, normalizedFee, txWriter, anchorTime): <=100
    ops always pass; over-quota requires a lock whose owner == txWriter, whose
    window contains anchorTime, and whose allowance >= opCount; over-quota with no
    lock is rejected. Matches the reference ordering exactly (owner/window checks
    skipped when lock is nil, then the allowance check rejects).

This is the widened value-lock policy surface — it carries amountLocked,
normalizedFee, owner, and the lock window. sidetree-go owns this policy; the
Bitcoin layer (ion-node) owns resolving the inputs — writerLockId -> ValueTimeLock
(#55), the per-block normalized fee (#54), and the transaction writer — and plugs
the policy in by implementing the existing ValueLocking callback as a thin adapter
that calls VerifyLockAmount. The bool callback is kept as the integration seam so
nothing in the reader path changes today (and ION mainnet ships value-locking
disabled, so this is latent until a writer locks BTC to exceed 100 ops/anchor).

Sentinels: ErrValueLockInvalidOwner, ErrValueLockTimeOutOfRange,
ErrValueLockInsufficientForOps.

Tested: TestCalculateMaxNumberOfOperationsAllowed (no-lock, sub-quota floor,
exactly-100, 200, floor-toward-zero, higher-fee-lowers-allowance, non-positive-fee
guard) and TestVerifyLockAmount (free quota, no-lock over quota, sufficient lock,
over-allowance, wrong owner, window boundaries inclusive/exclusive).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@LiranCohen LiranCohen merged commit 5ba1036 into master Jun 4, 2026
1 check passed
@LiranCohen LiranCohen deleted the feat/value-lock-verifier branch June 4, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant