sol_did/legacy/
legacy_did_account.rs

1use crate::constants::VM_DEFAULT_FRAGMENT_NAME;
2use crate::state::{DidAccount, VerificationMethodFlags, VerificationMethodType};
3use crate::{Service, VerificationMethod};
4use anchor_lang::prelude::*;
5use anchor_lang::{AccountDeserialize, AccountSerialize, Owner};
6use borsh::BorshDeserialize;
7use num_traits::*;
8use std::str::FromStr;
9
10#[derive(Clone, BorshDeserialize)]
11pub struct LegacyServiceEndpoint {
12    /// Id related to the endpoint
13    /// When the DID document is resolved, this is concatenated to the DID to produce
14    /// did:sol:<identifier>#<id>
15    pub id: String,
16    /// Endpoint type
17    pub endpoint_type: String,
18    /// The actual URL of the endpoint
19    pub endpoint: String,
20    /// More info about the endpoint
21    pub description: String,
22}
23
24impl LegacyServiceEndpoint {
25    pub fn post_migration_size(&self) -> usize {
26        4 + self.id.len() + 4 + self.endpoint_type.len() + 4 + self.endpoint.len()
27    }
28}
29
30#[derive(Clone, BorshDeserialize)]
31pub struct LegacyVerificationMethod {
32    /// Unique id for the verification method, and how to find it
33    /// When the DID document is resolved, this is concatenated to the DID to produce
34    /// did:sol:<identifier>#<id>
35    pub id: String,
36    /// What kind of key this is. TODO use an enum?
37    pub verification_type: String,
38    /// The associated pubkey itself
39    pub pubkey: Pubkey,
40}
41
42impl LegacyVerificationMethod {
43    pub fn post_migration_size(&self) -> usize {
44        4 + self.id.len() // fragment string
45            + 2 // flags
46            + 1 // method
47            + 4 + 32 // ed25519 pubkey
48    }
49}
50
51#[derive(Clone, BorshDeserialize)]
52pub struct LegacyDidAccount {
53    /// The version of the account for (de)serialization
54    pub account_version: u8,
55    /// The public key of the solData account - used to derive the identifier
56    /// and first verification method
57    pub authority: Pubkey,
58
59    /// DecentralizedIdentifier version - used to generate the DID JSON-LD context:
60    /// ["https://w3id.org/did/v1.0", "https://w3id.org/sol/v" +  version]
61    pub version: String,
62    /// List of controllers of this did, using the key of the did
63    pub controller: Vec<Pubkey>,
64
65    /// All of the public keys related to the DecentralizedIdentifier
66    pub verification_method: Vec<LegacyVerificationMethod>,
67    pub authentication: Vec<String>,
68    /// Currently the most important part, decides which ID gets to do things
69    pub capability_invocation: Vec<String>,
70    pub capability_delegation: Vec<String>,
71    pub key_agreement: Vec<String>,
72    pub assertion_method: Vec<String>,
73    /// Services that can be used with this DID
74    pub service: Vec<LegacyServiceEndpoint>,
75}
76
77impl LegacyDidAccount {
78    pub fn post_migration_size(&self) -> usize {
79        let mut size = DidAccount::initial_size();
80        size += self
81            .verification_method
82            .iter()
83            .fold(0, |accum, item| accum + item.post_migration_size()); // verification_methods
84        size += self
85            .service
86            .iter()
87            .fold(0, |accum, item| accum + item.post_migration_size()); // services
88        size += self.controller.len() * 32; // native_controllers
89        size += 8; // anchor discriminator
90        size
91    }
92
93    pub fn migrate(&self, into: &mut DidAccount, bump: u8) -> Result<()> {
94        // "default" authority always has proven ownership of the DID
95        let default_flags = self.get_flags(&VM_DEFAULT_FRAGMENT_NAME.to_string())
96            | VerificationMethodFlags::OWNERSHIP_PROOF;
97        into.init(bump, &self.authority, default_flags);
98        let migrated = self.migrate_verification_methods();
99        into.set_verification_methods(Vec::new(), migrated)?;
100        let migrated = self.migrate_services();
101        into.set_services(migrated, false)?;
102        into.set_native_controllers(self.controller.clone())?;
103
104        // TODO: Documentation
105        // pub version: String,
106        // pub account_version: u8, unhandled
107
108        Ok(())
109    }
110
111    fn migrate_verification_methods(&self) -> Vec<VerificationMethod> {
112        // Note, this migration with fail on duplicates ids OR another "default"
113        // in self.verification_method
114        self.verification_method
115            .iter()
116            .map(|vm| VerificationMethod {
117                fragment: vm.id.clone(),
118                method_type: VerificationMethodType::Ed25519VerificationKey2018
119                    .to_u8()
120                    .unwrap(),
121                flags: self.get_flags(&vm.id).bits(),
122                key_data: vm.pubkey.to_bytes().to_vec(),
123            })
124            .collect()
125    }
126
127    fn get_flags(&self, vm_fragment: &String) -> VerificationMethodFlags {
128        let mut flags = VerificationMethodFlags::NONE;
129
130        if self.authentication.contains(vm_fragment) {
131            flags |= VerificationMethodFlags::AUTHENTICATION;
132        }
133        if self.assertion_method.contains(vm_fragment) {
134            flags |= VerificationMethodFlags::ASSERTION;
135        }
136        if self.capability_invocation.contains(vm_fragment) {
137            flags |= VerificationMethodFlags::CAPABILITY_INVOCATION;
138        }
139        if self.capability_delegation.contains(vm_fragment) {
140            flags |= VerificationMethodFlags::CAPABILITY_DELEGATION;
141        }
142        if self.key_agreement.contains(vm_fragment) {
143            flags |= VerificationMethodFlags::KEY_AGREEMENT;
144        }
145
146        // special case for inferred "capability_invocation" verification method
147        if self.capability_invocation.is_empty() && vm_fragment == VM_DEFAULT_FRAGMENT_NAME {
148            flags |= VerificationMethodFlags::CAPABILITY_INVOCATION;
149        }
150
151        flags
152    }
153
154    fn migrate_services(&self) -> Vec<Service> {
155        self.service
156            .iter()
157            .map(|se| {
158                Service {
159                    fragment: se.id.clone(),
160                    service_type: se.endpoint_type.clone(),
161                    service_endpoint: se.endpoint.clone(),
162                    // TODO: Documentation NOTE, Descriptions will be dropped
163                    // service_description: se.description.clone(),
164                }
165            })
166            .collect()
167    }
168}
169
170impl AccountDeserialize for LegacyDidAccount {
171    fn try_deserialize_unchecked(buf: &mut &[u8]) -> Result<Self> {
172        // Deserialize whole account (without discriminator)
173        AnchorDeserialize::deserialize(buf).map_err(|_| ErrorCode::AccountDidNotDeserialize.into())
174    }
175}
176
177impl AccountSerialize for LegacyDidAccount {}
178
179impl Owner for LegacyDidAccount {
180    fn owner() -> Pubkey {
181        // TODO: Make static
182        Pubkey::from_str("idDa4XeCjVwKcprVAo812coUQbovSZ4kDGJf2sPaBnM").unwrap()
183    }
184}