DSNP Specification
Welcome to the Decentralized Social Networking Protocol (DSNP) specification. Here you can find the detailed specification documentation for DSNP, official implementation specifications, associated specifications, as well as a roadmap and proposals for future extensions.
Goals & Purpose
Free communication among users on the Internet faces a variety of problems in the modern day. These challenges include censorship by state and corporate actors, the amplification of misinformation through viral content, and an ever-shrinking collection of near monopolies with absolute power over social interaction in the twenty-first century. Through the DSNP, we hope to mitigate and ideally solve these challenges in the way social interaction operates online.
How to Read This Specification
There are three core specifications currently.
Name | Version | Description |
---|---|---|
DSNP | 1.1.0 | The implementation-agnostic DSNP specification |
DSNP on Frequency (Pre-release) | pre-1.0.0 | Frequency implementation for DSNP specification |
Activity Content | 1.1.0 | A specification for DSNP-referenced content (subset of W3C Activity Streams) |
Each specification is divided into several modules. Each module describes a mostly self-contained aspect of the specification.
Versioning
DSNP specification versions follow Semantic Versioning 2.0 for releases.
Contributions
Development occurs on GitHub. All interactions must follow the Code of Conduct and Contribution Guidelines.
Draft Specifications
Proposals for some future extensions can be found in the Draft Specifications section and on GitHub; however, these proposals should be regarded with caution as they are largely incomplete and may be subject to change or deletion without notice.
Name | Status | Description |
---|---|---|
Archivist | DRAFT | Long-term DSNP Announcement Storage |
Draft Status Definitions
Name | Description |
---|---|
Draft | Open for comment and major changes. |
Proposed | Ready for formal review. It should be complete save for some minor changes. |
Tentative | This is the accepted plan. The specification should not change unless there are blocking issues. |
Learning More
In addition to this document, more resources regarding the project can be found at DSNP.org, including the blog, forum, code repository, and SDK.
Roadmap
Last Updated: 2021-09-08
Introduction
This document represents the Roadmap for DSNP and related specifications. The Roadmap may not reflect current priorities and is subject to change.
Completed
Work | Status |
---|---|
Whitepaper | Done (2020 Q4) |
Spec Outline | Done (2021 Q1) |
Batch Announce First Draft | Done (2021 Q1) |
Spec Live | Done (2021 Q1) |
Identity Contract & Delegation | Done (2021 Q2) |
Batch Announce File Format | Done (2021 Q3) |
Identity Factory | Done (2021 Q2) |
Graph Handle Registry | Done (2021 Q2) |
Batch Announce Filter System | Done (2021 Q3) |
Graph Data | Done (2021 Q3) |
Announcement/Publishing Revision | Done (2021 Q3) |
Stabilize 1.0 | Done (2021 Q3) |
Future
Below is a tentative roadmap to what protocol features are being developed next. Items are ordered by current priority.
Chain Migration
Status: In progress
The cost to use Ethereum is too high. Migrate the DSNP architecture to another chain.
Private Graph
Status: Whitepaper
Keep some graph connections private with permissioned access.
Direct Messaging
Status: Whitepaper
Offer one-to-one messaging with metadata privacy.
Public Friends
Status: Not Started
Graph connections are currently limited to one-way follow relationships. Build support for other forms of relationships that require all parties to opt in.
Social Account Recovery
Status: Not Started
Use the social graph to leverage safe account recovery and protect against private key losses.
Private Friends
Status: Not Started
Expand the private graph to other relationships while maintaining metadata protection.
Verified Attributes
Status: Not Started
Some users and applications need social identities with additional layers of validation. How can these be created while preserving privacy and supporting an open system?
Distributed Content Moderation
Status: Not Started
Content moderation can be challenging because it means different things to different people. Make customizing content moderation easier through distributing the work and results.
Namespace and Multichain Support
Status: Not Started
Identity and content from other systems could be used without needing to understand the originating system. Support DSNP across multiple chains to increase user choice and avoid locking data to one chain or network.
Content Bridges
Status: Not Started
Open social network bridges would allow more content and users to be accessible to all via DSNP.
Service-Node Protocols
Status: Not Started
Sub-specifications targeted at ecosystem services to promote interoperability.
Private Group Messaging
Status: Not Started
Secure many-to-many conversations. Group chat style with metadata privacy.
Private Posts to Friends
Status: Not Started
Secure one-to-many posts that still allow for commenting.
Third-Party Identity Bridges
Status: Not Started
Create ways for users to either bring their own identities or connect a DSNP Identity with outside identities.
Archivists
Status: Draft
Long-term storage for announcements and perhaps expanding to content storage.
Peer-to-Peer Publishing
Status: Not Started
Build a peer-to-peer system for announcement publishing for faster routing and message delivery.
DSNP Specification
Version pre-1.2.0
DSNP (Decentralized Social Networking Protocol) is a social networking protocol designed to run on a blockchain. It specifies a set of social primitives along with requirements for interoperability. Go to Implementations for specifics on how DSNP is implemented on a specific blockchain.
Overview
What is a DSNP System?
A DSNP system is a state machine which generates an ongoing, publicly observable and verifiable stream of state change records in response to public input. This specification defines a set of operations (the DSNP Operations) that a compliant DSNP implementation MUST make available. For each DSNP Operation, the DSNP specification defines the data to be provided as input, the data that will be generated as output, and the state change records (the DSNP State Change Records) that will be observable as a result of the Operation, depending on the input and prior state of the system.
The nature of blockchain technology means that all DSNP Operations are potentially asynchronous; that is, DSNP State Change Records need not be created immediately upon invoking a DSNP Operation, and may appear in the stream of state changes at any subsequent time. A DSNP Operation can therefore only be confirmed to have completed successfully by observing the stream of newly-generated DSNP Records.
All DSNP Records MUST be publicly visible. On a blockchain, for example, all nodes must share a common set of Records, and an implementation MUST define how external systems can subscribe to or otherwise view all state changes. In practical terms, this means that all users of a given DSNP system exist conceptually within a single shared social graph, and a given user MUST have the ability at a protocol level to see and participate in all public discourse. Note, however, that this is not an application-level requirement; an application that interacts with DSNP might choose to filter what content is shown.
Finally, to ensure decentralization, a DSNP system MUST avoid having any single point of failure (many nodes) and MUST avoid having any single entity that can override its consensus mechanisms (many operators).
Implementation Compliance
Compliant DSNP implementations MUST document how each of the required DSNP Operations (for a given version of the DSNP specification) can be invoked, including, for example, what wire protocols should be used for discovery, authentication and operation execution, and how inputs and outputs will be serialized.
A compliant implementation MUST specify a mapping from its implementation-specific state change data (for example, the events emitted by a blockchain) to the DSNP State Change Records that data represents.
Prerelease Changelog
Releases
Implementations MUST specify the version(s) of the DSNP specification with which they are compatible.
Version | Description | Release Date | Changelog |
---|---|---|---|
1.1.0 | DIP-148, DIP-149, DIP-150, DIP-165, DIP-180 | 2022-05-06 | Changelog |
1.0.0 | Initial Release | 2021-09-09 | Changelog |
Identity
This specification is intended to cover the concept of identity and how identity is represented within the protocol.
Identifiers
DSNP Identifiers form the basis for pseudo-anonymous streams of content. While some users may choose to link or expose their real-world identity, DSNP implementations MUST NOT require such data exposure for account creation. The social graph is formed using this Identifier so that a user's connections maintain integrity regardless of changes in any user's client choices or access changes.
Pseudo Anonymity
- An Identifier MUST default to being disconnected from a real-world identity.
- An Identifier MUST be unique to the implementation.
Ownership
A user's ownership of their identity is expressed via ownership and control of their pseudo-anonymous Identifier(s). Control entails the power to invoke DSNP Operations including publishing announcements that create, update and delete (tombstone) content associated with the Identifier, delegating these powers to others, and managing keys associated with the Identifier.
Key Management
An initial control key must be created in order to acquire an Identifier. Each distinct Identifier MUST have distinct control keys; that is, the same key MUST NOT be linked to multiple Identifiers. Optionally, an implementation MAY allow the user to add and remove additional keys. An implementation MUST NOT allow the user to remove the only or last remaining control key.
Delegation
- An owner MUST be able to delegate permission to announce on their behalf to other parties.
- A user MUST be able to revoke delegated permissions.
- Announcements from a delegate MUST be able to be verify which delegate made the specific announcement.
- Delegation revocation MUST NOT be retroactive.
Related Operations
Non-normative
Retroactive Revocation of Delegation
There are times when one might desire retroactive revocation of delegation, if for example, a key were found to have been compromised at an earlier time. However, retroactive revocation is much more difficult from a caching and performance perspective. Instead reverting any undesirable Announcements via Tombstones allows a user to choose which specific events need reverting and then notify the network of that change.
To this end, any Batch Publications can always be validated from the perspective of the time they were published, and do not require re-validation at future read times. The validity of a Batch is thus immutable.
- Collect all the DSNP Ids used in a Batch.
- Validate that each DSNP Id had delegated to the publisher of the Batch at the time of publishing.
- Validate that any failures from step 2 were from DSNP Ids that revoked delegation within the acceptance window of prior blocks.
- Record the batch as valid or invalid.
Identifiers
The DSNP Identifiers form the basis for pseudo-anonymous streams of content. Graph connections are formed through the DSNP User Id.
DSNP User Id
- 64-bit Unsigned Integer
- MUST be serialized as decimal
- MUST be unique per implementation
DSNP Content Hash
- MUST be 32 bytes in size
- MUST be a keccak-256 hash of the bytes of the content
- MUST be serialized as hexadecimal
DSNP Protocol Scheme
- MUST always be the string
dsnp://
DSNP User URI
The DSNP User URI consists of two parts: the scheme and the user id. It is used to identify a user via a URI.
Example
dsnp://1311768467294899700
part | value |
---|---|
Scheme | dsnp:// |
User Id | 1311768467294899700 |
DSNP Content URI
The DSNP Content URI consists of three parts: the scheme, the user id, and the content hash. It is used to uniquely identify an announcement from a given user with content.
Any Announcement Types with a fromId
and contentHash
have a DSNP Content URI.
Example
dsnp://78187493520/0x1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef
part | value |
---|---|
Scheme | dsnp:// |
User Id | 78187493520 |
Content Hash | 0x1234567890abcdef0123456789abcdef0123456789abcdef0123456789abcdef |
Graph
This specification describes the social network graph and how it is represented in the protocol.
In this context a social graph means a graph that represents social relations between entities.
The DSNP graph represents nodes as DSNP User Ids.
Public Graph
The public graph is anchored in state transitions recorded in Graph Change Announcements. Playing all state transitions in order will generate the current state of the public graph.
Non-Normative
Private Graph
Implementation coming in the future. See the DSNP Whitepaper for foundational ideas.
Following
A "follow" is the act of publicly following a DSNP User Identity, which results in adding this DSNP User Id to a user's social graph.
An "unfollow" is the act of publicly unfollowing a DSNP User Identity which results in the removal of this DSNP User Id from a user's social graph.
Friendship
There is currently no concept of "friendship" within the DSNP network. Friendship requires a mutual acknowledgment between two different DSNP User Identities. Friendship could be thought of as "mutual following" - where two DSNP User Identities are following each other.
Batch Publications
A Batch Publication is an Apache Parquet file with a collection of Announcements.
Implementation Requirements
Discoverable
Implementations MUST have publicly discoverable Batch Publications.
Validity
Implementations MUST be able to validate Parquet file contents. Validity MUST be immutable.
Historical
Implementations MUST retain proof of existence of a Batch Publication.
Transparent Chain of Delegation
All Announcements in a Batch file MUST be able to be proven to be from or have a chain of delegation to the publisher of the Batch.
File Requirements
Batch files are stored and transferred in Apache Parquet format.
- Batch files MUST match the spec for a single Announcement Type.
- Batch files MUST have Bloom filters set in accordance with the Announcement Type Spec.
- Batch files MUST have NO MORE THAN 128*1024 rows.
Bloom Filter
- A Bloom filter MUST be a Split Block Bloom filter.
- The false-positive rate MUST be 0.001.
Calculation for filter bits is different and is nearly a factor of 10 lower than for a standard Bloom filter: 128*1024 rows with a 0.001 false-positive rate results in around 29,000 bits for a Split Block Bloom filter.
Bloom filters are ONLY added to some fields. See also Announcement Types.
Columns with Bloom Filters
Column | Parquet Type |
---|---|
contentHash | BYTE_ARRAY |
emoji | BYTE_ARRAY |
fromId | BYTE_ARRAY |
inReplyTo | BYTE_ARRAY |
objectId | BYTE_ARRAY |
Non-Normative
Design Requirements
Batch files need to be quickly and easily searchable. Minimal storage size and fast, simple querying are preferred over guarantees of no false positives or advanced data manipulation and column relationships. The files are parseable by client applications, web views, or browsers running pure JavaScript without needing to convert the format.
Applications need to know if a given Batch file has any information of interest without downloading the file first.
Why Parquet?
- Parquet is a column-oriented format. Since DSNP Batch Message data will have a very small column-to-row ratio compared to a typical web application database, it makes sense to prefer a column-oriented format.
- Parquet format has been field tested under extreme network conditions. It has broad support in cloud storage solutions, with libraries in multiple languages.
- Bloom filters are already supported in the Parquet specification, which allows for fast and accurate searching (with caveats for proper configuration).
- Amazon S3 support: We anticipate that some Batch Announcers (and possibly Archivists) will store Batch files on Amazon S3. Amazon Athena also supports storage in Parquet, and its API supports SQL-like queries.
- Parquet also allows references to the same column across files, which could enable multi-file querying in the future.
- Parquet supports compression formats such as Brötli, which is already a browser standard and offers a demonstrated improvement in compression speed and file size over older formats.
- Parquet files can be transferred directly to clients, which can parse the files in the app or browser. No conversion to a serialization format is necessary. This eliminates an entire class of bugs and makes both fetching and querying faster.
- Parquet uses schemas, which additionally reduce file size.
Rejected Alternatives
- Cassandra, RocksDB, CouchDB, MongoDB, and HBASE were rejected since DSNP data needs neither a database for storage nor the overhead of one. Each of these was designed for use cases ranging from somewhat different to drastically different from the DSNP network.
- JSON, BSON, and SQLite, while sometimes used for storage, are intended for serialization. They are schemaless, which results in redundant information and therefore a larger size than formats with schemas. They also don't support Bloom filters; thus, indexing would be required, or new batches would need to be downloaded entirely. The exception is SQLite, which does support more advanced queries. However, SQLite was designed for in-memory storage.
Batch Validity and Order
Batch validity is immutable and is usually based (in part) on the validation of the delegation of authors listed inside the batch to the publisher. Due to the nature of distributed systems, it is possible that a race condition occurs such that a user's delegation revocation presents before a Batch that contains a message from that user via the revoked delegate. While those individual messages should be considered invalid, a window of time for historical testing is suggested before considering the entire batch invalid. This is analogous to the idea of a confirmation time, but only applies to the past rather than the future.
Announcements Overview
Announcements are content or references to content that communicate new user activity to the rest of the network. Announcements are associated with an Identifier that can be validated as the creator of the Announcement. Depending on the implementation, Announcements may be published directly to the network, included in Batch Publication Files, or some combination of those two.
Announcement Validation
There is no guarantee that, at time of creation, a given Announcement will be from the fromId
claimed in the Announcement.
The reader MUST perform a validation of the Announcement at read time to ensure authenticity.
Implementations MUST provide a way to validate that the identifier associated with a given Announcement is authentic.
Announcement Types
Each Announcement has an enumerated type for use when separating out a stream of Announcements.
Value | Name | Description | DSNP Content URI | Tombstone Allowed |
---|---|---|---|---|
0 | Tombstone | an invalidation of previously announced content | no | no |
1 | Graph Change | social graph changes | no | no |
2 | Broadcast | a public post | YES | YES |
3 | Reply | a public response to a Broadcast | YES | YES |
4 | Reaction | a public visual reply to a Broadcast | no | no |
5 | Profile | a profile | YES | no |
6 | Update | an update to content | YES | no |
Duplicate Handling
Due to the nature of asynchronous communication, duplicate Announcements may occur. In the case of duplicates, the first Announcement should be considered the ONLY valid Announcement. Additional duplicate Announcements MUST be rejected or ignored.
Ordering Announcements
- Order Batch Publications by implementation order.
- Order Announcements in a Batch Publication File by row appearance order.
Reverting an Announcement
Announcements may not be deleted, but may be marked invalid by using a Tombstone Announcement, or updated by using an Update Announcement. For example, if a user creates a Reaction Announcement, they may remove that reaction by creating a Tombstone Announcement.
Related Operations
Non-Normative
Duplicate Announcements
Due to the distributed nature of DSNP, duplicate Announcements are possible from time to time. These should be discarded and ignored.
Replay Attacks
Implementations typically restrict replay attacks by testing that the chain transaction sender is authorized (often via delegation) to publish an Announcement.
Announcement Ordering and Activity Content Published Timestamp
Activity Content has a published field that contains a user-generated timestamp. User-generated timestamps cannot be validated, but may be used to indicate ordering other than the network order for Announcements (which are not time dependent.)
Announcement Reference Ordering
Some Announcements contain references to other Announcements via the inReplyTo
field.
Due to the distributed nature of DSNP, the canonical order can have an Announcement that refers to another announcement appearing later in the network order.
For display purposes, these messages should be considered to have occurred after the reference.
DSNP v1.0 Announcement Signatures
In DSNP v1.0, Announcements had individual signatures, producing Batch Publications that were generic and disconnected from the user. Announcements could be submitted to the chain via anyone—not just delegates or users.
In DSNP v1.1, Announcement signatures were removed in favor of the implementation being responsible for the connection between the on-chain signature and the user. Implementations require that the transaction that produces a Batch be performed by the user or delegate directly. This creates batches that are delegate specific, but allows for faster testing of the validity of individual Announcements in a Batch.
For more information see DIP-145.
Tombstone Announcement
A Tombstone Announcement is a way to note that a previously announced content is invalid and the related Announcement should be considered reverted. It is NOT possible to revert a tombstone.
Fields
Field | Description | Data Type | Serialization | Parquet Type | Bloom Filter |
---|---|---|---|---|---|
announcementType | Announcement Type Enum (0 ) | enum | decimal | INT32 | no |
fromId | Id of the user creating the Announcement and the Tombstoned Announcement | 64-bit unsigned integer | decimal | UINT_64 | YES |
targetAnnouncementType | target tombstoned Announcement type | enum | decimal | INT32 | no |
targetContentHash | target contentHash of the original Announcement to tombstone | 32 bytes | hexadecimal | BYTE_ARRAY | YES |
Field Requirements
announcementType
- MUST be fixed to
0
fromId
- MUST be a DSNP User Id
- MUST have authorized the creation of the Announcement, either directly or via a transparent chain of delegation
targetAnnouncementType
- MUST be the Announcement Type of the target Announcement
- MUST ONLY be a Tombstone allowed Announcement Type
Tombstone Allowed Announcement Types
targetContentHash
- MUST match a
contentHash
of previous Announcement with the samefromId
as the Tombstone Announcement
Graph Change Announcement
A Graph Change Announcement is for publishing relationship state changes for a user.
Fields
Field | Description | Data Type | Serialization | Parquet Type | Bloom Filter |
---|---|---|---|---|---|
announcementType | Announcement Type Enum (1 ) | enum | decimal | INT32 | no |
changeType | Type of relationship change | enum | decimal | INT32 | no |
fromId | Id of the user creating the relationship | 64-bit unsigned integer | decimal | UINT_64 | YES |
objectId | Id of the target of the relationship | 64-bit unsigned integer | decimal | UINT_64 | YES |
Field Requirements
announcementType
- MUST be fixed to
1
changeType
- MUST be one of the Change Type Enum
Change Type Enum
Different change types have different meanings.
Value | Name | Description |
---|---|---|
0 | Unfollow | Remove a Follow relationship |
1 | Follow | Create a Follow relationship |
fromId
- MUST be a DSNP User Id
- MUST have authorized the creation of the Announcement, either directly or via a transparent chain of delegation
objectId
- MUST be a DSNP User Id
Non-Normative
Graph Retrieval, Ordering and Reading
Each Graph Change event represents a state transition for the graph. The state of the graph at any time is given by taking the state of the graph at a previous time and applying all Graph Change events not previously applied in the order specified by Announcement Ordering.
Once those Graph Change events are retrieved, they can be ordered to reflect the current graph state (i.e. Charlie has followed Bob, then he unfollowed him, and then followed him again. The graph state reflects that Charlie is following Bob.)
To retrieve the graph, do the following:
- Retrieve the events with announcementType matching the enum for Graph Change.
- Filter the events to a particular DSNP User Id to retrieve information about the respective graph.
- Order the retrieved data by Announcement Ordering.
Broadcast Announcement
A Broadcast Announcement is a way to send a public message to everyone.
Fields
Field | Description | Data Type | Serialization | Parquet Type | Bloom Filter |
---|---|---|---|---|---|
announcementType | Announcement Type Enum (2 ) | enum | decimal | INT32 | no |
contentHash | keccak-256 hash of content stored at URL | 32 bytes | hexadecimal | BYTE_ARRAY | YES |
fromId | id of the user creating the announcement | 64-bit unsigned integer | decimal | UINT_64 | YES |
url | content URL | UTF-8 | UTF-8 | UTF8 | no |
Field Requirements
announcementType
- MUST be fixed to
2
contentHash
- MUST be the keccak-256 hash of the bytes of the reference at the URL
fromId
- MUST be a DSNP User Id
- MUST have authorized the creation of the Announcement, either directly or via a transparent chain of delegation
url
- MUST NOT refer to localhost or any reserved IP addresses as defined in RFC6890
- Resource MUST be one of the supported Activity Content Types
- MUST use one of the supported URL Schemes
Supported URL Schemes
Scheme | Description | Reference | DSNP Version Added |
---|---|---|---|
HTTPS | Hypertext Transfer Protocol Secure | RFC2818 | 1.0 |
Reply Announcement
A Reply Announcement is the same as a Broadcast Announcement,
but includes an inReplyTo
field for noting it as a reply to a given DSNP Content URI.
Fields
Field | Description | Data Type | Serialization | Parquet Type | Bloom Filter |
---|---|---|---|---|---|
announcementType | Announcement Type Enum (3 ) | enum | decimal | INT32 | no |
contentHash | keccak-256 hash of content stored at URL | 32 bytes | hexadecimal | BYTE_ARRAY | YES |
fromId | id of the user creating the Announcement | 64-bit unsigned integer | decimal | UINT_64 | YES |
inReplyTo | Target DSNP Content URI | UTF-8 | UTF-8 | UTF8 | YES |
url | content URL | UTF-8 | UTF-8 | UTF8 | no |
Field Requirements
announcementType
- MUST be fixed to
3
contentHash
- MUST be the keccak-256 hash of the bytes of the reference at the URL
fromId
- MUST be a DSNP User Id
- MUST have authorized the creation of the Announcement, either directly or via a transparent chain of delegation
inReplyTo
- MUST be a DSNP Content URI
url
- MUST NOT refer to localhost or any reserved IP addresses as defined in RFC6890
- Resource MUST be one of the supported Activity Content Types
- MUST use one of the supported URL Schemes
Supported URL Schemes
Scheme | Description | Reference | DSNP Version Added |
---|---|---|---|
HTTPS | Hypertext Transfer Protocol Secure | RFC2818 | 1.0 |
Reaction Announcement
A Reaction Announcement is for publishing emoji reactions to anything with a DSNP Content URI.
Fields
Field | Description | Data Type | Serialization | Parquet Type | Bloom Filter |
---|---|---|---|---|---|
announcementType | Announcement Type Enum (4 ) | enum | decimal | INT32 | no |
emoji | the encoded reaction | UTF-8 | UTF-8 | UTF8 | YES |
apply | how to apply the reaction | 8-bit unsigned integer | decimal | UINT_8 | no |
fromId | id of the user creating the relationship | 64-bit unsigned integer | decimal | UINT_64 | YES |
inReplyTo | Target DSNP Content URI | UTF-8 | UTF-8 | UTF8 | YES |
Field Requirements
announcementType
- MUST be fixed to
4
emoji
- Emoji fields must not be empty
- Emoji fields must consist only of Unicode points from
U+2000
toU+2BFF
, fromU+E000
toU+FFFF
, or fromU+1F000
toU+10FFFF
Examples
All of the following should be considered valid emojis:
"😀", "🤌🏼", "👩🏻🎤", "🧑🏿🏫", "🏳️🌈", "🏳️⚧️", "⚛︎", "🃑", "♻︎"
None of the following should be considered valid:
"F", ":custom-emoji:", "<custom-emoji>", "ᚱ", "ᘐ", "״"
apply
- MUST be an UINT_8
- Indicates whether the emoji should be applied and if so, at what "strength".
Potential uses:
- a single reaction
- ratings
- a range of responses, e.g. "strongly disagree" --> "strongly agree" = 1 --> 5 stars.
- recommendation engines
Apply Enums
Value | Name | Description |
---|---|---|
0 | retract | Remove the referenced emoji |
n | apply | Apply the referenced emoji N times |
fromId
- MUST be a DSNP User Id
- MUST have authorized the creation of the Announcement, either directly or via a transparent chain of delegation
inReplyTo
- MUST be a DSNP Content URI
Non-Normative
Likes
Generic "likes" should default to the "❤️"
or Unicode U+FE0F
as the emoji in the reaction.
Profile Announcement
A Profile Announcement is a constrained version of a Broadcast Announcement. The reference content MUST be of profile type.
Fields
Field | Description | Serialization | Parquet Type | Bloom Filter |
---|---|---|---|---|
announcementType | Announcement Type Enum (5 ) | decimal | INT32 | no |
contentHash | keccak-256 hash of content stored at URL | hexadecimal | BYTE_ARRAY | YES |
fromId | id of the user creating the Announcement | decimal | UINT_64 | YES |
url | profile content URL | UTF-8 | UTF8 | no |
Field Requirements
announcementType
- MUST be fixed to
5
contentHash
- MUST be 32 bytes in length
- MUST be the keccak-256 hash of the bytes of the reference at the URL
fromId
- MUST be a DSNP User Id
- MUST have authorized the creation of the Announcement, either directly or via a transparent chain of delegation
url
- MUST NOT refer to localhost or any reserved IP addresses as defined in RFC6890
- Resource MUST be a valid Profile Activity Content Type
- MUST use one of the supported URL Schemes
Supported URL Schemes
Scheme | Description | Reference | DSNP Version Added |
---|---|---|---|
HTTPS | Hypertext Transfer Protocol Secure | RFC2818 | 1.0 |
Non-Normative
Most Recent Profile
When displaying a DSNP user's profile, the most recent profile should be considered the complete and correct version.
Previous Profile Announcements from the same fromId
may be disregarded.
Update Announcement
An Update Announcement is a way to note intent to update previously announced content. If the original Broadcast/Reply is Tombstoned, subsequent Updates should be ignored.
Fields
Field | Description | Data Type | Serialization | Parquet Type | Bloom Filter |
---|---|---|---|---|---|
announcementType | Announcement Type Enum (6 ) | enum | decimal | INT32 | no |
fromId | id of the user creating the announcement | 64 bit unsigned integer | decimal | UINT_64 | YES |
contentHash | keccak-256 hash of updated content | 32 bytes | hexadecimal | BYTE_ARRAY | YES |
url | updated content URL | UTF-8 | UTF-8 | UTF8 | no |
targetAnnouncementType | target updated Announcement type | enum | decimal | INT32 | no |
targetContentHash | keccak-256 hash of target content | 32 bytes | hexadecimal | BYTE_ARRAY | YES |
Field Requirements
announcementType
- MUST be fixed to
6
fromId
- MUST be a DSNP User Id
contentHash
- MUST be the keccak-256 hash of the bytes of the reference at the url
url
- MUST NOT refer to localhost or any reserved IP addresses as defined in RFC6890
- Resource MUST be one of the supported Activity Content Types
- MUST use one of the supported URL Schemes
targetAnnouncementType
- MUST be the Announcement Type of the target Announcement
- MUST ONLY be an Update allowed Announcement Type
Update Allowed Announcement Types
targetContentHash
- MUST be the
contentHash
of an allowed Announcement type with the samefromId
as the Update Announcement
Operations
DSNP implementations perform well-defined DSNP Operations and generate DSNP State Change Records.
Control Keys and Proofs
Each invocation of a DSNP Operation MUST have verifiable approval of the acting principal(s) via a Control Key Ownership Proof. The precise data type and representation of both the Control Key and the Control Key Ownership Proof MUST be defined by each DSNP implementation. For example, an implementation might use the public key of an asymmetric key pair as a control key, and provide a proof for each operation by producing a cryptographic signature of the user's DSNP Identifier and some nonce value.
Where operations are listed as using control keys or ownership proofs as input parameters, this indicates that these keys or proofs should be provided in addition to those needed for invocation authentication.
Transaction Identifiers
Each invocation of a DSNP Operation should be associated with a Transaction Identifier. Transaction Identifiers are used to associate Operation invocations with asynchronously emitted State Change Records. It MUST be possible to associate a DSNP State Change Record with a Transaction Identifier from a particular DSNP Operation invocation. Transaction Identifiers MUST be unique within an implementation. Transaction Identifiers MUST be serializable as a string.
Failure Handling
Compliant implementations may respond to error conditions either synchronously, as a response to the invocation request, or asynchronously, by emitting a Failure Record.
List of Operations
Operation | Optional? | Principal(s) | Inputs | State Change Record |
---|---|---|---|---|
Create Identifier | no | None | Control Key, Control Key Ownership Proof | Identifier Creation Record |
Define Delegation | no | User AND Delegate | User's Identifier, Delegate's Identifier, Set of Allowed Announcement Types | Delegation Definition Record |
Revoke Delegation | no | User OR Delegate | User's Identifier, Delegate's Identifier | Delegation Revocation Record |
Add Control Key | YES | User | Key, Key Ownership Proof | Control Key Addition Record |
Remove Control Key | YES | User | Key | Control Key Removal Record |
Publish Announcement | no* | User OR Delegate | Announcement | Announcement Published Record |
Publish Batch | no* | User OR Delegate | Announcement Type, Batch Publication URL, Batch Publication Content Hash | Batch Published Record |
* For each Announcement Type, an implementation may support one or both of these operations. Implementations MUST document which of the operations is available for each Announcement Type.
State Change Records
State Change Records constitute the observable output of a DSNP system. Implementations MUST specify how applications can translate implementation-specific output into State Change Records.
Each Record is associated with a Transaction Identifier, which allows Operations to be asynchronously associated with their results.
Records consists of one or more fields.
Record Type | Field | Field Data Type |
---|---|---|
Identifier Creation Record | Control Key | Implementation dependent |
Control Key Ownership Proof | Implementation dependent | |
Delegation Definition Record | User Id | DSNP User Id |
Delegate Id | DSNP User Id | |
Allowed Announcement Types | List of enum values | |
Delegation Revocation Record | User Id | DSNP User Id |
Delegate Id | DSNP User Id | |
Control Key Addition Record | User Id | DSNP User Id |
Key | Implementation dependent | |
Key Ownership Proof | Implementation dependent | |
Control Key Removal Record | User Id | DSNP User Id |
Key | Implementation dependent | |
Announcement Published Record | Announcement | One of the types described in Announcements |
Batch Published Record | From Id | DSNP User Id |
Announcement Type | Enum value | |
URL | String | |
Content Hash | DSNP Content Hash | |
Failure Record | Message | String |
Serializations
Serialization is how the value should be stringified for signing and for transfer between systems. Most serializations use outside standards, but some requiring additional clarifications are provided here.
decimal
Used to represent integers. Strings are used to avoid issues with different implementations of numbers.
- MUST use 0-9 representation
- MUST NOT have spaces or separators
- MUST be a string
Invalid | Why | Valid |
---|---|---|
0x123 | Must be decimal | "291" |
291 | Must be a string | "291" |
291n | BigInt(291) serialization appends an n | "291" |
hexadecimal
Used to represent bytes.
- MUST use 0-9 and a-f representation
- MUST be lowercase
- MUST be prefixed with a
0x
- MUST NOT have spaces or separators
- MUST have two characters per byte in addition to the
0x
characters
Bytes | Invalid | Valid |
---|---|---|
2 | 0x123 | 0x0123 |
2 | 123h | 0x0123 |
2 | 0x0ABC | 0x0abc |
8 | 0xabc | 0x0000000000000abc |
32 | 0x3e34c4325f4461b9355027b314f3eb56d31af549f7da7bd9ef1ce951651e | 0x00003e34c4325f4461b9355027b314f3eb56d31af549f7da7bd9ef1ce951651e |
DSNP Implementations
- Frequency (Official)
- Ethereum/EVM Compatible (Deprecated)
Frequency DSNP Implementation (Pre-Release)
Version pre-1.0.0
Final release is pending release of Frequency v1.0.0
DSNP on Frequency is built using Message Source Accounts (MSAs) for identity and delegation with Schemas defined for each Announcement Type. Frequency uses Schemas to define how to create and pass messages.
Links
Schemas
Official DSNP Schemas may be found in GitHub.
Libraries
Name | Language(s) |
---|---|
TODO | JavaScript/TypeScript |
Releases
Version | Description | DSNP Compatibility | Release Date | Changelog |
---|---|---|---|---|
pre-1.0.0 | Pre-Release | 1.1.x | 2022-09-19 | N/A |
Identity
Purpose
- Specify how DSNP Identifiers map to Frequency.
- Specify how the DSNP delegation maps to Frequency.
- Detail how Frequency matches the ownership requirements of DSNP.
Details
DSNP uses Message Source Accounts (MSAs) that map directly to a DSNP Identity.
Identifier
MSAs each receive a unique unsigned 64-bit identifier that is used as the DSNP Id. These MSAs are pseudo-anonymous identifiers that are associated with one or more public keys. MSAs can be created by the user or on behalf of the user using a signed authorization.
Creating a DSNP User Id
- Generate a compatible key pair in your wallet.
- Create a Message Source Account (MSA) on Frequency using one of the
create
extrinsics. - Wait for the transaction to process.
Retrieving a DSNP User Id from a Public Key
- Use the Frequency state query
msa.messageSourceIdOf
to retrieve the MSA Id from the public key generated in step 1. - The MSA Id is the DSNP Id and can be used to generate the DSNP User URI.
Ownership
Frequency has a strong ownership model for MSAs. Ownership of an MSA is controlled by the associated keys. MSAs are the source for Messages either directly or via delegation.
Delegation
Frequency allows for delegation to others called Providers. Delegation comes with permissions that grant publishing of specific Schemas to a provider. Since each Announcement Type has a set Schema Id, a user may permission specific Announcement Types.
When a user delegates to a Provider, that delegation is either validated on chain or can be validated off chain using the msa_checkDelegations
RPC call.
Users can revoke a delegation at anytime without any fees, and revocation is locked to the point the transaction is included in a block.
Announcement Publishing
On Frequency, Announcements are mapped to Schemas which in turn publish Frequency Messages. Frequency Messages are either direct Graph Changes from a particular user, or a Batch Publication with a multitude of possible users.
Announcement Type Enum | Announcement | Batched | Schema Id Mainnet | Schema Id Rococo | Frequency Model Type | Frequency Payload Location |
---|---|---|---|---|---|---|
0 | Tombstone | YES | TBD | 1 (v1.1.0+) | Parquet | IPFS |
1 | Graph Change | no | TBD | 7 (v1.1.0+) | AvroBinary | OnChain |
2 | Broadcast | YES | TBD | 2 (v1.1.0+) | Parquet | IPFS |
3 | Reply | YES | TBD | 3 (v1.1.0+) | Parquet | IPFS |
4 | Reaction | YES | TBD | 4 (v1.1.0+) | Parquet | IPFS |
5 | Profile | YES | TBD | 5 (v1.1.0+) | Parquet | IPFS |
6 | Update | YES | TBD | 6 (v1.2.0+) | Parquet | IPFS |
Source code for each schema is also located in the LibertyDSNP/schemas repository.
DSNP Non-Batched Announcements
Frequency uses an on-chain data structure for storage of GraphChange
Announcements.
Each individual announcement is submitted using the add_onchain_message
extrinsic.
DSNP Batched Announcements
Frequency uses DSNP Batch Publications for Announcements that are batched. The Parquet file is stored on IPFS, but it is discovered through the Frequency Message.
Frequency Messages maintain the metadata of where and when the Batch Publication was published and which Provider MSA published it. It is the publisher's responsibility to maintain the IPFS pin so that the batch file is continuously available.
DSNP Batch Publications MUST be validated upon fetching to ensure data and permission integrity.
Ordering
Frequency Messages are well ordered within a Schema.
- Frequency: Block number ascending
- Frequency: Block index ascending
- DSNP Standard: Order Announcements in a Batch Publication File by row appearance order.
Ordering Across Schemas
Frequency provides complete ordering metadata for Messages across Schemas. Block index is unique per Message within the same block.
Human Order
Due to the asynchronous nature of networks and batching, it is possible that the canonical ordering of Announcements is wrong from a human viewpoint. With dependent Announcements, where one Announcement refers to another Announcement, the order may be inferred differently than the canonical ordering. It is left to user interfaces to handle these situations.
Retrieval
Frequency nodes provide an RPC interface get_messages_by_schema
with paginated responses that differ based on the Schema.
Frequency nodes can provide a websocket interface that will emit an event for each block that has one or more messages of a given schema in that block.
The MessagesStored
event can be used to know when to call the RPC interface to retrieve the messages.
See the Frequency Documentation for more details on Message retrieval.
Announcement Validation
DSNP Announcements are validated differently depending on the type of Announcement. Batched Announcements are off chain and MUST be validated at read time. Non-batched Announcements (e.g. Graph Change) are on chain, are validated at write time, and do not need to be re-validated at read time.
On-chain Announcements
Creation of an Announcement as an on-chain Frequency Message requires that the delegation be validated before adding the message to the chain. Thus, any Graph Change Announcements read from a trusted node, can be trusted to have been made by the DSNP identity or a delegate of the identity at the time the Frequency Message was sent.
On-Chain Announcement Types
- Graph Change
Batch Publication Announcements
DSNP Announcements that are in a Batch Publication MUST be validated at read time.
Batched Announcement Types
- Tombstone
- Broadcast
- Reply
- Reaction
- Profile
Batch Publication Frequency Message Validation
The Frequency Message for a Batch Publication has several important fields for validation:
Field | Description |
---|---|
provider_msa_id | MSA Id of the provider sending the message |
cid | The Content IDentifier v1 for IPFS content |
payload_length | Expected length of the content from IPFS |
block_number | Block number that the message was recorded on the chain. |
File Validation
- Retrieve the file from the IPFS network using the
cid
. - Verify the file hash by comparing it to the hash included in the
cid
. (Required for non-trusted IPFS nodes.) - Verify that the byte length of the retrieved file matches the
payload_length
.
Publication Announcements Validation
- Collect the unique set of
fromId
values. - Use the Custom RPC
msa_checkDelegations
with thefromId
values as thedelegator_msa_ids
and theprovider_msa_id
at theblock_number
. - The
fromId
values thatmsa_checkDelegations
verifies as having a delegation atblock_number
are valid Announcements.
Announcement Duplicates
Duplicate Announcements MUST be rejected or ignored.
Ethereum/EVM Compatible DSNP Implementation
Version 1.1.0
Deprecation Warning: EVM implementation is deprecated in favor of the Frequency Implementation.
DSNP on Ethereum is designed using smart contracts and log messages. Smart contracts are used for identity and delegation. Log messages are used for announcement publishing via batches.
Solidity
All included Solidity interfaces are targeting the Solidity language version 0.8.x. Other versions may be available in the official contracts code repository.
Contracts and Interfaces
Official DSNP interfaces, contracts, and deployment information may be found in GitHub.
Libraries
Name | Language(s) |
---|---|
LibertyDSNP/eth-sdk-ts | JavaScript/TypeScript |
Releases
Version | Description | DSNP Compatibility | Release Date | Changelog |
---|---|---|---|---|
1.1.0 | DIP-150, DIP-180 | 1.0.x, 1.1.x | 2022-05-06 | Changelog |
1.0.0 | Initial Release | 1.0.x, 1.1.x | 2021-09-09 | Changelog |
Identity
Purpose
- Provide the interface for a DSNP Identity.
- Specify the delegation model and related interface.
- Specify the ownership model and related interface.
- Provide the list of EIPs that must be supported by a DSNP-compatible identity.
Details
Ownership
Ownership of an identity on Ethereum is determined by the address of the signer of the content.
Creation of any new identity MUST be authorized by the owner's address. The official Identity Factory is provided for initial creation of a new identity.
Creation of an identity on behalf of someone is described in the Identity Factory.
Permissioned Owners
Ownership is managed through using permissions. While at least one owner is required, additional public keys may be considered to have ownership and may remove or add other owners.
Delegation
Delegation allows adding additional public keys, to the owner public key(s). These delegated keys are allowed to perform certain actions on behalf of the owner based on the delegate's "role".
Interface
/**
* @dev DSNP Identity Interface for managing delegates
*/
interface IDelegation {
struct DelegateAdd {
uint32 nonce;
address delegateAddr;
Role role;
}
struct DelegateRemove {
uint32 nonce;
address delegateAddr;
}
/**
* @dev Enumerated Permissions
* Roles have different permissions
* APPEND ONLY
*/
enum Permission {
/**
* @dev 0x0 NONE reserved for no permissions
*/
NONE,
/**
* @dev 0x1 Announce any DSNP message
*/
ANNOUNCE,
/**
* @dev 0x2 Add new delegate
*/
OWNERSHIP_TRANSFER,
/**
* @dev 0x3 Add new delegates
*/
DELEGATE_ADD,
/**
* @dev 0x4 Remove delegates
*/
DELEGATE_REMOVE
}
/**
* @dev Enumerated Roles
* Roles have different permissions
* APPEND ONLY
*/
enum Role {
/**
* @dev 0x0 NONE reserved for no permissions
*/
NONE,
/**
* @dev 0x1 OWNER:
* - Permission.*
*/
OWNER,
/**
* @dev 0x2 ANNOUNCER:
* - Permission.ANNOUNCE
*/
ANNOUNCER
}
/**
* @dev Log for addition of a new delegate
* @param delegate Address delegated
* @param role Permission Role
*/
event DSNPAddDelegate(address delegate, Role role);
/**
* @dev Log for removal of a delegate
* @param delegate Address revoked
*/
event DSNPRemoveDelegate(address delegate);
/**
* @dev Add or change permissions for delegate
* @param newDelegate Address to delegate new permissions to
* @param role Role for the delegate
*
* MUST be called by owner or other delegate with permissions
* MUST consider newDelegate to be valid from the beginning to time
* MUST emit DSNPAddDelegate
*/
function delegate(address newDelegate, Role role) external;
/**
* @dev Add or change permissions for delegate by EIP-712 signature
* @param v EIP-155 calculated Signature v value
* @param r ECDSA Signature r value
* @param s ECDSA Signature s value
* @param change Change data containing new delegate address, role, and nonce
*
* MUST be signed by owner or other delegate with permissions (implementation specific)
* MUST consider newDelegate to be valid from the beginning to time
* MUST emit DSNPAddDelegate
*/
function delegateByEIP712Sig(
uint8 v,
bytes32 r,
bytes32 s,
DelegateAdd calldata change
) external;
/**
* @dev Remove Delegate
* @param addr Address to remove all permissions from
*
* MUST be called by the delegate, owner, or other delegate with permissions
* MUST store the block.number as the endBlock for response in isAuthorizedToAnnounce (exclusive)
*
* MUST emit DSNPRemoveDelegate
*/
function delegateRemove(address addr) external;
/**
* @dev Remove Delegate By EIP-712 Signature
* @param v EIP-155 calculated Signature v value
* @param r ECDSA Signature r value
* @param s ECDSA Signature s value
* @param change Change data containing new delegate address and nonce
*
* MUST be signed by the delegate, owner, or other delegate with permissions
* MUST store the block.number as the endBlock for response in isAuthorizedToAnnounce (exclusive)
* MUST emit DSNPRemoveDelegate
*/
function delegateRemoveByEIP712Sig(
uint8 v,
bytes32 r,
bytes32 s,
DelegateRemove calldata change
) external;
/**
* @dev Checks to see if address is authorized with the given permission
* @param addr Address that is used to test
* @param permission Level of permission check. See Permission for details
* @param blockNumber Check for authorization at a particular block number, 0x0 reserved for endless permissions
* @return boolean
*
* @dev Immutable assuming operating on confirmed blocks
*/
function isAuthorizedTo(
address addr,
Permission permission,
uint256 blockNumber
) external view returns (bool);
/**
* @dev Get a delegate's nonce
* @param addr The delegate's address to get the nonce for
*
* @return nonce value for delegate
*/
function getNonceForDelegate(address addr) external view returns (uint32);
}
Upgrade Interface
It is an open question whether a standard set of upgrade interfaces should be required.
Additional Required Interfaces
EIP 165
EIP 165 provides standard interface detection. It is required to support optional interfaces and upgrade expansion.
Additional Optional Interfaces
EIP 173
EIP 173 provides methods that confirm ownership and provide methods to transfer ownership.
Implementations that choose to use this interface will need to consider that transferring ownership should revoke all existing delegations, or at a minimum all delegates at Role.OWNER
.
EIP 897
EIP 897 is for DSNP identity contracts that are proxies such as those produced by the default Identity Factory.
EIP 1271
EIP 1271 allows the identity contract to validate a signature. To support delegation, this interface allows a contract to validate against the signature of the owner.
Unlike IDelegation.isAuthorizedToAnnounce
which supports permission levels and end block number, isValidSignature
will only respond to currently permissioned addresses at the owner level.
REMEMBER: Implementation MUST ONLY return true for active owner level.
Identity Requirements
Interface | Required |
---|---|
IDelegation | Required |
EIP 165 | Required |
EIP 897 | Proxy Contracts Only |
EIP 173 | Optional |
EIP 1271 | Optional |
Rejected Solutions
- Centralized smart contract maintaining identifiers and owners
- Top Rejection Reasons:
- A centralized contract must either be controlled by the foundation, a DAO, or be non-upgradable.
- Does not allow for complete flexibility in ownership.
- Top Rejection Reasons:
- Using a single public key as an identifier
- Top Rejection Reasons:
- Does not allow for flexibility in ownership.
- Completely dependent on off-chain software and processing for verification of delegation.
- Top Rejection Reasons:
- Using a single owner model
- A single owner (as well as the existing EIP 173 to manage it) was considered as opposed to the multi-owner role system.
- Top Rejection Reasons:
- Does not allow for flexibility in ownership.
- Single-owner increases the likelihood of users using choosing less secure key management practices.
- Multi-owner pairs better with a permissioned system.
- Using a simple two-tier permission model
- Owner level and "everything" else
- Top Rejection Reasons:
- Limits options for future specific roles such as social recovery.
- Cost savings were minimal when paired with a role-based system.
Identifiers
The identity registry is the source of building and maintaining a unique DSNP User Id.
Creating and Retrieving a DSNP User Id
- Create an entry in the Identity Registry contract.
- Use the
DSNPRegistryUpdate
log event or theresolveRegistration
function to retrieve the uint64id
- That
id
value is the DSNP User Id, and can be used to generate the DSNP User URI.
Identity Factory
The least expensive way to create a new identity is through an identity factory. Official contracts will provide one or more of these standard interfaces to easily generate an identity with different upgrade paths.
Remember: Using a factory or even a proxy is just an optimization and is NOT required. Any contract that matches the DSNP Identity interfaces is valid.
Purpose
- Describe how an Identity Factory can create an identity.
- Describe how an Identity Factory can allow someone else to pay for the creation of an identity.
- Restrict the creation of identities without owner permission.
Assumptions
- All assumptions from DSNP Identity
Proxy Contracts
While it is not required, most of the DSNP Identity compatible contracts are proxy contracts. Proxy contracts are often created through a factory contract. Here are the interfaces to be a DSNP-compatible identity factory.
What is a Proxy Contract?
Proxy contracts are used to limit the gas for deploying multiple contracts that all have the same logic, but require different states. The state is maintained at the "proxy" contract while the logic to alter the state may exist in a single "logic" contract.
Remember: A Logic Contract has 100% control over the state of a smart contract. While a logic contract cannot have state that effects the execution of a proxy contract, a logic contract's code can be written in such a way that allows for others to take control of a contract. One should never use logic contracts that they do not trust.
What are the different types of Proxy Contracts?
OpenZeppelin has a great set of standard and audited proxy contracts.
While there may not be an identity factory interface for each type, the documentation from OpenZeppelin gives good detail on the differences between the types.
Can I switch from one type to another?
Switching types is possible, but difficult. See Identity Registry for more information on changing an identity contract to another type.
Data storage and EIP 1967
Due to the state management system that Ethereum uses, it can easily cause issues for upgradable contracts. EIP 1967 provides for ways to safely use state that will not collide. Implementations of upgradable proxies MUST use EIP 1967 style data storage.
Logic Contract Constraints
Contracts that are used as the logic for the proxy cannot use constructors for initialization. Proxy contracts, however, can have constructors. Additionally the factory can be used to call methods once the proxy is created. Remember that setting up the initial authorization state of a contract MUST be done in a single transaction to prevent others from sniping the contract.
Factory
An identity factory will give easy methods that allow for the creation of proxy contracts that function as DSNP Identities. Official implementation contract addresses will be published once deployed.
Clone Interface
Clones follow EIP 1167 for a non-upgradable identity contract.
/**
* @dev DSNP Identity Factory Interface for creating identities via [EIP 1167](https://eips.ethereum.org/EIPS/eip-1167)
*/
interface IIdentityCloneFactory {
/**
* @dev event to log the created proxy contract address
*/
event ProxyCreated(address addr);
/**
* @dev Creates a new identity with the message sender as the owner
* @dev [EIP 1167](https://eips.ethereum.org/EIPS/eip-1167) Proxy
* @param logic The address to use for the logic contract
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @return The address of the newly created proxy contract
*/
function createCloneProxy(address logic) public returns (address);
/**
* @dev Creates a new identity with the ecrecover address as the owner
* @dev [EIP 1167](https://eips.ethereum.org/EIPS/eip-1167) Proxy
* @param logic The address to use for the logic contract
* @param owner The initial owner's address of the new contract
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @return The address of the newly created proxy contract
*/
function createCloneProxyWithOwner(address logic, address owner) external returns (address);
}
Upgradable Proxy Interface
Upgradable Proxies can be upgraded by the owner or permissioned delegates.
/**
* @dev DSNP Identity Factory Interface for creating upgradable identities
*/
interface IIdentityUpgradableFactory {
/**
* @dev event to log the created proxy contract address
*/
event ProxyCreated(address addr);
/**
* @dev Logs updates to the suggested logic contract
* @dev MUST BE emitted when the contract changes the suggested logic address
* @param newLogic The new address
*/
event LogicUpdated(address newLogic);
/**
* @dev This may be upgradable by the owner of the factory
*
* @return The current logic contract suggested by this factory
*/
function getLogic() external view returns (address);
/**
* @dev Creates a new identity with the message sender as the owner
* and will be pointed at the default logic address.
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @return The address of the newly created proxy contract
*/
function createUpgradableProxy() external returns (address);
/**
* @dev Creates a new identity with the message sender as the owner
* @param logic The address to use for the logic contract
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @return The address of the newly created proxy contract
*/
function createUpgradableProxy(address logic) external returns (address);
/**
* @dev Creates a new identity with the ecrecover address as the owner
* @param logic The logic address to use for identity creation
* @param owner The initial owner's address of the new contract
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @return The address of the newly created proxy contract
*/
function createUpgradableProxyWithOwner(address logic, address owner) external returns (address);
}
Beacon Factory Interface
Beacon Proxies will use the beacon's logic address and will be upgraded when the beacon's logic address is changed. This is the suggested factory for use to remain up to date.
/**
* @dev DSNP Identity Factory Interface for creating beacon following identities
*/
interface IIdentityBeaconFactory {
/**
* @dev event to log the created proxy contract address
*/
event ProxyCreated(address addr);
/**
* @dev This MUST NOT be upgradable by the owner of the factory
*
* @return The current beacon contract suggested by this factory
*/
function getBeacon() external view returns (address);
/**
* @dev Creates a new identity with the message sender as the owner
* Uses the beacon defined by getBeacon()
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @return The address of the newly created proxy contract
*/
function createBeaconProxy() external returns (address);
/**
* @dev Creates a new identity with the message sender as the owner
* @param beacon The beacon address to use for logic contract resolution
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @return The address of the newly created proxy contract
*/
function createBeaconProxy(address beacon) external returns (address);
/**
* @dev Creates a new identity with the ecrecover address as the owner
* @param beacon The beacon address to use logic contract resolution
* @param owner The initial owner's address of the new contract
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @return The address of the newly created proxy contract
*/
function createBeaconProxyWithOwner(address beacon, address owner) external returns (address);
/**
* @dev Creates a new identity with the address as the owner and registers it with a handle
* @param beacon The beacon address to use logic contract resolution
* @param owner The initial owner's address of the new contract
* @param handle The handle the new identity proxy under which should be registered
*
* @dev This MUST emit ProxyCreated with the address of the new proxy contract
* @dev This MUST revert if registration reverts
* @dev This MUST emit a DSNPRegistryUpdate
*/
function createAndRegisterBeaconProxy(
address beacon,
address owner,
string calldata handle
) external;
}
Beacon Interface
A beacon contract follows the same interface as the OpenZeppelin 4 IBeacon
.
Updating the beacon logic address is left to the implementation of the beacon, however the OpenZeppelin 4 UpgradeableBeacon
is suggested.
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
* This follows the interface from OpenZeppelin 4.0.0 [IBeacon](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.0.0/contracts/proxy/beacon/IBeacon.sol)
*
* @return A contract address that implements the logic for the proxy
*/
function implementation() external view returns (address);
}
Identity Registry
A registry allows for distinct user identifiers and human-readable discovery of network members. The DSNP User Id Registry is a simple contract that allows switching handles, identity contracts, and chain migration while maintaining all graph connections, both public and private.
Purpose
- Describes how the Identity Registry resolves a DSNP User Id to an identity contract address.
- Describes how the Identity Registry allows for handle resolution.
- Presents the interface for the Identity Registry.
- Describes rejected alternatives.
Assumptions
- Ids will need to be moved from beta test to Mainnet.
- Handles are for display and discovery purposes only.
Discovery via DSNP Handles
- Contract addresses or numerical ids are not easy to remember.
- Most networks rely on text based handles for discovery of users on a network.
- DSNP handles are an easy way to allow user lookup.
Handles
- Handles are simple UTF-8 strings.
- No limitations are placed on length or contents, although some clients may not have support for the full unicode set.
- A user MAY NOT register multiple handles that point to the same DSNP User Id.
- Handles must be unique.
Homograph Attack Mitigation
UTF-8 support for handles opens handle users up to homograph attacks, not to mention case-sensitivity issues. Discussion is ongoing both for the DSNP as well as for ICANN domain names and other projects working with internationalization support.
Because the DSNP User Id is stable, attacks would only be successful in cases where the DSNP User Id were unknown. Punycode is used by some software to prevent homographs by encoding all non-Latin characters into Latin characters. However, this means Punycode does not properly present non-Latin characters and thus fails to reach the level of internationalization support desired by the DSNP.
Current Mitigation Strategies
Clients resolving handles MUST implement a method to detect potential homographs, check both user settings, and potentially check the registry for additional potential matching DSNP User Ids.
EIP 712 Methods, Replay Attacks and Nonces
The Registry supports EIP 712 methods to permit a second party to pay gas costs for address and handle changes. Once an EIP 712 transaction is made, anyone may replay that action without further authorization. This breaks the security guarantees of DSNP when the registration owner has made additional changes to either address or handle. To mitigate this, the Registry contract MUST store a nonce for every registration. When it receives an EIP 712 transaction, it MUST check that the nonce parameter matches the stored nonce, and it MUST increment the stored nonce if the transaction succeeds.
If a handle is changed, the registry MUST preserve the stored nonce for the old handle.
DSNP User Ids
Ethereum contract addresses are currently 160-bit values which are much larger than needed for unique identification. Identification can be reduced to 64-bit identifiers with the registry and enable contract changing.
Remember: Only DSNP User Ids are safe for long term data connections.
- DSNP Identity contract addresses are not guaranteed long term for upgrade and migration reasons.
- Handles can change.
Resolutions
Resolutions are possible between any of the three pieces of data: Handle, DSNP User Id, and Contract Address. While a utility method is provided for ease of moving from handle to the contract address, the other resolutions require using contract log events.
Current Handle -> Current DSNP User Id and Contract Address
IRegistry.resolveRegistration
provides easy access to the current registration for the given handle.
Current Handle -> Nonce
IRegistry.resolveHandleToNonce
is the only method for discovery of the next nonce to use.
Other Lookups & Historical Values
The DSNPRegistryUpdate
event is provided to resolve DSNP User Ids, handles, and contract addresses.
The DSNP User Id and contract address are indexed in the event, and thus utilize a log search using the event and the search data as topics.
A search by contract address may produce more than one result--meaning that the contract address is currently or previously attached to other DSNP User Ids.
To test for the current value, the query would need to be run again with each of the resulting DSNP User Ids retrieving the most recent DSNPRegistryUpdate
event.
A search by DSNP User Id will retrieve the history of all handles and contract addresses that have been connected to that DSNP User Id. The most recent event (the one with the highest block number) will give the current handle and contract address for the given DSNP User Id.
Handles may be reused if a DSNP User Id changes to a new handle.
While time consuming, discovering previous owners of a given handle requires locally filtering all DSNPRegistryUpdate
events for events with the given handle.
EIP 721
DSNP support for the NFT standard EIP 721 for handles is under consideration.
NFT Concerns
- The EIP 721 standard has its own ownership and permission system that is too limited for use across the DSNP.
- Supporting two ownership systems adds needless complexity.
- Identity contract ownership would require additional complexity to receive and transfer 721 tokens.
Rejected Alternatives
- Why not the Ethereum Name Service (ENS)?
- Overly complex and expensive for the simple use case of handles.
- Support can be added later through custom ENS resolution.
- Doesn't provide a numerical id for efficient graph storage.
- Still subject to homograph attacks.
Contract Interface
/**
* @dev DSNP Registry Interface
* @dev Suggested data storage implementation:
* uint64 internal currentIdSequenceMarker = 0x1; // Must not start at 0
* mapping(string => [id, address]) internal handleToIdAndAddress;
*/
interface IRegistry {
struct AddressChange {
uint32 nonce;
address addr;
string handle;
}
struct HandleChange {
uint32 nonce;
string oldHandle;
string newHandle;
}
/**
* @dev Log when a resolution address is changed
* @param id The DSNP User Id
* @param addr The address the DSNP User Id is pointing at
* @param handle The actual UTF-8 string used for the handle
*/
event DSNPRegistryUpdate(uint64 indexed id, address indexed addr, string handle);
/**
* @dev Register a new DSNP User Id
* @param addr Address for the new DSNP User Id to point at
* @param handle The handle for discovery
*
* MUST reject if the handle is already in use
* MUST emit DSNPRegistryUpdate
* MUST check that addr implements IDelegation interface
* @return id for new registration
*/
function register(address addr, string calldata handle) external returns (uint64);
/**
* @dev Alter a DSNP User Id resolution address
* @param newAddr Original or new address to resolve to
* @param handle The handle to modify
*
* MUST be called by someone who is authorized on the contract
* via `IDelegation(oldAddr).isAuthorizedTo(oldAddr, Permission.OWNERSHIP_TRANSFER, block.number)`
* MUST emit DSNPRegistryUpdate
* MUST check that newAddr implements IDelegation interface
*/
function changeAddress(address newAddr, string calldata handle) external;
/**
* @dev Alter a DSNP User Id resolution address by EIP-712 Signature
* @param v EIP-155 calculated Signature v value
* @param r ECDSA Signature r value
* @param s ECDSA Signature s value
* @param change Change data containing nonce, new address and handle
*
* MUST be signed by someone who is authorized on the contract
* via `IDelegation(oldAddr).isAuthorizedTo(ecrecovedAddr, Permission.OWNERSHIP_TRANSFER, block.number)`
* MUST check that newAddr implements IDelegation interface
* MUST emit DSNPRegistryUpdate
*/
function changeAddressByEIP712Sig(uint8 v, bytes32 r, bytes32 s, AddressChange calldata change) external;
/**
* @dev Alter a DSNP User Id handle
* @param oldHandle The previous handle for modification
* @param newHandle The new handle to use for discovery
*
* MUST NOT allow a registration of a handle that is already in use
* MUST be called by someone who is authorized on the contract
* via `IDelegation(oldHandle -> addr).isAuthorizedTo(ecrecovedAddr, Permission.OWNERSHIP_TRANSFER, block.number)`
* MUST emit DSNPRegistryUpdate
*/
function changeHandle(string calldata oldHandle, string calldata newHandle) external;
/**
* @dev Alter a DSNP User Id handle by EIP-712 Signature
* @param v EIP-155 calculated Signature v value
* @param r ECDSA Signature r value
* @param s ECDSA Signature s value
* @param change Change data containing nonce, old handle and new handle
*
* MUST NOT allow a registration of a handle that is already in use
* MUST be signed by someone who is authorized on the contract
* via `IDelegation(handle -> addr).isAuthorizedTo(ecrecovedAddr, Permission.OWNERSHIP_TRANSFER, block.number)`
* MUST emit DSNPRegistryUpdate
*/
function changeHandleByEIP712Sig(uint8 v, bytes32 r, bytes32 s, HandleChange calldata change) external;
/**
* @dev Resolve a handle to a DSNP User Id and contract address
* @param handle The handle to resolve
*
* Returns zeros if not found
* @return A tuple of the DSNP User Id and the Address of the contract
*/
function resolveRegistration(string calldata handle) view external returns (uint64, address);
/**
* @dev Resolve a handle to a EIP 712 nonce
* @param handle The handle to resolve
*
* rejects if not found
* @return expected nonce for next EIP 712 update
*/
function resolveHandleToNonce(string calldata handle) view external returns (uint32);
}
Announcement Publishing
On Ethereum, all Announcements are published via Batch Publication Files. Publishing is accomplished via an Ethereum Log Event.
Ethereum Log Event
The event topic for DSNPBatchPublication
follows the standard Solidity event name to hash standard.
0xe63a4904ccacc079f71e52aad2cf99c00a7d4963566562a94d7c07610f1df576 = keccak-256("DSNPBatchPublication(int16,bytes32,string)")
Log Event Data
Field | Description | Type | Indexed |
---|---|---|---|
announcementType | The single announcement type in the given file | int16 | YES |
fileHash | keccak-256 hash of the batch file | bytes32 | no |
fileUrl | URL to retrieve the referenced batch file via an approved schema | string | no |
Batch File Retrieval
- Batch File URLs MUST NOT refer to localhost or any reserved IP addresses as defined in RFC6890.
- Batch File URLs MUST use one of the supported URL Schemes.
Supported URL Schemes
Scheme | Description | Reference | DSNP Version Added |
---|---|---|---|
HTTPS | Hypertext Transfer Protocol Secure | RFC2818 | 1.0 |
Ordering
The DSNPBatchPublication
Ethereum events are ordered by information provided in the transaction.
DSNPBatchPublication
Block number ascendingDSNPBatchPublication
Transaction index ascendingDSNPBatchPublication
Log index ascending- DSNP Standard: Order Announcements in a Batch Publication File by row appearance order
Publisher Contract Requirements
Contracts that allow for generating DSNPBatchPublication
are called publishers.
A standard interface is available for use.
IPublish
Interface
interface IPublish {
struct Publication {
int16 announcementType;
string fileUrl;
bytes32 fileHash;
}
/**
* @dev Log Event for each batch published
* @param announcementType The type of Announcement in the batch file
* @param fileHash The keccak hash of the batch file
* @param fileUrl A url that resolves to the batch file
*/
event DSNPBatchPublication(int16 indexed announcementType, bytes32 fileHash, string fileUrl);
/**
* @param publications Array of Batch struct to publish
*/
function publish(Publication[] calldata publications) external;
}
Announcement Validation
DSNP Announcements on Ethereum are validated at read time.
- Read
DSNPBatchPublication
events. - Fetch and validate the Batch Publication.
- Validate the chain of delegation from the Announcement's
fromId
to the sender of the transaction with theDSNPBatchPublication
event.
Batch Publication Validation
- Hash the batch publication file using keccak-256.
- Retrieve the published hash for the given file from the
DSNPBatchPublication
Event. - The file hash MUST match the retrieved hash from the
DSNPBatchPublication
event.
Announcement Validation
- Get the Ethereum address of the sender for the transaction from the Batch (aka the Batch Publisher).
- Find the Identity Contract for the Announcement's
fromId
. - Test the Batch Publisher's Ethereum address against the Identity Contract via
IDelegation.isAuthorizedTo
with the permissionANNOUNCE
and the block number from theDSNPBatchPublication
event.
Announcement Duplicates
Duplicate Announcements MUST be rejected or ignored.
Activity Content Specification
Version 1.1.0
Content references shared via the DSNP consist of URLs pointing to documents containing Activity Streams JSON objects. For the purposes of the DSNP, restrictions are placed on the Activity Streams 2.0 specification.
JSON-LD and Activity Streams
All DSNP Activity Content is compatible with the Activity Streams 2.0 specification.
While there are some DSNP extensions, they are guaranteed to use non-colliding terms.
Therefore, the JSON-LD @context
field is set to https://www.w3.org/ns/activitystreams
according to Activity Streams 2.0 §2.1.
Core Activity Content Types
DSNP uses only the following content types at the root level:
Name | Description | DSNP Announcements |
---|---|---|
Note | standard user content | Broadcast, Reply |
Profile | user profile content | Profile |
Associated Types
Name | Description | Specification |
---|---|---|
Location | add a location to content | Activity Vocabulary |
Tag | add a tag to content | Activity Vocabulary |
Attachments | supported attachment types | Activity Vocabulary |
Hash | content validation hash | DSNP Extension |
Supported URL Schema
URLs in DSNP-compatible Activity Content MUST use one of the following URL schemes:
Scheme | Description | Reference | DSNP Version Added |
---|---|---|---|
HTTPS | Hypertext Transfer Protocol Secure | RFC2818 | 1.0 |
HTTP | Hypertext Transfer Protocol | RFC2616 | 1.0 |
Libraries
Name | Language(s) |
---|---|
LibertyDSNP/activity-content | JavaScript/TypeScript |
LibertyDSNP/activity-content-java | Java/Kotlin |
LibertyDSNP/activity-content-swift | Swift |
Releases
Version | Description | Release Date | Changelog |
---|---|---|---|
1.1.0 | DIP-158 | 2022-05-05 | Changelog |
1.0.0 | Initial Release | 2021-09-09 | Changelog |
Non-Normative
Additional Fields
Implementers may choose to support more of the Activity Streams standard as long as it does not conflict with this specification, but should note that other implementations may not recognize those additions. Implementers who extend their support for Activity Streams objects beyond the subset defined here do so at their own risk.
Activity Stream Type: Note
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
@context | Activity Streams 2.0 | YES | JSON-LD @context | MUST be set to https://www.w3.org/ns/activitystreams |
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Note |
content | Activity Vocabulary 2.0 | YES | Text content of the note | |
mediaType | Activity Vocabulary 2.0 | YES | MIME type for the content field | MUST be set to a supported MIME type |
published | Activity Vocabulary 2.0 | YES | The time of publishing | MUST be ISO8601 |
name | Activity Vocabulary 2.0 | no | The display name for the note | |
attachment | Activity Vocabulary 2.0 | no | Array of attached links or media | MUST be one of the Supported Attachments |
tag | Activity Vocabulary 2.0 | no | Array of tags/mentions | MUST follow Tag Type |
location | Activity Vocabulary 2.0 | no | For location | MUST follow Location Type |
Supported Content MIME Types
Format | MIME Type | Specification(s) |
---|---|---|
Plain | text/plain |
Examples
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "Hello world!",
"mediaType": "text/plain",
"published": "1970-01-01T00:00:00+00:00"
}
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
"mediaType": "text/plain",
"summary": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
"attachment": [
{
"type": "Link",
"href": "https://en.wikipedia.org/wiki/Citation_needed"
}
],
"published": "1970-01-01T00:00:00+00:00"
}
Activity Stream Type: Profile
Profiles are used to provide additional user information display.
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
@context | Activity Streams 2.0 | YES | JSON-LD @context | MUST be set to https://www.w3.org/ns/activitystreams |
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Profile |
name | Activity Vocabulary 2.0 | no | The display name for the profile | |
icon | Activity Vocabulary 2.0 | no | An array of avatars of the profile | MUST follow Image Link Type |
summary | Activity Vocabulary 2.0 | no | Used as a plain text biography of the profile | |
published | Activity Vocabulary 2.0 | no | The time of publishing | MUST be ISO8601 |
location | Activity Vocabulary 2.0 | no | For location | MUST follow Location Type |
tag | Activity Vocabulary 2.0 | no | For tags or mentions | MUST follow Tag Type |
Examples
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Profile",
"name": "John Doe",
"summary": "John Doe is actually a small kitten. See pfp.",
"icon": [
{
"type": "Link",
"href": "https://placekitten.com/256/256",
"mediaType": "image/jpeg",
"width": "256",
"height": "256"
},
{
"type": "Link",
"href": "https://placekitten.com/64/64",
"mediaType": "image/jpeg",
"width": "64",
"height": "64"
}
],
"published": "1970-01-01T00:00:00+00:00"
}
Associated Type: Hash
NOT part of the Activity Streams 2.0 Vocabulary.
Activity objects linking to external content such as audio, image or video files must include a "hash"
field for users to validate linked content.
The value of this "hash"
field must be an array of objects representing multiple hashes.
AT LEAST ONE hash in the array MUST be one of the supported algorithms, although others may also be used.
Property | Required | Description | Restrictions |
---|---|---|---|
algorithm | YES | The algorithm of the given hash | |
value | YES | Hash value serialization |
Supported Algorithms
Algorithm | Description | Value Serialization | Specification(s) |
---|---|---|---|
keccak256 | keccak-256 hash | hexadecimal | The Keccak SHA-3 submission v3 |
Example
{
"hash": [
{
"algorithm": "keccak256",
"value": "0x1234567890ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
},
{
"algorithm": "Ripemd256",
"value": "0x96a9209006748794193d1811ef2dd5f447782b8b1635841165bc031bb3db64da"
}
]
}
Associated Type: Location
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Place |
name | Activity Vocabulary 2.0 | YES | The display name for the location | |
accuracy | Activity Vocabulary 2.0 | no | The accuracy of the coordinates as a percentage. (e.g. "94.0" means "94.0% accurate") | |
altitude | Activity Vocabulary 2.0 | no | The altitude of the location | |
latitude | Activity Vocabulary 2.0 | no | The latitude of the location | |
longitude | Activity Vocabulary 2.0 | no | The longitude of the location | |
radius | Activity Vocabulary 2.0 | no | The area around the given point that comprises the location | |
units | Activity Vocabulary 2.0 | no | The units for radius and altitude (defaults to meters) | MUST be one of these: cm , feet , inches , km , m , miles |
Warning
Location data may pose a privacy danger to users. Users should be warned before publishing location data.
Example
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "I'm in NYC!",
"mediaType": "text/plain",
"location": {
"type": "Place",
"name": "New York City, NY",
"latitude": "40.73",
"longitude": "-73.93",
"accuracy": "94.0"
},
"published": "1970-01-01T00:00:00+00:00"
}
Associated Type: Tag
Hashtag
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
name | Activity Vocabulary 2.0 | YES | The text of the tag |
Example
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "I love the #dsnp spec!",
"mediaType": "text/plain",
"tag": [
{
"name": "#dsnp"
}
],
"published": "1970-01-01T00:00:00+00:00"
}
Mention
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
name | Activity Vocabulary 2.0 | no | The text of the tag | |
type | Activity Vocabulary 2.0 | YES | Identifies the tag as type Mention | MUST be Mention |
id | Activity Vocabulary 2.0 | YES | Link to the user mentioned | MUST be a DSNP User URI |
Example
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "@sally should check out the #dsnp spec!",
"mediaType": "text/plain",
"tag": [
{
"name": "#dsnp"
},
{
"name": "@sally",
"type": "Mention",
"id": "dsnp://0x12345678"
}
],
"published": "1970-01-01T00:00:00+00:00"
}
Associated Attachments
Audio
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Audio |
url | Activity Vocabulary 2.0 | YES | An array of links for given audio content in different formats | MUST be an Audio Link AND MUST have at least one supported audio MIME type |
name | Activity Vocabulary 2.0 | no | The display name for the audio file | |
duration | Activity Vocabulary 2.0 | no | Approximate duration of the audio |
Audio Link
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Link |
href | Activity Vocabulary 2.0 | YES | The URL for the given audio content | MUST be a Supported URL Schema |
mediaType | Activity Vocabulary 2.0 | YES | MIME type of href content | MUST follow |
hash | DSNP 1.0 | YES | Array of hashes for linked content validation | MUST include at least one supported hash |
Supported Audio MIME Types
Format | MIME Type | Specification(s) |
---|---|---|
MP3 | audio/mpeg | RFC3003 |
OGG | audio/ogg | RFC5334 |
WebM | audio/webm | WebM standard |
Example
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"attachments": [
{
"type": "Audio",
"name": "The Scream",
"url": [
{
"type": "Link",
"href": "https://upload.wikimedia.org/wikipedia/commons/d/d9/Wilhelm_Scream.ogg",
"mediaType": "audio/ogg",
"hash": [
{
"algorithm": "keccak256",
"value": "0x3b33df3d163e86514e9041ac97e3d920a75bbafa8d9c1489e631897874b762cc"
}
]
}
],
"duration": "1S"
}
],
"published": "1970-01-01T00:00:00+00:00"
}
Image
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Image |
url | Activity Vocabulary 2.0 | YES | An array of links for given image content in different formats | MUST be an Image Link AND MUST have at least one supported image MIME type |
name | Activity Vocabulary 2.0 | no | The display name or alt text for the image |
Image Link
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Link |
href | Activity Vocabulary 2.0 | YES | The URL for the given image | MUST be a Supported URL Schema |
mediaType | Activity Vocabulary 2.0 | YES | MIME type of href content | |
hash | DSNP 1.0 | YES | Array of hashes for linked content validation | MUST include at least one supported hash |
height | Activity Vocabulary 2.0 | no | A hint as to the rendering height in device-independent pixels | |
width | Activity Vocabulary 2.0 | no | A hint as to the rendering width in device-independent pixels |
Supported Image MIME Types
Format | MIME Type | Specification(s) |
---|---|---|
JPEG | image/jpeg | RFC2045 |
PNG | image/png | W3C PNG Standard |
SVG | image/svg+xml | W3C SVG standard |
WebP | image/webp | WebP standard |
GIF | image/gif | RFC2045 |
Example
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"attachments": [
{
"type": "Image",
"name": "One of the founders of DSNP",
"url": [
{
"type": "Link",
"href": "https://upload.wikimedia.org/wikipedia/commons/a/ae/Mccourt.jpg",
"width": 350,
"height": 228,
"mediaType": "image/jpg",
"hash": [
{
"algorithm": "keccak256",
"value": "0x90b3b09658ec527d679c2de983b5720f6e12670724f7e227e5c360a3510b4cb5"
}
]
}
]
}
],
"published": "1970-01-01T00:00:00+00:00"
}
Video
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Video |
url | Activity Vocabulary 2.0 | YES | An array of links for given video content in different formats | MUST be a Video Link AND MUST have at least one supported video MIME type |
name | Activity Vocabulary 2.0 | no | The display name for the video | |
duration | Activity Vocabulary 2.0 | no | Approximate duration of the video |
Video Link
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Link |
href | Activity Vocabulary 2.0 | YES | The URL for the given content | MUST be a Supported URL Schema |
mediaType | Activity Vocabulary 2.0 | YES | MIME type of href content | |
hash | DSNP 1.0 | YES | Array of hashes for linked content validation | MUST include at least one supported hash |
height | Activity Vocabulary 2.0 | no | A hint as to the rendering height in device-independent pixels | |
width | Activity Vocabulary 2.0 | no | A hint as to the rendering width in device-independent pixels |
Supported Video MIME Types
Format | MIME Type | Specification(s) |
---|---|---|
MPEG | video/mpeg | RFC2045 |
OGG | video/ogg | RFC5334 |
WebM | video/webm | WebM standard |
H265 | video/H265 | RFC7798 |
MP4 | video/mp4 | RFC4337 |
Example
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"attachments": [
{
"type": "Video",
"name": "One of the founders of DSNP",
"duration": "PT10M32S",
"url": [
{
"type": "Link",
"href": "https://upload.wikimedia.org/wikipedia/commons/c/c0/Big_Buck_Bunny_4K.webm",
"width": 4000,
"height": 2250,
"mediaType": "video/webm",
"hash": [
{
"algorithm": "keccak256",
"value": "0xf841950dfcedc968dbd63132da844b9f28faea3dbfd4cf326b3831b419a20e9a"
}
]
}
]
}
],
"published": "1970-01-01T00:00:00+00:00"
}
Link
Property | Base Spec | Required | Description | Restrictions |
---|---|---|---|---|
type | Activity Vocabulary 2.0 | YES | Identifies the type of the object | MUST be set to Link |
href | Activity Vocabulary 2.0 | YES | The URL for the given link | MUST be a Supported URL Schema |
name | Activity Vocabulary 2.0 | no | The display name for the link |
Examples
{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Note",
"content": "Check out the DSNP Website!",
"mediaType": "text/plain",
"attachment": [
{
"type": "Link",
"name": "DSNP Website",
"href": "https://dsnp.org"
}
],
"published": "1970-01-01T00:00:00+00:00"
}
Glossary
- Activity Content - The linked content connected to an "Announcement"
- Announcement - An item of data, typically an image or note, posted to the blockchain via a "Batch Publication"
- Bad actor - A user intentionally using the application for bad-faith or illegal purposes
- Batch Publication - A collection of Announcements
- Bot - An automated account, sometimes malicious but often providing some service ("Hey Alexa, what's the weather?")
- Consumer - Someone who reads content from social media
- Contract address - The unique number associated with a smart contract posted on the blockchain
- Hash Collision - The vanishingly unlikely possibility of a hash providing the same output for two different inputs
- Hash function - A cryptographic function whose output is effectively unique for any given input without any information from the input being accessible in the output
- Hash - A string of characters generated from a hash function, see "Hash function"
- Identity Contract - The smart contract used to store a user's delegated public addresses and permissions, see "Contract address"
- Publisher - The contract that posts new content from a publisher to the blockchain through batch publications
- Store - A means of keeping messages or data for an extended period of time
- User - A human using our application. See article for an exception
DRAFT: Archivists Overview
The job of an Archivist is to permanently store Batch content and DSNP Announcements in a format that is easily validated and retrieved.
The Archivist must be able to access chain data and all DSNP Content.
Validations that Archivists could perform
- Signature validation - proof that author provided a real signature
- Signature authentication - proof that the From address is the signer, or that signer is a valid delegate (proof of authorship)
- Content authentication - retrievable content is at the URL given in the DSNP message
- Content Hash authentication - content hash is valid and the URL serves the claimed data
All signatures for the announcement are included in the batch regardless of how the signature was/was not requested.
Archive Storage Format
Specify the off-chain Archivist storage format.
Assumptions
- Chain messages are on Ethereum.
- Message data is posted via Ethereum log events.
- Signature algorithm is secp256k1. This allows the use
ecreover
to get public keys. A public key also need not be included in a log event for ease of validation. - Content hashes are created via the same keccak-256 hashing algorithm used by Ethereum.
Archive Entry
An archive entry is a combination of data in a DSNP message and from the block in which it is included. It is a key-value map consisting of the following fields:
field name | description | type |
---|---|---|
dsnpType | DSNP message type | number/enum. see DSNP Message Types |
dsnpData | DSNP message data | see fields in DSNP Messages |
signatures | list of signatures for this message | array of Signatures |
dsnpType
- number
- Indicates what type of message this is, useful for indexers and filters.
dsnpData
- varies
- This can be encrypted where appropriate. The decrypted, fully deserialized version must be one of the types described in DSNP Messages.
signatures
- array
- all signatures applied to this message at the time of archival
Batch
A Batch is data that is referenced by a Batch Announcement.
It consists of one or more ArchiveEntries
.
field | description | type |
---|---|---|
archives | a set of ArchiveEntries | map[ArchiveEntry] |
batchId | keccak-256 hash of content stored at URL | bytes32 |
blockHeight | the block number this message was included in | number |
fromAddress | social identity of batch announcer, i.e. message sender | bytes |
logIndex | the index within the logs of this message | number |
signature | announcer's signature | Signature |
transactionIndex | the index of the transaction this message is associated with | number |
url | the location of this archive | string |
archives
The set of ArchiveEntries
is a key-value map, with the key
being the archiveEntryId
:
- bytes32
- the keccak-256 hash of all of the Archive Entry fields in a keccak-256 hash with the
archiveEntryId
field being blank
batchId
- bytes32
- the keccak-256 hash of content stored at the URL referenced in this batch
blockHeight
- number
- the block in which this DSNP Message is included
fromAddress
the social identity of the batch announcer, i.e. the message sender.
logIndex
- number
- The log index in which this DSNP Message is included
signature
- Signature (see below)
- the signature of this batch announcer
transactionIndex
- number
- the transaction index in which this DSNP Message is included
url
- string
- the permanent URL address where this archive is stored
Signature
A Signature consists of two fields:
signature
- A secp256k1 signatureresult
- Optional, bytes - a result of an operation performed. For example, if a signing entity wished to prove that they had performed some sort of validation or analysis on the message, they would put the result of the analysis in this field. It could be a meaningful number or string, some sort of proof hash, etc.