fix(token): vet test-token DARs on every LocalNet participant - #319
Merged
Conversation
token create only uploaded the bundle to the create participant, so mint to a party on app-user failed. Fetch once, cache by Splice commit, and upload plus vet on sv, app-provider, and app-user.
Comments added for cross-participant vetting restated mechanics and ticket context; rewrite them as single causal sentences per house style.
Token subcommands migrated from positional args + --name to named flags + --instance. All test cases updated: - token create: --token-name -> --name, add --non-interactive, --issuer - token mint: positional symbol/amount -> --instrument/--amount, add --to, --instance - token transfer: positional -> --instrument/--amount/--from/--to/--instance, add --auto-accept - token burn: positional -> --instrument/--amount/--from/--instance, add --yes - token balance: positional symbol -> --instrument, --to -> --party, --name -> --instance - env setup: remove stale WALLET_A/B capture (replaced by per-test party aliases) - M3-TOK-010 regression script updated to match new flag shapes
When the receiver party lives on a different participant than the sender (e.g. app-user vs app-provider), Canton rejects the accept submission with NO_SYNCHRONIZER_ON_WHICH_ALL_SUBMITTERS_CAN_SUBMIT because the sender's node has no CanActAs right for the receiver's party. Add resolveAcceptConn to pick the correct LedgerConn for the accept step, routing to the receiver's participant when the roles differ. Wire it into runMintLive, runTransferLiveOnLedger auto-accept, and runAcceptOnLedgerIfTestToken standalone accept. Add receiverRole helper (alias.go) and test seams (dialSenderFn, dialLedgerConcreteFn, findTokenRulesDisclosedFn, mintViaOfferMintFn) so unit tests can assert on accept-side routing without a live gRPC server.
zheli
marked this pull request as ready for review
August 19, 2026 09:12
…st step PR #319 changes user-facing behaviour (token create now vets the bundled test-token DARs on all three participants, prints the vetting line, caches DARs, and fails when a role's participant port is missing), so the proposal deviation log must record it per the tracking rule in AGENTS.md. The M3 cross-platform meta-test tore down the LocalNet instance as part of its own cleanup while sorting before M3-TOK-011, which both hid the ordering constraint and tied cleanup to a test that may never run on failure. Renumber it M3-TOK-999 so it always sorts last, and lift teardown into a standalone section that an automated run must execute unconditionally. Anchor the artifacts ignore rule to the repository root so nested directories named artifacts are not ignored by accident.
zheli
added a commit
that referenced
this pull request
Aug 19, 2026
* fix(token): vet test-token DARs on every LocalNet participant token create only uploaded the bundle to the create participant, so mint to a party on app-user failed. Fetch once, cache by Splice commit, and upload plus vet on sv, app-provider, and app-user. * Update .gitignore * docs(token): tighten DAR fan-out comments to state why Comments added for cross-participant vetting restated mechanics and ticket context; rewrite them as single causal sentences per house style. * docs(tests): update all M3-TOK command syntax to current flag shapes Token subcommands migrated from positional args + --name to named flags + --instance. All test cases updated: - token create: --token-name -> --name, add --non-interactive, --issuer - token mint: positional symbol/amount -> --instrument/--amount, add --to, --instance - token transfer: positional -> --instrument/--amount/--from/--to/--instance, add --auto-accept - token burn: positional -> --instrument/--amount/--from/--instance, add --yes - token balance: positional symbol -> --instrument, --to -> --party, --name -> --instance - env setup: remove stale WALLET_A/B capture (replaced by per-test party aliases) - M3-TOK-010 regression script updated to match new flag shapes * fix(token): dial receiver's participant for mint/transfer accept When the receiver party lives on a different participant than the sender (e.g. app-user vs app-provider), Canton rejects the accept submission with NO_SYNCHRONIZER_ON_WHICH_ALL_SUBMITTERS_CAN_SUBMIT because the sender's node has no CanActAs right for the receiver's party. Add resolveAcceptConn to pick the correct LedgerConn for the accept step, routing to the receiver's participant when the roles differ. Wire it into runMintLive, runTransferLiveOnLedger auto-accept, and runAcceptOnLedgerIfTestToken standalone accept. Add receiverRole helper (alias.go) and test seams (dialSenderFn, dialLedgerConcreteFn, findTokenRulesDisclosedFn, mintViaOfferMintFn) so unit tests can assert on accept-side routing without a live gRPC server. * docs: track token DAR fan-out deviation and make M3 teardown a non-test step PR #319 changes user-facing behaviour (token create now vets the bundled test-token DARs on all three participants, prints the vetting line, caches DARs, and fails when a role's participant port is missing), so the proposal deviation log must record it per the tracking rule in AGENTS.md. The M3 cross-platform meta-test tore down the LocalNet instance as part of its own cleanup while sorting before M3-TOK-011, which both hid the ordering constraint and tied cleanup to a test that may never run on failure. Renumber it M3-TOK-999 so it always sorts last, and lift teardown into a standalone section that an automated run must execute unconditionally. Anchor the artifacts ignore rule to the repository root so nested directories named artifacts are not ignored by accident.
zheli
added a commit
that referenced
this pull request
Aug 19, 2026
…g auth, JSON parity) (#321) * refactor(splice): add AllRoleNames as the single fan-out role source `dar upload --all-participants` re-spelled the literal {"sv","app-provider","app-user"} while the token DAR path derived the same list from splice.AllRoles(). A comment asserted the two must match, but nothing enforced it, so the two fan-out paths could silently drift in which participants they target or in what order. Both now derive from splice.AllRoleNames(), making the claim structural. * refactor(token): probe DAR vetting via the admin API through shared darops The token DAR fan-out carried its own dialling and vetting logic parallel to internal/localnet/darops, and its probe was wrong in ways that could report success while the participant could not host the package: - It called the ledger API's ListKnownPackages, which reports upload, not vetting, so "Vetted ..." overstated what had been checked. It now uses the admin API's ListDars, the same probe darops already relies on. - It matched packages by name alone, so the pinned splice-util-token-standard-wallet 1.1.0 was satisfied by an installed 1.0.0 that lacks BatchingUtilityV2. Matching is now name and version. - It issued one list call per package per role (21 per create). It now issues one per role before uploads and one after. - A failing list RPC was indistinguishable from "package absent", causing redundant uploads and a misleading "still not vetted after upload". List errors now propagate with the failing role and host. - An interrupted cache write could leave a truncated DAR that later runs would trust. Cache entries are now written to a temp file in the same directory and renamed into place. Extracting EnsureVetted into darops lets the token path and `dar upload` share one implementation instead of drifting. * fix(token): keep the auth token on the accept-leg connection Both auto-accept paths rebuilt a LedgerConn from the options to target the receiver's participant, but the rebuilt value omitted Token. The accept leg therefore dialled unauthenticated whenever the caller had supplied a JWT, failing against an auth-on LocalNet. Both blocks now pass the in-scope conn, which already carries Token. * feat(token): expose vetted_roles on both token create JSON surfaces Which participants a create vetted was only ever printed as prose, so neither `token create --format json` nor POST /api/tokens could report it. Scripts and the Web UI had no way to tell a fully vetted create from a registry-only one. Both surfaces now encode a shared types.TokenCreateResponse carrying schema_version and vetted_roles, so they cannot drift. TokenRef stays embedded rather than nested, keeping the instrument's fields where they have always been on the wire. vetted_roles is omitted for a registry-only create, which vets nothing. * docs: record the corrected vetting claim and the vetted_roles field AGENTS.md requires the proposal-deviation entry to track user-facing behaviour. The existing `token create` entry described a ledger-port failure mode that is now an admin-port one, and predates both the vetted_roles JSON field and the atomic DAR cache write. * fix(token): claim "known" where only ListKnownPackages was checked resolvePackageID and discoverTokenSurfaces both call the ledger API's ListKnownPackages, which proves a package was uploaded to the participant. It does not prove the package is vetted -- vetting is a topology fact, and darops.EnsureVetted reads it through the admin API. Both functions and the four user-facing errors that inherit from them nonetheless told the operator a package was or was not "vetted". That sends someone debugging a routing failure to look at vetting state that was never actually checked, which is the opposite of what the message should do. Reword the claims to match what the call proves, and record on discoverTokenSurfaces which API to reach for when the vetting state itself is what matters. No behaviour change: the same packages are found by the same call.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
token createonly vetted the bundled test-token DARs on the create participant, so mint/transfer to a party onapp-userfailed. Create now fetches each DAR once, caches it by Splice commit, and uploads plus vets onsv,app-provider, andapp-user. Fixes #318.Changes
ensureTokenDARsout to all three LocalNet participants; missing port or upload/vet failure on any role fails create~/.canton-devkit/localnet/.dar-cache/<commit>/(shared across instances)token createhelp,docs/tokens.md, anddocs/troubleshooting.mdTest plan
make test(./internal/localnet/token/,./internal/cli/localnet/token/,./internal/cli/)make lintscripts/e2e/run-all.shtoken create, thentoken mintto anapp-userpartyChecklist
website/synced if any mirroreddocs/*.mdchanged)TODO(#issue)comment)