.canton Naming CIP#209
Conversation
meiersi-da
left a comment
There was a problem hiding this comment.
Thanks @dave-axymos . This looks like a good first step. The current write-up is rather technical, and requires quite a few things to "reverse engineered".
What do you think about structuring the document to clearly separate:
- the UX to be built: what problems are solved, and what UX flows are provided to solve them
- the economic model: who does what and why are they willing to do this?
- the technical implementation: split into
- component view: dApp(s), backends running at registrars, .dar packages vetted by users, .dar packages vetted by registrars only
- information flows: how does the information flow across the components to for the different UX flows described in (1)
- key design decisions: e.g., how to ensure uniqueness of names
- link to PoC implementation (where available)
|
|
||
| Governance of the service is managed by consensus among the approved registrars. Parameters of the service (like min pricing, vote thresholds etc) can be agreed upon by the governance layer and then are stored in the Name Registry contract itself. Registrars compete to provide name sales, renewals, and support to end users, but every name they sell is recorded in the same canonical `NameRegistry` contract. | ||
|
|
||
| The reference implementation (to follow) is a DAML contract package targeting Canton SDK 3.6.0. All authorisation flows through DAML's signatory model; the DAR would be vetted, so holders can exercise their own choices directly via the JSON Ledger API. |
There was a problem hiding this comment.
Why 3.6, that is not yet out. Did you mean 3.5, which is what provides contract keys?
There was a problem hiding this comment.
I remember hitting an issue when trying to compile against 3.5 — I'll compile against that target again and double-check!
There was a problem hiding this comment.
btw, if 3.5 provides contract keys does that mean that they're available now on devnet @meiersi-da ?
There was a problem hiding this comment.
Contract keys require connecting to a synchronizer running Canton Protocol Version 35 (PV=35), which in turn requires a Canton 3.5.x participant node that understands PV=35. For local development that's easy, just spin up a synchronizer with with PV=35 and your done.
For DevNet this requires a logical synchronizer migration to change the synchchronizers protocol from its current PV=34 to PV=35. This is coming soon. Here's the CIP proposing the upgrade: https://github.com/canton-foundation/cips/pull/208/changes#diff-2fd1b7daef2c45396290d28ec49523ef503f8af706f2308ab1932751ffe1aea2
There was a problem hiding this comment.
And here's the operations schedule for the networks: https://docs.google.com/document/d/1QhLL5bL0u8temBL86y957VbWDtZJhH9udH-_C7nBlvc/edit?tab=t.0#heading=h.ripdn5ydglli
search for LSU
There was a problem hiding this comment.
@meiersi-da thanks, when I saw 0.6.4 drop yesterday I was hopefully it was on devnet already! 😅
I've started to deploy a "scratchnet" super validator stack to EC2 just so I can have a play around with it from our own boxes as multiple validators. Will be great when it's on devnet! thanks for sharing the schedule, that's really helpful.
| -> assertMsg "Payment >= minPriceFloor" | ||
| -> lookupByKey @NameRecord -> assertMsg "Name not already registered" | ||
| -> Fee split via chained TransferFactory calls: | ||
| 1. Treasury transfer (paymentHoldingCids -> DRO); capture senderChangeCids |
There was a problem hiding this comment.
We generally avoid having the DSO receiver transfers. We want it to only facilitate the flows, but not partake in the flows.
There was a problem hiding this comment.
Sorry I never replied to this — so this is our "DRO" here rather than the DSO proper. Did you mean that that's a pattern to avoid here too?
| 3. Sibling[1] transfer (senderChangeCids from step 2); ... | ||
| Each transfer consumes its inputHoldingCids and returns new change holdings. | ||
| Registrar retains their fee as the final change holdings (no transfer needed). | ||
| -> create NameRecord (live immediately; usable from this point) |
There was a problem hiding this comment.
It's not clear to me who pays whom and how much.
There was a problem hiding this comment.
Yeah this needs to be nailed down and better explaiend.
Our thinking at the moment was that if there's some off-chain work.co-ordination needed between registrars then there would be fee split going on to account for that level of effort.
Then we'd also on-chain enforce things like a floor for prices
|
|
||
| #### Transfer | ||
|
|
||
| Names can be transferred either in the case of: |
There was a problem hiding this comment.
Interesting idea. In that case, I'd suggest to represent names as CIP-56 assets by making each name a holding with InstrumentId with admin = dso; id = 'name' and amount = 1.0.
This way these names can be held and managed using a token standard wallet.
There was a problem hiding this comment.
And they can be traded.
There was a problem hiding this comment.
that sounds really good - I was toying with this before and I can't remember why we moved away from it at the time. I'll revisit it in the reference impl to trial. thanks!
|
|
||
| #### Dispute lifecycle | ||
|
|
||
| Disputes occur in the case that a disputing registrar claims the registration should not stand — e.g. a rogue registrar registering an agreed reserved name, or a holder using the name in a way that violates registrar conduct policy. |
There was a problem hiding this comment.
What defines an "agreed reserved name"?
There was a problem hiding this comment.
What defines an "agreed reserved name"?
At the moment for v1 live on chain, we've a reserve list that we don't let people register out of the gate — mainly profanity and clear phishing attempts e.g. stopping end users signing up as hsbc.canton, just to allow a decision to be made on that approach.
I think there's multiple options we could explore here:
- free rein — any name goes
- literally register the names that we think may want to be held (e.g. the names of each validator/supervalidator say) with a long expiry
- an off-chain list (which we have here at the moment)
I think we need to go away and strengthen up here the "what" and the "why" both and a path to someone claiming a reservered name etc if that's the why we go down.
| -> CounterStake (takes registryCid) -> fetches live registry -> sets voteDeadline (now + registry.voteWindow) | ||
| -> continue to step 4 | ||
| | | ||
| 4. Registrars vote via AddVote (True = for dispute, False = against) |
There was a problem hiding this comment.
This sounds like it might be quite a bit of work. What's the motivation for registrars to partake in these votes?
There was a problem hiding this comment.
Appreciate the challenge to reframe here! :)
Again this is probably something that we're porting over from a trustless model. We're thinking of a flow similar to a Token Curated Registry where e.g.
- Companies agree to stake X to become a registrar
- In the event of a dispute, a contesting registrar can trigger a vote
- There's a fee split between registrars voting
a. each voting registrar receives financial incentive to vote
b. winning registrar (disputer or challenger) receives a fee
In fact, based on the "do we need a reserve list" above, maybe this piece could naturally fall away - if there are no reserved names then there's no need to dispute on chain
| -> assertMsg "Registrar not in allowlist" | ||
| -> assertMsg "Invalid name format" (isValidName — lowercase, .canton suffix, no leading/trailing hyphens, 1–63 chars) | ||
| -> assertMsg "Payment >= minPriceFloor" | ||
| -> lookupByKey @NameRecord -> assertMsg "Name not already registered" |
There was a problem hiding this comment.
There is no uniqueness guarantee for contract keys in Canton 3.x
I suspect that name allocation will have to be funnelled through an on-ledger representation of the map of all names (e.g. as a prefix tree) to ensure uniqueness even when there are many different nodes submitting name allocation transactions.
There was a problem hiding this comment.
The way we were looking to leverage contract keys was to do make sure that we gate our look-up on the fact that the multi-hosted party (the 'DRO') is the maintainer of the key and then it's choosing not to create duplicates.
So having a flow of:
- Registrar e.g. Axymos does a look-up on chain to see if a name is available
- If it is, calls
NameRegistry.RegisterName()- which does a look-up of all active contracts owned collectively by the registrar pool and checks that it doesn't exist - So then using
len(results) == 0as a proxy check for uniqueness. - Since the DRO is the key
maintainerand since all registrars are registering as the DRO then our thoughts were that were safe enough to gate it
Couple of thoughts:
- we were leaning on the atomicity and sequencing of transactions on the network to make sure that race conditions prevented the second registration
- in the case of a "rogue registrar" (e.g. someone offboarded from the allowlist but still a host of the DRO) could bypass the path of driving all registrations through
RegisterName()and instead just hitcreateCmdon NameRecord directly.
One thing we had that I seem to have accidentally dropped from NameRecord was the concept of activation. So thinking that the flow would be:
- registrar calls
NameRegistry.RegisterName() - registrats calls
NameRecord.Activate()
Then resolving a name requires that it has been activated. I'm coming from a trustless EVM world so maybe that's overkill and we've better practices in Canton Network around stuff like this (e.g. literal contractual sign-up, say)
| 4. **On-chain integrity** — Names are guaranteed unique via on-chain checks. We rely on the underlying infrastructure to solve for the race conditions/atomicity of these registrations. All authorisation flows through DAML's signatory model. | ||
|
|
||
|
|
||
| ### Open Questions |
There was a problem hiding this comment.
I'm also missing clarity on the end-to-end flow for end-user actions: how will users build and submit their transactions (e.g., will they use a ".canton dApp" built as part of this CIP, or will they use their token standard wallet)? what transactions are built and submitted by end-users, which ones by registrars?
There was a problem hiding this comment.
So existing in production for us, Axymos as registrar are submitting all TXs on the user behalf (as our DAR is running on our node, solely) - we were trying to move to a position where users would be able to do more with their own assets, so were thinking if this becomes a CIP standard, that
- end-users would be able to have the DAR vetted on their node, so could interact directly, signing TXs as themselves
- hosting nodes would be able to query the
NameRegistry"natively" from their own node (if added as an observer to the registry, or maybe if the DSO is a singleton observer? Note sure if that puts a burden on the DSO though) - non-hosting nodes or external companies could still use APIs provided by any registrar to query the records.
At the moment, we're thinking that end users would co-sign TXs for things that they particular are actioning like if they choose to release a name (Credential_ArchiveAsHolder()) or for TransferWithApproval (to support transfer/sales/etc)
You're right that we've more focused the CIP on the on-chain elements that would allow registrars to build a full end-to-end product on top, rather than including elements like what a full dApp would look like.
We can definitely add more colour here to give an example, though we imagine that elements of UX etc will be driven by individual registrars building on top of this. But definitely will try to make the "bones" more obvious of who is signing what when, from where etc.
And I think if we do implement elements like CIP-56 then maybe we get more "for free" from the core standards (we're currently modelling it based on the credential inferface but maybe Token is more of a fit)
There was a problem hiding this comment.
And I think if we do implement elements like CIP-56 then maybe we get more "for free" from the core standards (we're currently modelling it based on the credential inferface but maybe Token is more of a fit)
I'd have expected for the NameRecord contracts to implement both the interfaces from the token standard and the ones from the credential standard. They server different purposes and enable different kinds of usages.
It's totally fine for one contract to implement multiple interfaces.
There was a problem hiding this comment.
And I think if we do implement elements like CIP-56 then maybe we get more "for free" from the core standards (we're currently modelling it based on the credential inferface but maybe Token is more of a fit)
I'd have expected for the
NameRecordcontracts to implement both the interfaces from the token standard and the ones from the credential standard. They server different purposes and enable different kinds of usages.It's totally fine for one contract to implement multiple interfaces.
Thanks @meiersi-da, yeah I've updated our latest approach to implement both CIP-56 and the in-flight credential standard. I've also looked at moving NameRegistry itself to implement the credential factory (mapping the "renew" flow into UpdateCredential, but then keeping other flows like sales outside of the credential interface and leaning on the CIP-56 transferallocation and DvP flows).
that sounds good, thanks @meiersi-da will restructure along those lines! |
|
|
||
| ## Reference Implementation | ||
|
|
||
| Reference implementation to follow. Currently targeting Canton SDK 3.6.0 (LF target 2.3-staging), which is still in an alpha phase. |
There was a problem hiding this comment.
We're working against contract keys, still in alpha, so are only on Sandbox at the moment, and we're also aiming to align with standards coming down the track like the RegistryV1.Credential, so have that stubbed out.
Other than that though, everything is based on working DAML code (though harder to test some aspects on a single node so sure there's more edge cases to find!)
But I'll try to make time to tidy our reference implementation and get into a shareable state soon.
Co-authored-by: Simon Meier <simon@digitalasset.com> Signed-off-by: dave-axymos <dave@axymos.com>
Co-authored-by: Simon Meier <simon@digitalasset.com> Signed-off-by: dave-axymos <dave@axymos.com>
|
@meiersi-da (and everyone!) since your feedback, have been working to:
I'm going to try and update this draft to align with these changes now, but also trying to get a scratchnet set-up working so that can test more of these corner cases with real code before locking in on shapes |
Note: we usually distinguish between decentralized parties that cannot submit transactions on their own and external parties whose tx signing keys are held externally to the validator nodes. The DSO party used for CC is setup as a decentralized party using a decentralized namespace.
The Splice Amulet UI does not. However you should be able to run https://github.com/canton-network/wallet/tree/main/examples/portfolio to get a CIP-56 wallet UI. Haven't tried it myself yet, but heard good things. |
thanks Simon, I think I understand the split as:
Is that accurate? If so, we’re looking to split at both layers, but initially thinking of modelling them 1-to-1 (so an onboarded registrar would become a quorum party to the DND and would hold a key for signing authority as the DRO). I believe this matches the DSO model. We’re also assuming (for simplicity) that each host of the DRO are themselves a validator so the “party to participant” is straightforward. (And we don’t have, say, a registrar who has their own external key but who isn’t running a validator and are hosted on elsewhere. Though guess in theory we could open this up, too if desired!)
Happy to remodel that if you think a different take on this would be better for our use-case? (Or maybe in the future we decide that e.g. not every registrar needs to be part of the DND itself)
But wanted to start with a structure that felt open to collaboration.
Ah cool thanks! Will look at getting wallet bridge set-up on localnet and see if I can hook that in too. |
|
On decentralized party: consider building on https://github.com/gabitu7/canton-dev-fund/blob/decentralization-manager-proposal/proposals/decentralization-manager.md |
Signed-off-by: dave-axymos <dave@axymos.com>
Signed-off-by: dave-axymos <dave@axymos.com>
Cip updates
Co-authored-by: leonidr-c7 <leo@c7.digital> Signed-off-by: dave-axymos <dave@axymos.com>
Axymos launched xNS as a naming service on Canton Network in late 2025.
Through the 'Identity and Metadata' SIG, we were invited to provide a CIP for how we could decentralise this as we move forward so that
.cantonnamespaceThis is an early draft of the CIP and we'd welcome any questions/comments/suggestions.