Token Registry Integration tests - #668
Conversation
There was a problem hiding this comment.
Pull request overview
This PR introduces a new registry-kit Go module providing Canton Registry utility onboarding/mint/supply verification helpers plus CCIP “token registry” utilities, and wires these utilities into existing CCIP integration tests.
Changes:
- Added
registry-kitCLI + config/state management for devnet Registry onboarding, minting, and CCIP TokenAdminRegistry linking. - Added registry/CCIP utility helpers (ACS discovery, choice-context building, disclosures, pool deploy/send/release helpers, package/DAR checks).
- Updated integration tests to consume
registry-kitand added generated bindings / contract package metadata for Utility DARs.
Reviewed changes
Copilot reviewed 40 out of 58 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| registry-kit/registry/verify.go | Adds holding verification utilities (template + interface view + aggregated balance). |
| registry-kit/registry/supply.go | Adds supply aggregation query over Registry Holding contracts. |
| registry-kit/registry/packages.go | Adds Utility DAR loading and package presence checks. |
| registry-kit/registry/onboarding.go | Adds devnet onboarding flows for Provider/Registrar services + instrument configuration creation. |
| registry-kit/registry/mint.go | Adds local mint flow via AllocationFactory with manually assembled choice-context. |
| registry-kit/registry/mint_devnet.go | Adds devnet mint flow using DA operator backend choice-context + disclosures. |
| registry-kit/registry/discover.go | Adds ACS discovery for Registry service/factory contracts. |
| registry-kit/registry/context.go | Adds Registry mint choice-context construction helpers. |
| registry-kit/registry/bootstrap.go | Adds local bootstrap for Registry services (operator=provider=registrar). |
| registry-kit/registry/acs.go | Adds generic ACS contract lookup helpers by template/entity. |
| registry-kit/registry-kit.yaml.example | Adds example devnet configuration (TOML-formatted). |
| registry-kit/operator/backend.go | Adds HTTP client for DA operator backend choice-context + disclosed contract parsing. |
| registry-kit/main.go | Adds CLI entrypoint for canton-registry-kit. |
| registry-kit/ledger/parse.go | Adds transaction parsing helpers (created contract IDs, lock/burn & release outputs). |
| registry-kit/ledger/devnet_client.go | Adds devnet client connection helper using CLDF RPC participant + auth. |
| registry-kit/ledger/ctf_client.go | Adds participant-backed command submission wrapper (create/exercise + disclosures). |
| registry-kit/ledger/client.go | Adds ledger client interface abstraction for registry-kit. |
| registry-kit/go.mod | Introduces standalone Go module for registry-kit. |
| registry-kit/config/state.go | Adds persisted state file for CLI progress tracking. |
| registry-kit/config/config.go | Adds config loader/validator and party-role selection helpers. |
| registry-kit/config/config_test.go | Adds config + state round-trip test. |
| registry-kit/cmd/runtime.go | Adds CLI runtime (config/state loading, connecting, operator client, timeouts). |
| registry-kit/cmd/root.go | Adds Cobra root command + global flags. |
| registry-kit/cmd/operator.go | Adds CLI commands for TAR linking and validation. |
| registry-kit/cmd/onboarding.go | Adds CLI onboarding commands (package checks, provider service request/wait, registrar onboarding, discovery). |
| registry-kit/cmd/issuer.go | Adds CLI issuer lifecycle commands (create instrument, mint, accept mint, query supply). |
| registry-kit/ccip/validate.go | Adds read-only TAR/pool mapping validation and holdings visibility checks. |
| registry-kit/ccip/send.go | Adds BurnMintTokenPool LockOrBurn helpers + SendingMessage creation and TAR factory config. |
| registry-kit/ccip/release.go | Adds BurnMintTokenPool ReleaseFromTicket helper. |
| registry-kit/ccip/register.go | Adds TAR token pool registration helpers (sequence-based + explicit actAs via ledger client). |
| registry-kit/ccip/pool_context.go | Adds disclosure builders and nested extraContext construction for pool send/release/execute. |
| registry-kit/ccip/pool_context_v1.go | Adds “latest bindings” variant of pool extraContext for integration-test compatibility. |
| registry-kit/ccip/deploy.go | Adds helpers to deploy rate limiters and burn/mint pools for a given owner party. |
| registry-kit/ccip/bundle.go | Adds CLDF operations bundle helper. |
| integration-tests/go.mod | Adds local replace for registry-kit and switches canton dep to local v0.0.0. |
| integration-tests/ccip/registry_send_helpers_test.go | Adds helpers for building pool disclosures/choice-context for registry token pool tests. |
| integration-tests/ccip/ccip_execute_token_registry_test.go | Adds full receive-flow integration test using Registry holdings + registrar-owned token pool. |
| contracts/contracts.go | Adds Utility DAR packages + pinned package IDs + splice package constants. |
| contracts/cmd/bindings/main.go | Adjusts bindings generation to skip prim/stdlib dalfs. |
| bindings/generated/latest/utility/registry_holding_v0/tuple2.go | Adds Tuple2 shim type required by utility DAR bindings. |
| bindings/generated/latest/utility/credential_v0/tuple2.go | Adds Tuple2 shim type required by utility DAR bindings. |
| bindings/generated/latest/utility/credential_v0/credential_v0.go | Adds generated utility credential bindings. |
| bindings/generated/latest/splice/splice_api_token_allocation_v1/splice_api_token_allocation_v1.go | Adds generated splice allocation bindings. |
| bindings/generated/latest/splice/splice_api_token_allocation_instruction_v1/splice_api_token_allocation_instruction_v1.go | Adds generated splice allocation-instruction bindings. |
| bindings/generated/latest/splice/splice_api_featured_app_v1/splice_api_featured_app_v1.go | Adds generated splice featured-app bindings. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
2a2b453 to
e229d2f
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 54 out of 66 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- contracts/cmd/bindings/utility_patch.go: Generated file
Suppressed comments (8)
registry-kit/registry/acs.go:51
- FindFirstContractByEntity also accepts template as
any; with FindContractsByEntity updated to require GetTemplateID, this helper should use the same interface type to avoid runtime panics and keep the API consistent.
registry-kit/registry/supply.go:39 - QuerySupply filters holdings only by Instrument.Id and ignores the instrument admin/registrar. If two registrars use the same instrument ID string, this will aggregate unrelated holdings into the same supply total.
registry-kit/registry/mint_devnet.go:121 - FindMintRequestForInstrument takes an instrumentID parameter but never uses it, so it can return a MintRequest for a different instrument when multiple requests exist in the ACS.
registry-kit/registry/packages.go:83 - PackageCheckResult.FoundID is always set to the expected ID even when the package is missing, which makes the result struct misleading for callers/logging.
registry-kit/registry/acs.go:21 - FindContractsByEntity uses a forced type assertion (template.(interface{ GetTemplateID() string })) which will panic at runtime if a caller passes the wrong template type. Since GetTemplateID is the only requirement, accept an interface type in the signature instead.
This issue also appears on line 48 of the same file.
registry-kit/ccip/pool_context.go:217
- DisclosePoolReleaseContracts always uses the provided
clientfor disclosing CCIP-owned contracts (TokenAdminRegistry, RMNRemote, TokenReceiveTicket). This makes the helper unusable in the same multi-participant setups that PoolSend/PoolExecute support via a separate CcipClient.
integration-tests/ccip/ccip_execute_test.go:87 - finalityConfigValueFromBlockConfirmations no longer accepts a blockConfirmations argument, but the name still implies it does. Making the parameter optional (variadic) keeps the current call sites working while restoring the meaning of the name (and the previous WaitForFinality behavior for 0).
func finalityConfigValueFromBlockConfirmations() *apiv2.Value {
return &apiv2.Value{Sum: &apiv2.Value_Variant{Variant: &apiv2.Variant{
Constructor: "BlockDepth",
Value: &apiv2.Value{Sum: &apiv2.Value_Int64{Int64: 2000}},
}}}
}
contracts/utilitydars/fetch.go:188
- downloadBundle uses http.DefaultClient with no timeout. If ctx has no deadline/cancel, a network stall can hang indefinitely while fetching the utility DAR bundle (and GetDar currently calls EnsureCache with context.Background()). Consider using an http.Client with a sane Timeout and/or enforcing a default deadline when ctx has none.
func downloadBundle(ctx context.Context, url string) ([]byte, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("create utility bundle request: %w", err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("download utility bundle: %w", err)
}
defer resp.Body.Close()
| // EncodeUint256Hex encodes a base-10 integer string as a 32-byte hex string (CCIP MessageCodec style). | ||
| func EncodeUint256Hex(decimalValue string) string { | ||
| n := new(big.Int) | ||
| _, ok := n.SetString(decimalValue, 10) | ||
| if !ok { | ||
| panic("invalid decimal: " + decimalValue) | ||
| } | ||
| hexStr := n.Text(16) | ||
| return strings.Repeat("0", 64-len(hexStr)) + hexStr | ||
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 55 out of 68 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- contracts/cmd/bindings/utility_patch.go: Generated file
Suppressed comments (4)
registry-kit/ccip/send.go:271
EncodeUint256Hexcurrently panics on parse failures and can also panic if the input overflows 256 bits (negative padding length). Since this is in a non-test package and exported, it should return an error instead of panicking and validate uint256 bounds.
contracts/utilitydars/fetch.go:186downloadBundleuseshttp.DefaultClientwith no timeout. Since this can run in CI or at runtime (viacontracts.GetDar), a stalled connection can hang indefinitely. Use anhttp.Clientwith a reasonable timeout (or enforce a context deadline).
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, fmt.Errorf("download utility bundle: %w", err)
}
registry-kit/registry/packages.go:83
CheckPackagessetsFoundIDtoexpectedIDeven when the package is missing, which makes the per-package results misleading (a missing package appears to have a found ID). Consider using the actual found ID from the lookup (empty when missing).
integration-tests/ccip/ccip_execute_test.go:86finalityConfigValueFromBlockConfirmationsno longer accepts a block depth and always returns a constant (2000), so the name is now misleading and may confuse future changes/tests. Consider renaming it to reflect the fixed value or reintroducing the parameter.
func finalityConfigValueFromBlockConfirmations() *apiv2.Value {
return &apiv2.Value{Sum: &apiv2.Value_Variant{Variant: &apiv2.Variant{
Constructor: "BlockDepth",
Value: &apiv2.Value{Sum: &apiv2.Value_Int64{Int64: 2000}},
}}}
| for i := range len(raw) { | ||
| if raw[i] == ':' { | ||
| out = append(out, raw[start:i]) | ||
| start = i + 1 | ||
| } | ||
| } |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 55 out of 68 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- contracts/cmd/bindings/utility_patch.go: Generated file
Suppressed comments (1)
registry-kit/registry/packages.go:83
FoundIDis always set to the expected package ID, even when the package is missing. This makes the output misleading (a missing package will still report a non-empty FoundID). Set FoundID only whenfoundis true (or populate it from the installed package details).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 55 out of 68 changed files in this pull request and generated no new comments.
Files not reviewed (1)
- contracts/cmd/bindings/utility_patch.go: Generated file
Suppressed comments (4)
contracts/contracts.go:253
- getUtilityDar calls EnsureCache with context.Background(), so a network fetch for utility DARs can block indefinitely (no cancellation/deadline). Using a bounded context here avoids hanging CLI/test runs when the bundle URL is slow/unreachable.
if err := utilitydars.EnsureCache(context.Background(), utilityPackageIDs()); err != nil {
return nil, fmt.Errorf("ensure utility DAR cache: %w", err)
}
integration-tests/ccip/ccip_execute_test.go:86
- finalityConfigValueFromBlockConfirmations no longer takes a confirmation count and always returns BlockDepth=2000, so the name is now misleading. Consider either restoring a parameter or renaming to reflect the fixed default.
func finalityConfigValueFromBlockConfirmations() *apiv2.Value {
return &apiv2.Value{Sum: &apiv2.Value_Variant{Variant: &apiv2.Variant{
Constructor: "BlockDepth",
Value: &apiv2.Value{Sum: &apiv2.Value_Int64{Int64: 2000}},
}}}
registry-kit/registry/packages.go:84
- PackageCheckResult.FoundID is always set to the expected ID, even when Found is false, which makes the output misleading (it looks like the package ID was found when it wasn’t). Set FoundID only when the package is present.
registry-kit/ccip/send.go:282 - EncodeUint256Hex panics on invalid input. Since this is an exported helper, returning an error is safer than crashing the process (especially if this gets reused in CLI paths).
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 55 out of 68 changed files in this pull request and generated 1 comment.
Files not reviewed (1)
- contracts/cmd/bindings/utility_patch.go: Generated file
Suppressed comments (4)
registry-kit/operator/backend.go:205
for i := range len(raw)does not compile in Go (cannot range over an int). Range over the string itself to iterate indices.
registry-kit/registry/context.go:23new(types.CONTRACT_ID(instrumentConfigCID))/new(types.BOOL(true))won’t compile becausenewtakes a type, not a value. Create typed variables and take their addresses instead so the ChoiceContext contains the intended values.
registry-kit/ccip/pool_context_v1.go:20- This function uses
new(types.CONTRACT_ID(...))andnew(types.BOOL(true)), which won’t compile becausenewtakes a type, not a value. Use typed variables and take their addresses so the ChoiceContext encodes the intended contract IDs/bool.
contracts/utilitydars/fetch.go:186 http.DefaultClienthas no timeouts, so fetching the utility bundle can hang indefinitely if the remote stalls (and callers might not set a deadline onctx). Consider enforcing a bounded timeout for the download.
resp, err := http.DefaultClient.Do(req)
| func registryPoolBurnMintExtraContext( | ||
| rateLimiterCID, allocationFactoryCID, instrumentConfigCID string, | ||
| enableResultContracts bool, | ||
| ) splice_api_token_metadata_v1.ChoiceContext { | ||
| nestedValues := registry.MintChoiceContext(instrumentConfigCID, enableResultContracts).Values | ||
|
|
||
| return splice_api_token_metadata_v1.ChoiceContext{ | ||
| Values: map[string]splice_api_token_metadata_v1.AnyValue{ | ||
| string(ratelimiter.RateLimiterContextKey): { | ||
| AVContractId: new(types.CONTRACT_ID(rateLimiterCID)), | ||
| }, | ||
| string(burnminttokenpool.BurnMintFactoryContextKey): { | ||
| AVContractId: new(types.CONTRACT_ID(allocationFactoryCID)), | ||
| }, | ||
| string(burnminttokenpool.BurnMintFactoryExtraArgsContextValuesContextKey): { | ||
| AVMap: &nestedValues, | ||
| }, | ||
| }, | ||
| } | ||
| } |
| }}} | ||
| } | ||
|
|
||
| func finalityConfigValueFromBlockConfirmations() *apiv2.Value { |
There was a problem hiding this comment.
What's this? 😆
The method is called FromBlockConfirmations but we're removing the parameter?
There was a problem hiding this comment.
Every test was using the same 2000 value
There was a problem hiding this comment.
I see that - it just seems like an odd choice and LLM-y to touch this unrelated function here and keep calling it FromBlockConfirmations when there isn't anything to take the block confirmations from anymore
add integration tests with registry fix rebase add small fixes fix lint tidy mods remove not used reg dars add registry mod to ci remove utility dars
accb943 to
4653040
Compare
Invalid CODEOWNERS file detected - @RodrigoAD.1 error(s) were found in the CODEOWNERS file. See the workflow summary and PR annotations for more information. |
| Meta: emptyMetadata(), | ||
| }, | ||
| } | ||
| res, err := client.SubmitExerciseMulti(ctx, mintActAs(bootstrap.Party, owner), registryapp.AllocationFactory{}, bootstrap.AllocationFactory, "AllocationFactory_RequestMint", args, []*apiv2.DisclosedContract{disclosed}) |
There was a problem hiding this comment.
Shouldn't the mint be requested from just the owner? By acting as both parties, doesn't this defeat the purpose of the request-accept-pattern?
There was a problem hiding this comment.
We are using a a single-participant test bootstrap in the test, so for this case makes sense. If we want a realistic mint where the holder and registrar are on different participants with separate credentials, then yes, we'd want two separate submissions
Uh oh!
There was an error while loading. Please reload this page.