Skip to content

Run the CN Token Standard CLI against an instrument and record conformance #43

Description

@lmcorbalan

User story / Problem statement

As a maintainer, I want the upstream CN Token Standard CLI to read our holdings and
transactions unmodified, so that the claim of standard compliance rests on a
registry-agnostic third-party client rather than on tests we wrote ourselves.

Every conformance claim today is self-referential:

  • The 38 Daml scripts and 89 registry tests were written by us, against our own
    reading of the standard. A shared misreading of an interface is invisible to
    both.
  • The registry's responses are validated against the vendored OpenAPI specs, which
    proves the shape of a body but not that a client built for another registry can
    drive the flow end to end.
  • One encoding has never been exercised at all: AnyValue inside a submitted
    choice context. The service hands a context out and that much is live-verified,
    but nothing has ever sent one back to a participant. A third-party client doing
    a real transfer is the cheapest way to close that gap.

Expected outcome

Pointed at a seeded sandbox, the CLI decodes our holdings (plain and locked) and
renders our transactions, without special-casing for canton-token-forge. A tracked
CONFORMANCE.md records which standard interfaces are implemented, which commands
were run against what, their real output, and every deviation from the standard,
so a reader can tell what is proven from what is claimed.

Acceptance criteria

Getting the CLI running

  • The CLI runs against our sandbox, either from the vendored tree or from a
    published package, and the document records which and at what version
  • If the vendored tree needed changes to install, they are made in
    scripts/fetch-dep.sh (not by hand-editing deps/, which is gitignored)
    or the workaround is written down
  • The exact invocation for every command is recorded, including the ledger URL
    and how the auth token was supplied

Read path, all must pass

  • list-holdings <party> for a seeded party decodes owner, instrument id,
    amount, and lock state
  • Both a plain Token and a LockedToken are decoded, and the lock is
    visible on the locked one
  • list-holding-txs <party> renders the mint, a faucet tap, and a completed
    transfer
  • list-holding-txs --strict is run and its result recorded; a strict failure
    is a conformance defect on our side and is filed as a bug rather than worked
    around in the document
  • list-transfer-instructions <party> lists a pending offer for its receiver

Write path

  • transfer and accept-transfer-instruction are either exercised against the
    service's transfer factory, or the document states plainly that they require
    external-party signing keys we do not set up, and what was done instead
  • If they are exercised, the issue records that this is the first submitted
    AnyValue choice context, closing the last unverified wire encoding

The document

  • CONFORMANCE.md is tracked (see Technical notes: docs/ is gitignored)
  • It lists each of the seven splice-api-token-* interfaces as implemented or
    not, naming the implementing template for each
  • It states that AllocationInstruction is deliberately not implemented:
    allocateImpl returns a completed allocation with no pending instruction
    step, while the registry still advertises
    splice-api-token-allocation-instruction-v1 in supportedApis because it
    serves the allocation-factory HTTP endpoint
  • It records each deviation explicitly: no economics (no decay, fees, mining
    rounds, rewards, or DSO governance); one admin party is both the instrument
    admin and the registry identity, and instruments are created directly by
    admin tooling; the registry service is read-only; direct transfers require a
    receiver-signed TokenTransferPreapproval inside its validity window and
    otherwise become a two-step offer; holdings are co-signed by their owner and
    locks by their holders; (admin, instrumentId) uniqueness is not enforced
    on-ledger and surfaces as a 409 on get-by-id and the factory routes while
    GET /instruments dedupes and answers 200; decimals is display guidance
    bounded 0..10, not an enforced amount scale
  • It states that the registry's request and response bodies validate against
    the vendored OpenAPI specs, and how that is checked
  • Every command in the document is shown with its real output, not a summary

Alternatives considered

  • Write our own standard-compliance suite instead. Rejected: it would re-test
    our own reading of the specs, which is exactly what this issue exists to
    cross-check.
  • Document conformance from the specs without running anything. Rejected: a
    conformance document that cites no output is a claim, not evidence.
  • Wait for allocation support in the CLI. Rejected: the CLI's transaction
    interface list is holding + transfer-factory + transfer-instruction only
    (upstream TODO #563), and the transfer path alone is worth proving now. Record
    the allocation gap as a limitation of the tool.

Technical notes

  • The vendored CLI does not install as-is.
    deps/token-standard/cli/package.json carries two file: dependencies that the
    sparse clone does not bring down: @canton-network/canton-json-api-v2-openapi ->
    ../dependencies/canton-json-api-v2/openapi-ts-client (the whole dependencies/
    tree is absent) and @canton-network/transfer-instruction-openapi ->
    ../splice-api-token-transfer-instruction-v1/openapi-ts-client (that directory
    holds only daml/, daml.yaml, docs/, openapi/). Either extend
    scripts/fetch-dep.sh's sparse-clone paths or generate the clients from the
    vendored OpenAPI specs. Expect this to be the first half hour of the work.
  • Commands available (deps/token-standard/cli/src/token-standard-cli.ts):
    list-holdings, list-holding-txs, transfer, list-transfer-instructions,
    accept-transfer-instruction. Every one requires --ledger-url and
    --auth-token; our sandbox is unauthenticated, so pass a placeholder token. The
    JSON API port comes from .canton-ports.json (RUNBOOK section 1).
  • transfer and accept-transfer-instruction require --public-key and
    --private-key
    , so they drive external-party signing. scripts/seed.mjs
    allocates ordinary local parties, so those two are not runnable against a plain
    seed without standing up external parties first. The three read commands are.
  • The CLI resolves contracts by package-name interface id
    (#splice-api-token-holding-v1:Splice.Api.Token.HoldingV1:Holding) and matches
    on the Module:Entity suffix, so it finds our holdings through the interface
    rather than the template. That is the same package-name rule the RUNBOOK records.
  • docs/ is gitignored (.gitignore:11, confirmed with git check-ignore), so
    the originally specified docs/CONFORMANCE.md would be untracked and invisible
    to everyone else. Put CONFORMANCE.md at the repo root beside RUNBOOK.md and
    architecture.md.
  • The deviation list must describe the current model. The operator role,
    TokenRegistry, InstrumentConfigProposal, the propose-accept gate, and the
    /admin/* endpoints were all removed by Collapse the operator role into the instrument admin #57 and must not appear.

Additional context

Part of #23. The sandbox runbook and seed it depends on landed with #41. The write
path overlaps #42, which drives a transfer through the service directly; if #42
lands first, this issue inherits a working transfer to inspect.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestpriority: highMust be addressed in current sprint

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions