The Provenance Blockchain name service builds up a hierarchy of names similar to DNS using dot-separated strings, where each level in the hierarchy can be owned by an account that must sign transactions to add new names under that level. The system supports delegating control through ownership transfer, implements strict normalization rules for name components including length restrictions and character validation, uses the restricted flag to control child name creation permissions, and requires root names to be created through genesis or governance proposals due to the hierarchical dependency structure.
While public names is not traditional to many blockchains, Provenance was created to engender trust in blockchain technology amongst the financial community. Thus, the name module was created, which can give increased transparency, on chain and when required/desired, for greater trust and adoption.
ConceptsDelegating ControlNormalizationNormalization RulesCreation of Root NamesStateName Record KV ValuesExample Key StructureAddress Record KV IndexExample Address IndexName RecordMessagesMsgBindNameRequestExpected FailuresMsgDeleteNameRequestExpected FailuresMsgModifyNameRequestExpected FailuresCreateRootNameProposalExpected FailuresMsgCreateRootNameRequestExpected FailuresEventsHandlersMsgBindNameRequestMsgDeleteNameRequestMsgModifyNameRequestCreateRootNameProposalRelated ResourcesQueriesParamsQueryParamsRequestQueryParamsResponseResolveQueryResolveRequestQueryResolveResponseReverseLookupQueryReverseLookupRequestQueryReverseLookupResponseParamsName Service ParametersParametersParameter DetailsMaxSegmentLengthMinSegmentLengthMaxNameLevelsAllowUnrestrictedNames
Concepts
Delegating Control
Every label in a name is owned by an address. Starting from the root address each level can be configured to allow any user to add a new child or for the exclusive control of the creator to add child names. The
Restricted
flag is used to indicate the permission requirements for adding child nodes.protobuf// Source: https://github.com/provenance-io/provenance // NameRecord is a structure used to bind ownership of a name heirarchy to a collection of addresses message NameRecord { option (gogoproto.goproto_stringer) = false; // The bound name string name = 1; // The address the name resolves to. string address = 2; // Whether owner signature is required to add sub-names. bool restricted = 3; }
Normalization
Name records are normalized before being processed for creation or query. Each component of the name must conform to a standard set of rules. The sha256 of the normalized value is used internally for comparison purposes.
Normalization Rules
- Names are always stored and compared using a lower case form or a hash derived from this normalized form.
- Unicode values that are not graphic, lower case, or digits are considered invalid.
- A single occurrence of the hyphen-minus character is allowed unless the value conforms to a valid UUID.
HYPHEN-MINUS
Unicode: U+002D, UTF-8: 2D
- Each component of the name is restricted to a length of 2 to 32 characters (inclusive). These limits are configurable in the module parameters.
- A maximum of 16 components for a name (levels in the hierarchy) is also enforced and configurable in the module parameters.
- Leading and trailing spaces are always trimmed off of names for consistency during processing and evaluation.
Creation of Root Names
As every name hierarchy depends on the name above it for permissioning and control, the root names present a problem with no parent to enforce their management. Because of this inception problem root names must be created in the genesis of the blockchain or through a governance proposal process.
State
The Provenance Blockchain name service maintains a simple state collection that stores name records using hash-based keys for efficient hierarchical queries and includes an address cache for fast reverse lookup operations. The state uses concatenated SHA256 hashes of each label component to create unique keys that enable quick iteration over all names under a given hierarchy level, while maintaining a separate address-to-name index for reverse resolution queries, with all records encoded using the NameRecord protobuf structure that binds names to addresses and ownership permissions.
Name Record KV Values
Name records are stored using a key based upon a concatenated list of hashes based on each label within the name. This approach allows all of the names in the tree under a given name to be quickly queried and iterated over.
Example Key Structure
Name:
foo
Key:
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae
Name:
foo.bar
Key:
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae.fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9
Name:
foo.bar.baz
Key:
2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae.fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9.baa5a0964d3320fbc0c6a922140453c8513ea24ab8fd0577034804a967248096
Address Record KV Index
In addition to the records stored by name an address cache is maintained for the addresses associated with each name record. This allows simple and fast reverse lookup queries to be performed.
Example Address Index
Address:
pb1tg3ktger9ttlscehl3r5j4pqw7qzmvs4qr9vpm
Key:
5A2365A3232AD7F86337FC4749542077802DB215.2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae.fcde2b2edba56bf408601fb721fe9b5c338d10ee429ea04fae5511b68fbf8fb9
Value:
foo.bar
Name Record
Name records are encoded using the following protobuf type:
protobuf// Source: https://github.com/provenance-io/provenance // NameRecord is a structure used to bind ownership of a name heirarchy to a collection of addresses message NameRecord { option (gogoproto.goproto_stringer) = false; // The bound name string name = 1; // The address the name resolved to. string address = 2; // Whether owner signature is required to add sub-names. bool restricted = 3; }
Messages
The Provenance Blockchain name service provides a comprehensive set of messages for managing the hierarchical name system, including MsgBindNameRequest for creating new name records under existing parents with ownership validation, MsgDeleteNameRequest for removing leaf name records and their associated attributes, MsgModifyNameRequest for updating existing name records through governance or owner authority, and both CreateRootNameProposal and MsgCreateRootNameRequest for establishing new root-level names through the governance process, ensuring proper authorization and validation at each level of the name hierarchy.
MsgBindNameRequest
A name record is created using the
MsgBindNameRequest
message.protobuf// Source: https://github.com/provenance-io/provenance message MsgBindNameRequest { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // The parent record to bind this name under. NameRecord parent = 1 [(gogoproto.nullable) = false]; // The name record to bind under the parent NameRecord record = 2 [(gogoproto.nullable) = false]; }
Expected Failures
This message is expected to fail if:
- The parent name record does not exist
- The requestor does not match the owner listed on the parent record and the parent record indicates creation of child records is restricted
- The record being created is otherwise invalid due to format or contents of the name value itself
- Insufficient length of name
- Excessive length of name
- Not deriving from the parent record (targets another root)
If successful a name record will be created as described and an address index record will be created for the address associated with the name.
MsgDeleteNameRequest
The delete name request method allows a name record that does not contain any children records to be removed from the system. All associated attributes on account addresses will be deleted.
protobuf// Source: https://github.com/provenance-io/provenance // MsgDeleteNameRequest defines an sdk.Msg type that is used to remove an existing address/name binding. The binding // may not have any child names currently bound for this request to be successful. All associated attributes on account addresses will be deleted. message MsgDeleteNameRequest { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // The parent record the record to remove is under. NameRecord parent = 1 [(gogoproto.nullable) = false]; // The record being removed NameRecord record = 2 [(gogoproto.nullable) = false]; }
Expected Failures
This message is expected to fail if:
- Any components of the request do not pass basic integrity and format checks
- The parent name record does not exist
- The record to remove does not exist
- Any child records exist under the record being removed
- The requestor does not match the owner listed on the record
MsgModifyNameRequest
A name record is modified by proposing the
MsgModifyNameRequest
message.protobuf// Source: https://github.com/provenance-io/provenance // MsgModifyNameRequest defines a method that is used to update an existing address/name binding. message MsgModifyNameRequest { option (cosmos.msg.v1.signer) = "authority"; // The address signing the message string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // The record being updated NameRecord record = 2 [(gogoproto.nullable) = false]; }
Expected Failures
This message is expected to fail if:
- Any components of the request do not pass basic integrity and format checks
- The record to update does not exist
- The authority does not match the gov module or the name owner
If successful a name record will be updated with the new address and restriction.
CreateRootNameProposal
The create root name proposal is a governance proposal that allows new root level names to be established after the genesis of the blockchain.
protobuf// Source: https://github.com/provenance-io/provenance message CreateRootNameProposal { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; option (gogoproto.goproto_stringer) = false; string title = 1; string description = 2; string name = 3; string owner = 4; bool restricted = 5; }
Expected Failures
This message is expected to fail if:
- The name already exists
- Insufficient length of name
- Excessive length of name
MsgCreateRootNameRequest
The
MsgCreateRootNameRequest
is a governance proposal that allows new root level names to be established after the genesis of the blockchain.protobuf// Source: https://github.com/provenance-io/provenance message MsgCreateRootNameRequest { option (cosmos.msg.v1.signer) = "authority"; // The signing authority for the request string authority = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"]; // NameRecord is a structure used to bind ownership of a name hierarchy to a collection of addresses NameRecord record = 2; }
Expected Failures
This message is expected to fail if:
- The name already exists
- Insufficient length of name
- Excessive length of name
- The authority does not match the gov module
If successful a name record will be created with the provided address and restriction.
Events
The Provenance Blockchain name service emits specific events for each message handler operation to provide comprehensive tracking and auditing of name record lifecycle management. These events include name_bound events for successful name creation and root name proposals, name_unbound events for name deletion operations, and name_modify events for name record updates, with each event containing detailed attribute information about the name, address, and restriction status to enable complete visibility into name service operations.
Handlers
MsgBindNameRequest
Type | Attribute Key | Attribute Value |
name_bound | name | {NameRecord.name} |
name_bound | address | {NameRecord.address} |
name_bound | restricted | {NameRecord.restricted} |
MsgDeleteNameRequest
Type | Attribute Key | Attribute Value |
name_unbound | name | {NameRecord.name} |
name_unbound | address | {NameRecord.address} |
name_unbound | restricted | {NameRecord.restricted} |
MsgModifyNameRequest
Type | Attribute Key | Attribute Value |
name_modify | authority | {String} |
name_modify | name | {NameRecord.name} |
name_modify | address | {NameRecord.address} |
name_modify | restricted | {NameRecord.restricted} |
CreateRootNameProposal
Type | Attribute Key | Attribute Value |
name_bound | name | {NameRecord.name} |
name_bound | address | {NameRecord.address} |
name_bound | restricted | {NameRecord.restricted} |
Related Resources
- GitHub Repository: provenance-io/provenance
- Documentation: Provenance Blockchain Developer Portal
go// Example event emission structure // Source: https://github.com/provenance-io/provenance // Event types emitted by the name module const ( EventTypNameBound = "name_bound" EventTypeNameUnbound = "name_unbound" EventTypeNameModify = "name_modify" ) // Attribute keys used in events const ( AttributeKeyName = "name" AttributeKeyAddress = "address" AttributeKeyRestricted = "restricted" AttributeKeyAuthority = "authority" )
Queries
The Provenance Blockchain name service provides a comprehensive query interface for resolving names to addresses and performing reverse lookups to find all names associated with a given address. The query service includes endpoints for retrieving module parameters, resolving names to their bound addresses with restriction status information, and performing reverse lookups with pagination support to discover all names bound to a specific address, enabling efficient name resolution and discovery operations within the hierarchical name system.
Params
Queries the parameters of the name module.
HTTP Endpoint:
GET /provenance/name/v1/params
Request:
QueryParamsRequest
Response: QueryParamsResponse
QueryParamsRequest
protobuf// Source: https://github.com/provenance-io/provenance // QueryParamsRequest is the request type for the Query/Params RPC method. message QueryParamsRequest {}
QueryParamsResponse
protobuf// Source: https://github.com/provenance-io/provenance // QueryParamsResponse is the response type for the Query/Params RPC method. message QueryParamsResponse { // params defines the parameters of the module. Params params = 1 [(gogoproto.nullable) = false]; }
Resolve
Queries for the address associated with a given name.
HTTP Endpoint:
GET /provenance/name/v1/resolve/{name}
Request:
QueryResolveRequest
Response: QueryResolveResponse
QueryResolveRequest
protobuf// Source: https://github.com/provenance-io/provenance // QueryResolveRequest is the request type for the Query/Resolve method. message QueryResolveRequest { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // name to resolve the address for string name = 1; }
QueryResolveResponse
protobuf// Source: https://github.com/provenance-io/provenance // QueryResolveResponse is the response type for the Query/Resolve method. message QueryResolveResponse { // a string containing the address the name resolves to string address = 1; // Whether owner signature is required to add sub-names. bool restricted = 2; }
ReverseLookup
Queries for all names bound against a given address.
HTTP Endpoint:
GET /provenance/name/v1/lookup/{address}
Request:
QueryReverseLookupRequest
Response: QueryReverseLookupResponse
QueryReverseLookupRequest
protobuf// Source: https://github.com/provenance-io/provenance // QueryReverseLookupRequest is the request type for the Query/ReverseLookup method. message QueryReverseLookupRequest { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // address to find name records for string address = 1; // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageRequest pagination = 2; }
QueryReverseLookupResponse
protobuf// Source: https://github.com/provenance-io/provenance // QueryReverseLookupResponse is the response type for the Query/Resolve method. message QueryReverseLookupResponse { option (gogoproto.equal) = false; option (gogoproto.goproto_getters) = false; // an array of names bound against a given address repeated string name = 1; // pagination defines an optional pagination for the request. cosmos.base.query.v1beta1.PageResponse pagination = 2; }
protobuf// Example Query Service Definition // Source: https://github.com/provenance-io/provenance service Query { // Params queries params of the name module. rpc Params(QueryParamsRequest) returns (QueryParamsResponse) { option (google.api.http).get = "/provenance/name/v1/params"; } // Resolve queries for the address associated with a given name rpc Resolve(QueryResolveRequest) returns (QueryResolveResponse) { option (google.api.http).get = "/provenance/name/v1/resolve/{name}"; } // ReverseLookup queries for all names bound against a given address rpc ReverseLookup(QueryReverseLookupRequest) returns (QueryReverseLookupResponse) { option (google.api.http).get = "/provenance/name/v1/lookup/{address}"; } }
Params
Name Service Parameters
The Provenance Blockchain name service module contains configurable parameters that control the structure and validation rules for the hierarchical name system. These parameters define the minimum and maximum segment lengths for individual name components, set limits on the total number of hierarchy levels allowed in a name, and control whether unrestricted names can be created, providing essential constraints to ensure name consistency, prevent abuse, and maintain the integrity of the naming system across the blockchain network.
Parameters
The name module contains the following parameters:
Key | Type | Example |
MaxSegmentLength | uint32 | 32 |
MinSegmentLength | uint32 | 2 |
MaxNameLevels | uint32 | 16 |
AllowUnrestrictedNames | bool | false |
Parameter Details
MaxSegmentLength
Maximum length allowed for individual name segments (components).
Type:
uint32
Example: 32
MinSegmentLength
Minimum length required for individual name segments (components).
Type:
uint32
Example: 2
MaxNameLevels
Maximum number of levels (hierarchy depth) allowed in a complete name.
Type:
uint32
Example: 16
AllowUnrestrictedNames
Controls whether unrestricted names can be created in the system.
Type:
bool
Example: false